/// <summary> /// 跨式期权开仓 /// </summary> /// <param name="dataToday"></param> /// <param name="signal"></param> /// <param name="positions"></param> /// <param name="myAccount"></param> /// <param name="pairs"></param> /// <param name="today"></param> /// <param name="index"></param> /// <param name="duration"></param> private void openStrangle(ref Dictionary <string, List <KLine> > dataToday, ref Dictionary <string, MinuteSignal> signal, ref SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions, ref BasicAccount myAccount, ref SortedDictionary <DateTime, List <StranglePair> > pairs, DateTime today, int index, double duration) { DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), index); double etfPriceNow = dataToday[targetVariety][index].open; var optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today); //选取指定的看涨期权 var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); OptionInfo call = list[0]; //根据给定的看涨期权选取对应的看跌期权 OptionInfo put = OptionUtilities.getCallByPutOrPutByCall(optionInfoList, call); if (call.strike != 0 && put.strike != 0 && (call.modifiedDate > today.AddDays(10) || call.modifiedDate < today)) //跨式期权组合存在进行开仓 { tradeAssistant(ref dataToday, ref signal, call.optionCode, call.contractMultiplier, today, now, index); tradeAssistant(ref dataToday, ref signal, put.optionCode, put.contractMultiplier, today, now, index); StranglePair openPair = new StranglePair() { callCode = call.optionCode, putCode = put.optionCode, callPosition = call.contractMultiplier, putPosition = put.contractMultiplier, endDate = call.endDate, etfPrice = etfPriceNow, callStrike = call.strike, putStrike = put.strike, modifiedDate = now, strangleOpenPrice = dataToday[call.optionCode][index].open + dataToday[put.optionCode][index].open, closeDate = new DateTime(), closePrice = 0 }; List <StranglePair> pairList = new List <StranglePair>(); pairList.Add(openPair); pairs.Add(now, pairList); MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } }
/// <summary> /// 构造函数 /// </summary> /// <param name="todayInt"></param> /// <param name="riskFreeRate"></param> public VolSurface(int todayInt = 0, double riskFreeRate = 0.027) { if (todayInt == 0) { today = DateTime.Now; } else { today = Kit.ToDate(todayInt); } optionList = getExistingOption(today); //获取今日期权的到期日期 var dateStructure = OptionUtilities.getDurationStructure(optionList, today); //存储今日期权的基本信息 optionInfo = optionList.ToDictionary(x => x.optionCode, y => y); rate = riskFreeRate; duartion = dateStructure[0]; getOptionData(dateStructure[0], today); while (DateTime.Now.TimeOfDay <= new TimeSpan(15, 00, 0)) { DateTime now1 = DateTime.Now; computeVol(); DateTime now2 = DateTime.Now; Console.WriteLine(now2 - now1); Thread.Sleep(5000); } }
/// <summary> /// 获取对应的期权合约 /// </summary> /// <param name="duration"></param> /// <param name="strike"></param> /// <param name="etfPriceNow"></param> /// <param name="type"></param> /// <param name="today"></param> /// <returns></returns> private OptionInfo getOptionCode(double duration, double etfPriceNow, string type, DateTime today) { OptionInfo option = new OptionInfo(); var optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today); if (type == "认购") { //选取指定的看涨期权 var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); if (list.Count > 0) { option = list[0]; } } else if (type == "认沽") { //选取指定的看跌期权 var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽"), etfPriceNow - 0.5, etfPriceNow), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); if (list.Count > 0) { option = list[0]; } } return(option); }
protected void Page_Load(object sender, EventArgs e) { this.ShowTransactions(); OptionUtilities optionUtilities = new OptionUtilities(); pspName.Items.AddRange(optionUtilities.GetPSPOptions()); }
/// <summary> /// 估算未来的期权价格 /// </summary> /// <param name="days"></param> /// <param name="code"></param> /// <param name="etfPrice"></param> /// <returns></returns> private double computeFuturePrice(int days, string code, double etfPrice) { double price = 0; var option = OptionUtilities.getOptionByCode(optionList, code); int x = Convert.ToInt32(optionPrice[code].duration - days); int y = Convert.ToInt32(Math.Round(1000 * Math.Log(option.strike / etfPrice), 0) + 500); double vol = volSurface[x, y]; price = ImpliedVolatilityUtilities.ComputeOptionPrice(option.strike, (optionPrice[code].duration - days) / 252.0, riskFreeRate, 0, option.optionType, vol, etfPrice); return(price); }
public void getOptionData(double duration, DateTime today) { var optionListNow = OptionUtilities.getOptionListByDuration(getExistingOption(today), today, duration); var list = from OptionInfo in optionListNow orderby OptionInfo.optionType ascending, OptionInfo.strike ascending select OptionInfo; List <string> code = new List <string>(); dataListKey = code; foreach (var item in list) { code.Add(item.optionCode); } code.Add(ETF); Execute(code); }
/// <summary> /// 以收益损失比给期权组合打分 /// </summary> /// <param name="code1"></param> /// <param name="code2"></param> /// <param name="days"></param> /// <param name="upperPrice"></param> /// <param name="lowerPrice"></param> /// <param name="up"></param> /// <param name="low"></param> /// <returns></returns> private double computeMarkOfPairs(string code1, string code2, int days, double upperPrice, double lowerPrice, ref double up, ref double low) { double marks = -100; double initialValue = optionPrice[code1].lastPrice - optionPrice[code2].lastPrice; double upperValue = computeFuturePrice(days, code1, upperPrice) - computeFuturePrice(days, code2, upperPrice); double lowerValue = computeFuturePrice(days, code1, lowerPrice) - computeFuturePrice(days, code2, lowerPrice); var option2 = OptionUtilities.getOptionByCode(optionList, code2); double margin = OptionMargin.ComputeOpenMargin(etfPrice, optionPrice[code2].lastPrice, option2.strike, option2.optionType, 1, etfPrice) + (upperPrice - lowerPrice) / 2; double capitalOccupied = margin - initialValue; up = (upperValue - initialValue) / capitalOccupied; low = (lowerValue - initialValue) / capitalOccupied; marks = up / low; return(marks); }
public void recorddata(DateTime startDate, DateTime endDate) { var tradedays = dateRepo.GetStockTransactionDate(startDate, endDate); foreach (var date in tradedays) { stockRepo.GetStockTransaction("510050.SH", date, date.AddHours(17)); var list = infoRepo.GetStockOptionInfo(underlying, date, date); list = OptionUtilities.modifyOptionListByETFBonus(list, date); foreach (var item in list) { optionRepo.GetStockTransaction(item.code, date, date.AddHours(17)); } } }
private void displayPairs() { foreach (var item in pairs) { var option1 = OptionUtilities.getOptionByCode(optionList, item.option1); var option2 = OptionUtilities.getOptionByCode(optionList, item.option2); var price1 = optionPrice[option1.optionCode]; var price2 = optionPrice[option2.optionCode]; double delta = price1.delta - price2.delta; double gamma = price1.gamma - price2.gamma; double vega = price1.vega - price2.vega; double theta = price1.theta - price2.theta; Console.WriteLine("代码:{0}, 名称:{1}, 价格:{2} ask:{3} bid:{4}, 波动率:{5}", option1.optionCode, option1.optionName, optionPrice[option1.optionCode].lastPrice, optionPrice[option1.optionCode].ask, optionPrice[option1.optionCode].bid, price1.impv); Console.WriteLine("代码:{0}, 名称:{1}, 价格:{2} ask:{3} bid:{4}, 波动率:{5}", option2.optionCode, option2.optionName, optionPrice[option2.optionCode].lastPrice, optionPrice[option2.optionCode].ask, optionPrice[option2.optionCode].bid, price2.impv); Console.WriteLine("mark:{0}, delta:{1}, gamma:{2}, vega:{3}, theta:{4}, profit:{5}, loss:{6}", item.mark, delta, gamma, vega, theta, item.profit, item.loss); Console.WriteLine("=============================================="); } }
private StraddlePairCode getStraddlePairCode(List <OptionInfo> optionInfoList, double durationFront, double durationNext, double etfPrice, DateTime today) { StraddlePairCode pair = new StraddlePairCode(); var call = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, durationFront), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList(); pair.callCodeFront = call[0].optionCode; var callATM = call[0]; var put = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, durationFront), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList(); pair.putCodeFront = put[0].optionCode; var callNext = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, durationNext), "认购").OrderBy(x => Math.Abs(x.strike - callATM.strike)).Where(x => x.startDate <= today).ToList(); pair.callCodeNext = callNext[0].optionCode; var putNext = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, durationNext), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList(); pair.putCodeNext = putNext[0].optionCode; return(pair); }
public void record(DateTime startDate, DateTime endDate) { var tradedays = dateRepo.GetStockTransactionDate(startDate, endDate); CreateDBOrTableIfNecessary(startDate); CreateDBOrTableIfNecessary(startDate.AddYears(1)); var start = startDate; while (start < endDate) { if (!ExistInSqlServer(start)) { CreateDBOrTableIfNecessary(start); } start = start.AddYears(1); } if (!ExistInSqlServer(endDate)) { CreateDBOrTableIfNecessary(endDate); } foreach (var date in tradedays) { var list = infoRepo.GetStockOptionInfo(underlying, date, date); list = OptionUtilities.modifyOptionListByETFBonus(list, date); parityList = new List <StockOptionParity>(); foreach (var item in list) { if (item.type == "认购" && item.expireDate <= date.AddDays(180) && item.listedDate <= date) { var parity = StockOptionExtension.GetParity(list, item); Console.WriteLine("Date {0}: strike {1} expireDate {2} option1 {3} option2 {4}", date, item.strike, item.expireDate, item.name, parity.name); var pair = new StockOptionParity { call = item.code, put = parity.code, strike = item.strike, expireDate = item.expireDate, unit = item.unit }; parityList.Add(pair); } } compute(date); } }
protected void Page_Load(object sender, EventArgs e) { RoleUtilities roleUtilities = new RoleUtilities(); OptionUtilities optionUtilities = new OptionUtilities(); if (Request.HttpMethod == "POST") { string roleName = rolename.Value; string groupOption = groupoptions.Value; string roleType = Request.Form["type"]; string roleLink = rolelink.Value; if (!string.IsNullOrEmpty(roleName) || !string.IsNullOrEmpty(groupOption) || !string.IsNullOrEmpty(roleType)) { string result = roleUtilities.InsertRole(roleName, groupOption, roleType, roleLink); if (result == "Success") { ClearFields(); toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("success", $"{roleName} Successfully Added.")); } else { toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("error", $"An Error Occured Please Contact System Administrator.")); } } else { toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("error", $"Please fill up all Fields.")); } } ListItem[] gOptions = optionUtilities.GetGroupOptions(); groupoptions.Items.Clear(); groupoptions.Items.AddRange(gOptions); sgroupoptions.Items.Clear(); sgroupoptions.Items.AddRange(gOptions); editgroupoptions.Items.Clear(); editgroupoptions.Items.AddRange(gOptions); ShowRoles(roleUtilities.GetRoles()); }
public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startdate), Kit.ToInt_yyyyMMdd(endDate)); var repo = Platforms.container.Resolve <OptionInfoRepository>(); var OptionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1); Caches.put("OptionInfo", OptionInfoList); List <DateTime> tradeDays = DateUtils.GetTradeDays(startdate, endDate); //var ETFDaily = Platforms.container.Resolve<StockDailyRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", Kit.ToDate(20150101),Kit.ToDate(20160731)); foreach (var day in tradeDays) { Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >(); var list = OptionUtilities.getOptionListByDate(OptionInfoList, Kit.ToInt_yyyyMMdd(day)); List <DateTime> durationArr = OptionUtilities.getEndDateListByAscending(list); var ETFtoday = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", day); data.Add("510050.SH", ETFtoday.Cast <KLine>().ToList()); foreach (var info in list) { string IHCode = OptionUtilities.getCorrespondingIHCode(info, Kit.ToInt_yyyyMMdd(day)); var repoOption = Platforms.container.Resolve <OptionMinuteRepository>(); var optionToday = repoOption.fetchFromLocalCsvOrWindAndSave(info.optionCode, day); data.Add(info.optionCode, optionToday.Cast <KLine>().ToList()); } int index = 0; SortedDictionary <DateTime, Dictionary <string, MinutePositions> > positions = new SortedDictionary <DateTime, Dictionary <string, MinutePositions> >(); while (index < 240) { int nextIndex = index + 1; DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(day), index); Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); double etfPrice = ETFtoday[index].close; List <double> strikeTodayArr = OptionUtilities.getStrikeListByAscending(list).OrderBy(x => Math.Abs(x - etfPrice)).ToList(); try { OptionInfo callCandidateFront = OptionUtilities.getSpecifiedOption(list, durationArr[0], "认购", strikeTodayArr[0])[0]; OptionInfo putCandidateFront = OptionUtilities.getSpecifiedOption(list, durationArr[0], "认沽", strikeTodayArr[0])[0]; OptionInfo callCandidateNext = OptionUtilities.getSpecifiedOption(list, durationArr[1], "认购", strikeTodayArr[0])[0]; OptionInfo putCandidateNext = OptionUtilities.getSpecifiedOption(list, durationArr[1], "认沽", strikeTodayArr[0])[0]; MinuteSignal callFront = new MinuteSignal() { code = callCandidateFront.optionCode, volume = -1, time = now, tradingVarieties = "option", price = data[callCandidateFront.optionCode][index].close, minuteIndex = index }; MinuteSignal putFront = new MinuteSignal() { code = putCandidateFront.optionCode, volume = -1, time = now, tradingVarieties = "option", price = data[putCandidateFront.optionCode][index].close, minuteIndex = index }; MinuteSignal callNext = new MinuteSignal() { code = callCandidateNext.optionCode, volume = 1, time = now, tradingVarieties = "option", price = data[callCandidateNext.optionCode][index].close, minuteIndex = index }; MinuteSignal putNext = new MinuteSignal() { code = putCandidateNext.optionCode, volume = 1, time = now, tradingVarieties = "option", price = data[putCandidateNext.optionCode][index].close, minuteIndex = index }; signal.Add(callFront.code, callFront); signal.Add(putFront.code, putFront); signal.Add(callNext.code, callNext); signal.Add(putNext.code, putNext); DateTime next = MinuteTransactionWithSlip.computeMinutePositions(signal, data, ref positions, slipPoint: 0.01, now: now); nextIndex = Math.Max(nextIndex, TimeListUtility.MinuteToIndex(next)); } catch (Exception) { throw; } index = nextIndex; } } }
private void computeImpv() { optionVol = new double[etfDailyData.Count()]; for (int i = startIndex; i < etfDailyData.Count(); i++) { DateTime today = etfDailyData[i].time; double etfPrice = etfDailyData[i].close; double volThisMonth; double volNextMonth; double duration; //获取当日期限结构,选取当月合约 List <double> dateStructure = OptionUtilities.getDurationStructure(optionInfoList, today); double duration0 = dateStructure[0] == 0 ? dateStructure[1] : dateStructure[0]; duration = duration0; var call = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList(); var callATM = call[0]; var callPrice = Platforms.container.Resolve <OptionDailyRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today, today); double callImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(callATM.strike, duration / 252.0, 0.04, 0, callATM.optionType, callPrice[0].close, etfPrice); var put = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList(); var putATM = put[0]; var putPrice = Platforms.container.Resolve <OptionDailyRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today, today); double putImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(putATM.strike, duration / 252.0, 0.04, 0, putATM.optionType, putPrice[0].close, etfPrice); if (callImpv * putImpv == 0) { volThisMonth = callImpv + putImpv; } else { volThisMonth = (callImpv + putImpv) / 2; } //获取当日期限结构,选取下月合约,若下月合约不存在,就获取季月合约 double duration1 = dateStructure[0] == 0 ? dateStructure[2] : dateStructure[1]; duration = duration1; call = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList(); callATM = call[0]; callPrice = Platforms.container.Resolve <OptionDailyRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today, today); callImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(callATM.strike, duration / 252.0, 0.04, 0, callATM.optionType, callPrice[0].close, etfPrice); put = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList(); putATM = put[0]; putPrice = Platforms.container.Resolve <OptionDailyRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today, today); putImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(putATM.strike, duration / 252.0, 0.04, 0, putATM.optionType, putPrice[0].close, etfPrice); if (callImpv * putImpv == 0) { volNextMonth = callImpv + putImpv; } else { volNextMonth = (callImpv + putImpv) / 2; } if (duration0 >= step) { optionVol[i] = Math.Sqrt(step / duration0) * volThisMonth; } else if ((duration0 < step && duration1 > step)) { optionVol[i] = Math.Sqrt((duration1 - step) / (duration1 - duration0)) * volThisMonth + Math.Sqrt((step - duration0) / (duration1 - duration0)) * volNextMonth; } else if (duration1 <= step) { optionVol[i] = volNextMonth; } } }
public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate)); var repo = Platforms.container.Resolve <OptionInfoRepository>(); optionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1); Caches.put("OptionInfo", optionInfoList); //初始化头寸信息 SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化(宽)跨式组合信息 SortedDictionary <DateTime, List <StranglePair> > pairs = new SortedDictionary <DateTime, List <StranglePair> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.initialAssets = initialCapital; myAccount.totalAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); List <double> benchmark = new List <double>(); //50ETF的日线数据准备,从回测期开始之前100个交易开始取 int number = 100; List <StockDaily> dailyData = new List <StockDaily>(); dailyData = Platforms.container.Resolve <StockDailyRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, DateUtils.PreviousTradeDay(startDate, number), endDate); var closePrice = dailyData.Select(x => x.close).ToArray(); //按交易日回测 for (int day = 0; day < tradeDays.Count(); day++) { benchmark.Add(closePrice[day + number]); double lastETFPrice = dailyData[number + day - 1].close; var today = tradeDays[day]; //获取当日上市的期权合约列表 var optionInfoList2 = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today); //初始化信号的数据结构 Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); //获取今日日内50ETF数据 var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, tradeDays[day]); //初始化行情信息,将50ETF的价格放入dataToday Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(targetVariety, etfData.Cast <KLine>().ToList()); //记录今日账户信息 myAccount.time = today; //获取今日期权的到期日期 var dateStructure = OptionUtilities.getDurationStructure(optionInfoList2, tradeDays[day]); //选定到日期在40个交易日至60个交易日的合约 double duration = 0; for (int i = 0; i < dateStructure.Count(); i++) { if (dateStructure[i] >= 40 && dateStructure[i] <= 80) { duration = dateStructure[i]; break; } } for (int index = 0; index < 234; index++) //遍历日内的逐个分钟(不包括最后5分钟) { signal = new Dictionary <string, MinuteSignal>(); DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(tradeDays[day]), index); double etfPriceNow = etfData[index].open; //策略思路,当前没有持仓的时候开仓,如果有持仓就判断需不需要止盈或者换月 if (pairs.Count == 0 || (pairs.Count > 0 && pairs.Last().Value.Last().closePrice != 0)) //没有持仓则开仓,简单的在开盘1分钟的时候开仓 { signal = new Dictionary <string, MinuteSignal>(); //今日没有合适的期权合约 if (duration == 0) { continue; } openStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, today, index, duration); } StranglePair pair = pairs.Last().Value.Last(); double durationNow = DateUtils.GetSpanOfTradeDays(today, pair.endDate); //如果有持仓,通过以下手段记录每日分钟信息 if ((pairs.Count > 0 && pairs.Last().Value.Last().closePrice == 0) && (dataToday.ContainsKey(pair.callCode) == false || dataToday.ContainsKey(pair.putCode) == false)) { tradeAssistant(ref dataToday, ref signal, pair.callCode, 0, today, now, index); tradeAssistant(ref dataToday, ref signal, pair.putCode, 0, today, now, index); var callInfo = OptionUtilities.getOptionByCode(optionInfoList2, pair.callCode); if (today.AddDays(1) >= callInfo.modifiedDate && !(today.AddDays(-10000) > callInfo.modifiedDate)) { closeStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, today, index); } } //检查每一个跨式或者宽跨式组合,看看需不需要调整 if (durationNow < 10 && etfPriceNow < pair.callStrike + motion && etfPriceNow > pair.putStrike - motion) //不用调仓直接平仓 { closeStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, today, index); } else if (durationNow < 20 && !(etfPriceNow < pair.callStrike + motion && etfPriceNow > pair.putStrike - motion)) //跨期调仓 { //先进行平仓,然后开仓 closeStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, today, index);//平仓 signal = new Dictionary <string, MinuteSignal>(); StranglePair newPair = new StranglePair(); if (etfPriceNow > pair.callStrike + motion) { OptionInfo call = getOptionCode(duration, etfPriceNow, "认购", today); OptionInfo put = getOptionCode(duration, pair.putStrike, "认沽", today); if (call.strike != 0 && put.strike != 0) //开仓 { newPair = new StranglePair() { callCode = call.optionCode, callStrike = call.strike, callPosition = call.contractMultiplier, putCode = put.optionCode, putStrike = put.strike, putPosition = put.contractMultiplier, closeDate = new DateTime(), closePrice = 0, endDate = call.endDate, etfPrice = etfPriceNow, modifiedDate = today, strangleOpenPrice = dataToday[call.optionCode][index].open + dataToday[put.optionCode][index].open }; modifyStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, ref newPair, today, index); } } else if (etfPriceNow < pair.putStrike - motion) { OptionInfo call = getOptionCode(duration, pair.callStrike, "认购", today); OptionInfo put = getOptionCode(duration, etfPriceNow, "认沽", today); if (call.strike != 0 && put.strike != 0) //开仓 { newPair = new StranglePair() { callCode = call.optionCode, callStrike = call.strike, callPosition = call.contractMultiplier, putCode = put.optionCode, putStrike = put.strike, putPosition = put.contractMultiplier, closeDate = new DateTime(), closePrice = 0, endDate = call.endDate, etfPrice = etfPriceNow, modifiedDate = today, strangleOpenPrice = dataToday[call.optionCode][index].open + dataToday[put.optionCode][index].open }; modifyStrangle(ref dataToday, ref signal, ref positions, ref myAccount, ref pairs, ref newPair, today, index); } } } else if (durationNow >= 20 && !(etfPriceNow < pair.callStrike + motion && etfPriceNow > pair.putStrike - motion)) //不跨期调仓 { if (etfPriceNow > pair.callStrike + motion) { //认购期权向上移仓 OptionInfo call = getOptionCode(durationNow, etfPriceNow, "认购", today); if (call.strike != 0) { tradeAssistant(ref dataToday, ref signal, pair.putCode, 0, today, now, index); tradeAssistant(ref dataToday, ref signal, pair.callCode, -pair.callPosition, today, now, index); tradeAssistant(ref dataToday, ref signal, call.optionCode, call.contractMultiplier, today, now, index); pair.closeDate = now; pair.closePrice = dataToday[pair.callCode][index].open + dataToday[pair.putCode][index].open; StranglePair newPair = new StranglePair() { callCode = call.optionCode, callStrike = call.strike, callPosition = call.contractMultiplier, putCode = pair.putCode, putStrike = pair.putStrike, putPosition = pair.putPosition, closeDate = new DateTime(), closePrice = 0, endDate = call.endDate, etfPrice = etfPriceNow, modifiedDate = now, strangleOpenPrice = dataToday[call.optionCode][index].open + dataToday[pair.putCode][index].open }; pairs.Last().Value.Add(newPair); MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } } else if (etfPriceNow < pair.putStrike - motion) { //认沽期权向下移仓 OptionInfo put = getOptionCode(durationNow, etfPriceNow, "认沽", today); if (put.strike != 0) { tradeAssistant(ref dataToday, ref signal, pair.callCode, 0, today, now, index); tradeAssistant(ref dataToday, ref signal, pair.putCode, -pair.putPosition, today, now, index); tradeAssistant(ref dataToday, ref signal, put.optionCode, put.contractMultiplier, today, now, index); pair.closeDate = now; pair.closePrice = dataToday[pair.callCode][index].open + dataToday[pair.putCode][index].open; StranglePair newPair = new StranglePair() { callCode = pair.callCode, callStrike = pair.callStrike, callPosition = pair.callPosition, putCode = put.optionCode, putStrike = put.strike, putPosition = put.contractMultiplier, closeDate = new DateTime(), closePrice = 0, endDate = put.endDate, etfPrice = etfPriceNow, modifiedDate = now, strangleOpenPrice = dataToday[pair.callCode][index].open + dataToday[put.optionCode][index].open }; pairs.Last().Value.Add(newPair); MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } } } } if (etfData.Count > 0) { //更新当日属性信息 AccountOperator.Minute.maoheng.AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, etfData.Last().time, etfData.Count() - 1, dataToday); //记录历史仓位信息 accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets)); benchmark.Add(etfData.Last().close); if (netValue.Count() == 0) { netValue.Add(new NetValue { time = today, netvalueReturn = 0, benchmarkReturn = 0, netvalue = myAccount.totalAssets, benchmark = etfData.Last().close }); } else { var netValueLast = netValue.Last(); netValue.Add(new NetValue { time = today, netvalueReturn = myAccount.totalAssets / netValueLast.netvalue - 1, benchmarkReturn = etfData.Last().close / netValueLast.benchmark - 1, netvalue = myAccount.totalAssets, benchmark = etfData.Last().close }); } } } //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //记录净值数据 RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "strangle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtil.Transfer(positions); RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "strangle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "strangle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //统计指标在console 上输出 Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate); Console.WriteLine("-----------------------------------------------\n"); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("Base", netWorthOfBenchmark.ToArray()); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); //maoheng 画图 //Application.Run(new PLChart(line, datestr)); //cuixun 画图 //绘制图形的标题 string formTitle = this.startDate.ToShortDateString() + "--" + this.endDate.ToShortDateString() + " " + this.targetVariety + " 净值曲线" + "\r\n" + "\r\n" + "净利润:" + myStgStats.netProfit + " " + "夏普率:" + myStgStats.anualSharpe + " " + "最大回撤:" + myStgStats.maxDrawDown + "\r\n" + "\r\n"; //生成图像 PLChart plc = new PLChart(line, datestr, formTitle: formTitle); //运行图像 //Application.Run(plc); plc.LoadForm(); //保存图像 plc.SaveZed(GetType().FullName, this.targetVariety, this.startDate, this.endDate, myStgStats.netProfit.ToString(), myStgStats.anualSharpe.ToString(), myStgStats.maxDrawDown.ToString()); }
public void compute() { //初始化头寸信息 SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(initialAssets: initialCapital, totalAssets: initialCapital, freeCash: initialCapital); //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); List <double> benchmark = new List <double>(); //标记当日跨式组合多空信号。1表示多头,0表示无信号,-1表示空头。 double orignalSignal = 0; StraddlePair holdingStatus = new StraddlePair(); //统计历史波动率分位数,从回测期开始前一天,统计到最后一天 double[][] fractile = new double[backTestingDuration + 1][]; fractile = computeRollingFractile(startIndex - 1, etfDailyData.Count() - 1, 100); //统计隐含波动率 computeImpv(); //统计隐含波动率和历史波动率之差 epsilon=max[E(IV-HV),0] computeEpsilon(); //按时间遍历,2015年02月09日50ETF期权上市开始,2月10日开始昨日收盘的隐含波动率数据。 for (int i = startIndex + 1; i < startIndex + backTestingDuration; i++) { DateTime today = etfDailyData[i].time; //获取当日上市的期权合约列表 var optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today); //若当日发生50ETF分红派息,myAccount要加上分红的钱,并且需要调整持有头寸的strike if (today == timeOf50ETFDividend2016 && positions.Count > 0 && positions.Last().Value.ContainsKey("510050.SH")) { //50ETF的头寸中记入分红 positions.Last().Value["510050.SH"].totalCashFlow += positions.Last().Value["510050.SH"].volume * bonusOf50ETFDividend2016; //期权持仓行权价调整 foreach (var item in optionInfoList) { if (item.optionCode == holdingStatus.callCodeFront) { holdingStatus.strike = item.strike; holdingStatus.callPositionFront *= item.contractMultiplier / standardContractMultiplier; holdingStatus.putPositionFront *= item.contractMultiplier / standardContractMultiplier; holdingStatus.callPositionNext *= item.contractMultiplier / standardContractMultiplier; holdingStatus.putPositionNext *= item.contractMultiplier / standardContractMultiplier; } } } Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); double fractile90Yesterday = fractile[i - 1][9]; //昨日历史波动率90分位数 double fractile70Yesterday = fractile[i - 1][7]; //昨日历史波动率70分位数 double fractile50Yesterday = fractile[i - 1][5]; //昨日历史波动率50分位数 double fractile30Yesterday = fractile[i - 1][3]; //昨日历史波动率30分位数 double volYesterday = etfVol[i - 1]; //昨日历史波动率 double impvYesterday = optionVol[i - 1]; //昨日隐含波动率 //获取当日ATM期权合约代码 double etfPrice = etfDailyData[i].close; //获取当日期限结构,选取当月合约,若当日合约到日期小于等于3天,直接开仓下月合约 List <double> dateStructure = OptionUtilities.getDurationStructure(optionInfoList, today); double durationFront = dateStructure[0] <= 3 ? dateStructure[1] : dateStructure[0]; double durationNext = dateStructure[0] <= 3 ? dateStructure[2] : dateStructure[1]; StraddlePairCode myPair = getStraddlePairCode(optionInfoList, durationFront, durationNext, etfPrice, today); var callATM = OptionUtilities.getOptionByCode(optionInfoList, myPair.callCodeFront); var callPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today); var putATM = OptionUtilities.getOptionByCode(optionInfoList, myPair.putCodeFront); var putPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today); var callATMNext = OptionUtilities.getOptionByCode(optionInfoList, myPair.callCodeNext); var callPriceNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(callATMNext.optionCode, today); var putATMNext = OptionUtilities.getOptionByCode(optionInfoList, myPair.putCodeNext); var putPriceNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(putATMNext.optionCode, today); //整合当日分钟线数据 Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", today); dataToday.Add("510050.SH", etfData.Cast <KLine>().ToList()); dataToday.Add(callATM.optionCode, callPrice.Cast <KLine>().ToList()); dataToday.Add(putATM.optionCode, putPrice.Cast <KLine>().ToList()); dataToday.Add(callATMNext.optionCode, callPriceNext.Cast <KLine>().ToList()); dataToday.Add(putATMNext.optionCode, putPriceNext.Cast <KLine>().ToList()); //策略信号处理 //信号1 //orignalSignal = 0; //if (volYesterday >= fractile70Yesterday) //{ // //卖出跨式期权 // orignalSignal = -1; //} //else if (impvYesterday < volYesterday) //{ // //买入跨式期权 // orignalSignal = 1; //} //else if (impvYesterday - volYesterday > epsilon[i - 1]) //{ // //卖出跨式期权 // orignalSignal = -1; //} //信号2 orignalSignal = 0; if (volYesterday - impvYesterday > 0 && volYesterday <= fractile50Yesterday) { //买入跨式期权 orignalSignal = 1; } else if (impvYesterday - volYesterday > epsilon[i - 1]) { //卖出跨式期权 orignalSignal = -1; } //指定平仓时间为开盘第一个分钟。 int openIndex = 0; DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex); Console.WriteLine("time: {0}, 昨日历史波动率: {1}, 历史波动率70分位数: {2}, 昨日隐含波动率: {3}", now, volYesterday.ToString("N"), fractile70Yesterday.ToString("N"), optionVol[i - 1].ToString("N")); //如果有持仓先判断持仓状态和信号方向是否相同,如果不同先平仓 if (holdingStatus.callPositionFront != 0) { if (dataToday.ContainsKey(holdingStatus.callCodeFront) == false) { var callLastDayFront = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCodeFront, today); dataToday.Add(holdingStatus.callCodeFront, callLastDayFront.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCodeFront) == false) { var putLastDayFront = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCodeFront, today); dataToday.Add(holdingStatus.putCodeFront, putLastDayFront.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.callCodeNext) == false) { var callLastDayNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCodeNext, today); dataToday.Add(holdingStatus.callCodeNext, callLastDayNext.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCodeNext) == false) { var putLastDayNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCodeNext, today); dataToday.Add(holdingStatus.putCodeNext, putLastDayNext.Cast <KLine>().ToList()); } if (holdingStatus.callPositionFront * orignalSignal < 0) //仓位和信号相反,强制平仓 { Console.WriteLine("平仓!"); MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint); holdingStatus = new StraddlePair(); } if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓 { Console.WriteLine("平仓!"); MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint); holdingStatus = new StraddlePair(); } } //指定开仓时间为开盘第10分钟。错开开平仓的时间。 openIndex = 10; now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex); if (holdingStatus.callPositionFront == 0 && orignalSignal != 0) //无仓位有信号,开仓 { if (orignalSignal == 1) //做多跨式期权 { MinuteSignal openSignalCallFront = new MinuteSignal() { code = callATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalPutFront = new MinuteSignal() { code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalCallNext = new MinuteSignal() { code = callATMNext.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPriceNext[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalPutNext = new MinuteSignal() { code = putATMNext.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPriceNext[openIndex].open, minuteIndex = openIndex }; Console.WriteLine("做多跨式期权!"); signal.Add(callATM.optionCode, openSignalCallFront); signal.Add(putATM.optionCode, openSignalPutFront); signal.Add(callATMNext.optionCode, openSignalCallNext); signal.Add(putATMNext.optionCode, openSignalPutNext); //变更持仓状态 holdingStatus = new StraddlePair { callCodeFront = callATM.optionCode, putCodeFront = putATM.optionCode, callCodeNext = callATMNext.optionCode, putCodeNext = putATMNext.optionCode, callPositionFront = optionVolume, putPositionFront = optionVolume, callPositionNext = -optionVolume, putPositionNext = -optionVolume, etfPrice_open = etfData[openIndex].open, straddlePairPrice_open = callPrice[openIndex].open + putPrice[openIndex].open - callPriceNext[openIndex].open - putPriceNext[openIndex].open, straddleOpenDate = today, endDate = callATM.endDate, strike = callATM.strike, endDateNext = callATMNext.endDate }; } else if (orignalSignal == -1) //做空跨式期权 { MinuteSignal openSignalCall = new MinuteSignal() { code = callATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalPut = new MinuteSignal() { code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalCallNext = new MinuteSignal() { code = callATMNext.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPriceNext[openIndex].open, minuteIndex = openIndex }; MinuteSignal openSignalPutNext = new MinuteSignal() { code = putATMNext.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPriceNext[openIndex].open, minuteIndex = openIndex }; Console.WriteLine("做空跨式期权!"); signal.Add(callATM.optionCode, openSignalCall); signal.Add(putATM.optionCode, openSignalPut); signal.Add(callATMNext.optionCode, openSignalCallNext); signal.Add(putATMNext.optionCode, openSignalPutNext); //变更持仓状态 holdingStatus = new StraddlePair { callCodeFront = callATM.optionCode, putCodeFront = putATM.optionCode, callCodeNext = callATMNext.optionCode, putCodeNext = putATMNext.optionCode, callPositionFront = -optionVolume, putPositionFront = -optionVolume, callPositionNext = optionVolume, putPositionNext = optionVolume, etfPrice_open = etfData[openIndex].open, straddlePairPrice_open = -callPrice[openIndex].open - putPrice[openIndex].open + callPriceNext[openIndex].open + putPriceNext[openIndex].open, straddleOpenDate = today, endDate = callATM.endDate, strike = callATM.strike, endDateNext = callATMNext.endDate }; } MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: openIndex); } //每日收盘前,整理持仓情况 int thisIndex = 239; double delta = 0; var thisTime = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex); if (today >= timeOf50ETFDividend2016) { benchmark.Add(etfData[thisIndex].close + bonusOf50ETFDividend2016); } else { benchmark.Add(etfData[thisIndex].close); } if (holdingStatus.callPositionFront != 0) { if (dataToday.ContainsKey(holdingStatus.callCodeFront) == false) { var callLastDayFront = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCodeFront, today); dataToday.Add(holdingStatus.callCodeFront, callLastDayFront.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCodeFront) == false) { var putLastDayFront = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCodeFront, today); dataToday.Add(holdingStatus.putCodeFront, putLastDayFront.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.callCodeNext) == false) { var callLastDayNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCodeNext, today); dataToday.Add(holdingStatus.callCodeNext, callLastDayNext.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCodeNext) == false) { var putLastDayNext = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCodeNext, today); dataToday.Add(holdingStatus.putCodeNext, putLastDayNext.Cast <KLine>().ToList()); } //计算期权delta值,并用50ETF对冲 //var positionLast = positions.Last().Value; //delta= computeOptionDelta(positionLast, holdingStatus, today, dataToday, thisIndex); //double etfChangeVolume =Math.Round( -delta - holdingStatus.etfPosition); //MinuteSignal openSignalETF = new MinuteSignal() { code = "510050.SH", volume = etfChangeVolume, time = thisTime, tradingVarieties = "stock", price = dataToday["510050.SH"][thisIndex].open, minuteIndex = thisIndex }; //signal = new Dictionary<string, MinuteSignal>(); //signal.Add("510050.SH", openSignalETF); //MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: thisTime); //holdingStatus.etfPosition += etfChangeVolume; // AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday); } //更新当日属性信息 AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, thisTime, data: dataToday, nowIndex: thisIndex); //记录历史仓位信息 accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets)); //在控制台上数据每日持仓信息 if (holdingStatus.callPositionFront != 0) { Console.WriteLine("time: {0},etf: {1}, strike: {2}, position: {3}, call: {4}, put: {5}, endDate: {6}, delta: {7}, etfVolume: {8}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPositionFront, dataToday[holdingStatus.callCodeFront][thisIndex].close, dataToday[holdingStatus.putCodeFront][thisIndex].close, holdingStatus.endDate, delta, holdingStatus.etfPosition); } //Console.WriteLine("time: {0}, total: {1}, cash: {2}, option: {3}, margin: {4}", thisTime, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin); } //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions, benchmark.ToArray()); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //记录净值数据 RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtil.Transfer(positions); RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //统计指标在console 上输出 Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate); Console.WriteLine("-----------------------------------------------\n"); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("Base", netWorthOfBenchmark.ToArray()); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); Application.Run(new PLChart(line, datestr)); }
public void compute() { SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.initialAssets = initialCapital; myAccount.totalAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); List <double> benchmark = new List <double>(); //标记当日跨式组合多空信号。1表示多头,0表示无信号,-1表示空头。 double orignalSignal = 0; Straddle holdingStatus = new Straddle(); //统计历史波动率分位数,从回测期开始前一天,统计到最后一天 double[][] fractile = new double[backTestingDuration + 1][]; fractile = computeRollingFractile(startIndex - 1, etfDailyData.Count() - 1, 100); //统计隐含波动率 computeImpv(); //统计隐含波动率和历史波动率之差 epsilon=max[E(IV-HV),0] computeEpsilon(); //按时间遍历,2015年02月09日50ETF期权上市开始,2月10日开始昨日收盘的隐含波动率数据。 for (int i = startIndex + 1; i < startIndex + backTestingDuration; i++) { Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); double fractile90Yesterday = fractile[i - 1][9]; //昨日历史波动率90分位数 double fractile70Yesterday = fractile[i - 1][7]; //昨日历史波动率70分位数 double fractile50Yesterday = fractile[i - 1][5]; //昨日历史波动率50分位数 double fractile30Yesterday = fractile[i - 1][3]; //昨日历史波动率30分位数 double volYesterday = etfVol[i - 1]; //昨日历史波动率 double impvYesterday = optionVol[i - 1]; //昨日隐含波动率 //获取当日ATM期权合约代码 DateTime today = etfDailyData[i].time; double etfPrice = etfDailyData[i].close; double duration; //获取当日期限结构,选取当月合约,若当日合约到日期小于等于3天,直接开仓下月合约 List <double> dateStructure = OptionUtilities.getDurationStructure(optionInfoList, today); double duration0 = dateStructure[0] <= 3 ? dateStructure[1] : dateStructure[0]; duration = duration0; var call = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList(); var callATM = call[0]; var callPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today); // double callImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(callATM.strike, duration / 252.0, 0.04, 0, callATM.optionType, callPrice[0].close, etfPrice); var put = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList(); var putATM = put[0]; var putPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today); //double putImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(putATM.strike, duration / 252.0, 0.04, 0, putATM.optionType, putPrice[0].close, etfPrice); //整合当日分钟线数据 Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", today); dataToday.Add("510050.SH", etfData.Cast <KLine>().ToList()); dataToday.Add(callATM.optionCode, callPrice.Cast <KLine>().ToList()); dataToday.Add(putATM.optionCode, putPrice.Cast <KLine>().ToList()); //策略信号处理 ////信号1 //orignalSignal = 0; //if (volYesterday >= fractile70Yesterday) //{ // //卖出跨式期权 // orignalSignal = -1; //} //else if (optionVol[i - 1] < volYesterday) //{ // //买入跨式期权 // orignalSignal = 1; //} //else if (optionVol[i - 1] - volYesterday > epsilon[i - 1]) //{ // //卖出跨式期权 // orignalSignal = -1; //} //信号2 orignalSignal = 0; if (volYesterday - impvYesterday > 0 && volYesterday <= fractile70Yesterday) { //买入跨式期权 //orignalSignal = 1; } else if (impvYesterday - volYesterday > 2 * epsilon[i - 1]) { //卖出跨式期权 orignalSignal = -1; } //指定平仓时间为开盘第一个分钟。 DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), 0); Console.WriteLine("time: {0}, 昨日历史波动率: {1}, 历史波动率70分位数: {2}, 昨日隐含波动率: {3}", now, volYesterday.ToString("N"), fractile70Yesterday.ToString("N"), optionVol[i - 1].ToString("N")); //如果有持仓先判断持仓状态和信号方向是否相同,如果不同先平仓 if (holdingStatus.callPosition != 0) { //平仓之前获取IH数据 if (holdingStatus.IHCode != null) { var IHData = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.IHCode, today); dataToday.Add(holdingStatus.IHCode, IHData.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.callCode) == false) { var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today); dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCode) == false) { var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today); dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList()); } if (holdingStatus.callPosition * orignalSignal < 0) //仓位和信号相反,强制平仓 { Console.WriteLine("平仓!"); MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint); holdingStatus = new Straddle(); } if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓 { Console.WriteLine("平仓!"); MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint); holdingStatus = new Straddle(); } } //指定开仓时间为开盘第10分钟。错开开平仓的时间。 int openIndex = 10; now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex); if (holdingStatus.callPosition == 0 && orignalSignal != 0) //无仓位有信号,开仓 { if (orignalSignal == 1) //做多跨式期权 { MinuteSignal openSignalCall = new MinuteSignal() { code = callATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex }; MinuteSignal openSignalPut = new MinuteSignal() { code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex }; Console.WriteLine("做多跨式期权!"); signal.Add(callATM.optionCode, openSignalCall); signal.Add(putATM.optionCode, openSignalPut); //变更持仓状态 holdingStatus.callCode = callATM.optionCode; holdingStatus.putCode = putATM.optionCode; holdingStatus.callPosition = optionVolume; holdingStatus.putPosition = optionVolume; holdingStatus.etfPrice_open = etfData[openIndex].close; holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close; holdingStatus.straddleOpenDate = today; holdingStatus.endDate = callATM.endDate; holdingStatus.strike = callATM.strike; } else if (orignalSignal == -1) //做空跨式期权 { MinuteSignal openSignalCall = new MinuteSignal() { code = callATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex }; MinuteSignal openSignalPut = new MinuteSignal() { code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex }; Console.WriteLine("做空跨式期权!"); signal.Add(callATM.optionCode, openSignalCall); signal.Add(putATM.optionCode, openSignalPut); //变更持仓状态 holdingStatus.callCode = callATM.optionCode; holdingStatus.putCode = putATM.optionCode; holdingStatus.callPosition = -optionVolume; holdingStatus.putPosition = -optionVolume; holdingStatus.etfPrice_open = etfData[openIndex].close; holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close; holdingStatus.straddleOpenDate = today; holdingStatus.endDate = callATM.endDate; holdingStatus.strike = callATM.strike; } MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, capitalVerification: false); } //每日收盘前,整理持仓情况 int thisIndex = 239; var thisTime = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex); benchmark.Add(etfData[thisIndex].close); if (holdingStatus.callPosition != 0) { if (dataToday.ContainsKey(holdingStatus.callCode) == false) { var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today); dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList()); } if (dataToday.ContainsKey(holdingStatus.putCode) == false) { var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today); dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList()); } AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday); } //更新当日属性信息 AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday); //记录历史仓位信息 accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets)); //在控制台上数据每日持仓信息 if (holdingStatus.callPosition != 0) { Console.WriteLine("time: {0},etf: {1}, strike: {2}, position: {3}, call: {4}, put: {5}, endDate: {6}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPosition, dataToday[holdingStatus.callCode][thisIndex].close, dataToday[holdingStatus.putCode][thisIndex].close, holdingStatus.endDate); } //Console.WriteLine("time: {0}, total: {1}, cash: {2}, option: {3}, margin: {4}", thisTime, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin); } //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions, benchmark.ToArray()); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //记录净值数据 RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtil.Transfer(positions); RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //统计指标在console 上输出 Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate); Console.WriteLine("-----------------------------------------------\n"); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("Base", netWorthOfBenchmark.ToArray()); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); Application.Run(new PLChart(line, datestr)); }
public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate)); var repo = Platforms.container.Resolve <OptionInfoRepository>(); var optionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1); Caches.put("OptionInfo", optionInfoList); SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.initialAssets = initialCapital; myAccount.totalAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); List <double> benchmark = new List <double>(); ///数据准备 //记录牛市价差两条腿的信息 BullSpread myLegs = new BullSpread(); //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); //50ETF的日线数据准备,从回测期开始之前100个交易开始取 int number = 100; List <StockDaily> dailyData = new List <StockDaily>(); dailyData = Platforms.container.Resolve <StockDailyRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, DateUtils.PreviousTradeDay(startDate, number), endDate); //计算50ETF的EMA var closePrice = dailyData.Select(x => x.close).ToArray(); List <double> ema7 = TA_MA.EMA(closePrice, 5).ToList(); List <double> ema50 = TA_MA.EMA(closePrice, 20).ToList(); List <double> ema10 = TA_MA.EMA(closePrice, 10).ToList(); double maxProfit = 0; for (int day = 1; day < tradeDays.Count(); day++) { benchmark.Add(closePrice[day + number]); var today = tradeDays[day]; myAccount.time = today; var dateStructure = OptionUtilities.getDurationStructure(optionInfoList, tradeDays[day]); double duration = 0; for (int i = 0; i < dateStructure.Count(); i++) { if (dateStructure[i] >= 20 && dateStructure[i] <= 40) { duration = dateStructure[i]; break; } } Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, tradeDays[day]); if (ema7[day + number - 1] - ema50[day + number - 1] > 0 && dailyData[number + day - 1].close > ema10[day + number - 1] && myLegs.strike1 == 0) // EMA7日线大于EMA50日线,并且ETF价格站上EMA10,开牛市价差 { //取出指定日期 double lastETFPrice = dailyData[number + day - 1].close; Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(targetVariety, etfData.Cast <KLine>().ToList()); DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(tradeDays[day]), 0); //MinuteSignal openSignal = new MinuteSignal() { code = targetVariety, volume = 10000, time = now, tradingVarieties = "stock", price =averagePrice, minuteIndex = day }; //signal.Add(targetVariety, openSignal); //选取指定的看涨期权 var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, tradeDays[day], duration), "认购"), lastETFPrice, lastETFPrice + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); //如果可以构成看涨期权牛市价差,就开仓 if (list.Count() >= 2) { var option1 = list[0]; var option2 = list[list.Count() - 1]; var option1Data = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(option1.optionCode, today); var option2Data = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(option2.optionCode, today); if ((option1Data[0].close > 0 && option2Data[0].close > 0) == true) { dataToday.Add(option1.optionCode, option1Data.Cast <KLine>().ToList()); dataToday.Add(option2.optionCode, option2Data.Cast <KLine>().ToList()); //var vol1 = ImpliedVolatilityUtilities.ComputeImpliedVolatility(option1.strike, duration / 252.0, 0.04, 0, option1.optionType, option1Data[0].close, etfData[0].close); //var vol2 = ImpliedVolatilityUtilities.ComputeImpliedVolatility(option2.strike, duration / 252.0, 0.04, 0, option2.optionType, option2Data[0].close, etfData[0].close); MinuteSignal openSignal1 = new MinuteSignal() { code = option1.optionCode, volume = 10000, time = now, tradingVarieties = "option", price = option1Data[0].close, minuteIndex = 0 }; MinuteSignal openSignal2 = new MinuteSignal() { code = option2.optionCode, volume = -10000, time = now, tradingVarieties = "option", price = option2Data[0].close, minuteIndex = 0 }; Console.WriteLine("开仓!"); signal.Add(option1.optionCode, openSignal1); signal.Add(option2.optionCode, openSignal2); myLegs.code1 = option1.optionCode; myLegs.code2 = option2.optionCode; myLegs.strike1 = option1.strike; myLegs.strike2 = option2.strike; myLegs.endDate = option1.endDate; myLegs.spreadPrice_Open = option1Data[0].close - option2Data[0].close; myLegs.etfPrice_Open = etfData[0].close; myLegs.spreadOpenDate = now; maxProfit = 0; Console.WriteLine("time: {0},etf: {1}, call1: {2} call1price: {3}, call2: {4}, call2price: {5}", now, etfData[0].close, myLegs.strike1, option1Data[0].close, myLegs.strike2, option2Data[0].close); } } MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, capitalVerification: false); } if (positions.Count() > 0 && myLegs.strike1 != 0) { Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(targetVariety, etfData.Cast <KLine>().ToList()); var option1Data = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(myLegs.code1, today); var option2Data = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(myLegs.code2, today); dataToday.Add(myLegs.code1, option1Data.Cast <KLine>().ToList()); dataToday.Add(myLegs.code2, option2Data.Cast <KLine>().ToList()); int thisIndex = 239; var thisTime = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex); var etfPriceNow = etfData[thisIndex].close; var durationNow = DateUtils.GetSpanOfTradeDays(today, myLegs.endDate); Console.WriteLine("time: {0},etf: {1}, call1: {2} call1price: {3}, call2: {4}, call2price: {5}", thisTime, etfPriceNow, myLegs.strike1, option1Data[thisIndex].close, myLegs.strike2, option2Data[thisIndex].close); //多个退出条件①收益达到最大收益的60%以上②多日之内不上涨③迅速下跌 double spreadPrice = option1Data[thisIndex].close - option2Data[thisIndex].close; maxProfit = (spreadPrice - myLegs.spreadPrice_Open) > maxProfit ? spreadPrice - myLegs.spreadPrice_Open : maxProfit; double holdingDays = DateUtils.GetSpanOfTradeDays(myLegs.spreadOpenDate, today); //止盈 bool profitTarget = (spreadPrice) > 0.6 * (myLegs.strike2 - myLegs.strike1) && durationNow >= 10; //止损 bool lossTarget1 = (spreadPrice - myLegs.spreadPrice_Open) < 0 && holdingDays > 20; bool lossTarget2 = etfPriceNow < myLegs.strike1 - 0.2; bool lossTarget3 = spreadPrice / myLegs.spreadPrice_Open < 0.6; bool lossTarget4 = maxProfit > 0.02 && (spreadPrice - myLegs.spreadPrice_Open) / maxProfit < 0.8; if (profitTarget || lossTarget1 || lossTarget2 || lossTarget3 || lossTarget4 || durationNow <= 1 || holdingDays >= 7) { Console.WriteLine("平仓!"); maxProfit = 0; myLegs = new BullSpread(); MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, thisTime, slipPoint); } AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday); } else { int thisIndex = 239; var thisTime = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex); Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(targetVariety, etfData.Cast <KLine>().ToList()); AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday); } BasicAccount tempAccount = new BasicAccount(); tempAccount.time = myAccount.time; tempAccount.freeCash = myAccount.freeCash; tempAccount.margin = myAccount.margin; tempAccount.positionValue = myAccount.positionValue; tempAccount.totalAssets = myAccount.totalAssets; tempAccount.initialAssets = myAccount.initialAssets; accountHistory.Add(tempAccount); } //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //记录净值数据 RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "EMA7_EMA50", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtil.Transfer(positions); RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "EMA7_EMA50", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "EMA7_EMA50", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //统计指标在console 上输出 Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate); Console.WriteLine("-----------------------------------------------\n"); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("Base", netWorthOfBenchmark.ToArray()); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); Application.Run(new PLChart(line, datestr)); }
/// <summary> /// 策略回测部分,期权时间价值策略 /// 在日循环上判断(1)选择操作标的(2)是否开平仓,在分钟循环上进行具体操作 /// 分钟上的操作:(1)开仓(2)到期平仓(3)调仓平值期权(4)止盈止损 /// (1)若直接开平仓,在开盘15分钟时进行操作(2)若判断止盈止损,在收盘15分钟时进行操作 /// </summary> public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startdate), Kit.ToInt_yyyyMMdd(endDate)); var repo = Platforms.container.Resolve <OptionInfoRepository>(); var optionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1); Caches.put("OptionInfo", optionInfoList); List <DateTime> tradeDays = DateUtils.GetTradeDays(startdate, endDate); //var ETFDaily = Platforms.container.Resolve<StockDailyRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", Kit.ToDate(20150101),Kit.ToDate(20160731)); ///账户初始化 //初始化position SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.totalAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); ///回测循环 //回测循环--By Day foreach (var day in tradeDays) { //日内数据准备 Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >(); var list = OptionUtilities.getOptionListByDate(optionInfoList, Kit.ToInt_yyyyMMdd(day)); List <DateTime> endDate = OptionUtilities.getEndDateListByAscending(list); var ETFtoday = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", day); data.Add("510050.SH", ETFtoday.Cast <KLine>().ToList()); foreach (var info in list) { string IHCode = OptionUtilities.getCorrespondingIHCode(info, Kit.ToInt_yyyyMMdd(day)); var repoOption = Platforms.container.Resolve <OptionMinuteRepository>(); var optionToday = repoOption.fetchFromLocalCsvOrWindAndSave(info.optionCode, day); data.Add(info.optionCode, optionToday.Cast <KLine>().ToList()); } int index = 0; //交易开关设置,控制day级的交易开关 bool tradingOn = true; //总交易开关 bool openingOn = true; //开仓开关 bool closingOn = true; //平仓开关 //是否为交割日 bool isExpiredDay = day.Equals(endDate[0]); //是否为回测最后一天 bool isLastDayOfBackTesting = day.Equals(endDate); //回测循环 -- By Minute //不允许在同一根1minBar上开平仓 while (index < 240) { int nextIndex = index + 1; DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(day), index); Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); double etfPrice = ETFtoday[index].close; //按strike price与etf价格的接近程度排序 List <double> strikeTodayArr = OptionUtilities.getStrikeListByAscending(list).OrderBy(x => Math.Abs(x - etfPrice)).ToList(); try { /* * if (index != 225) * { * index = nextIndex; * continue; * } */ //持仓查询,先平后开 //若当前有持仓 且 允许平仓 //是否是空仓,若position中所有品种volum都为0,则说明是空仓 bool isEmptyPosition = positions.Count != 0 ? positions[positions.Keys.Last()].Values.Sum(x => Math.Abs(x.volume)) == 0 : true; if ((positions.Count != 0 && !isEmptyPosition) && tradingOn) { //平仓条件 //(1)若当天为交割日或回测结束日,平仓,且关闭开仓开关,次日才能开仓; //(2)若closingOn为false,平仓; //(3)检查持仓期权是否为平价期权,若否,清掉当前头寸并建立新的持仓; //-------------------------------------------------------------------- //(1)若当天为交割日或回测结束日,平仓,且关闭开仓开关,次日才能开仓; //(2)若closingOn为false,平仓; //取出当前持仓期权的strike double strikePriceOfPositions = optionInfoList[optionInfoList.FindIndex(a => a.optionCode == positions[positions.Keys.Last()].Values.First().code)].strike; bool isParPriceOption = strikePriceOfPositions == strikeTodayArr[0]; //-------------------------------------------------------------------- if (!isEmptyPosition && (isExpiredDay || isLastDayOfBackTesting || closingOn == false)) { //全部平仓 DateTime next = MinuteCloseAllPositonsWithSlip.closeAllPositions(data, ref positions, ref myAccount, now: now, slipPoint: slipPoint); //当天不可再开仓 openingOn = false; } //(3)检查持仓期权是否为平价期权,若否,清掉当前头寸并建立新的持仓; else if (!isEmptyPosition && !isParPriceOption) { //全部平仓 DateTime next = MinuteCloseAllPositonsWithSlip.closeAllPositions(data, ref positions, ref myAccount, now: now, slipPoint: slipPoint); //当天不可再开仓 openingOn = false; } } //若当前无持仓 且 允许开仓 //若当前为交割日,则不开仓 if (isExpiredDay == true) { openingOn = false; } else if ((positions.Count == 0 || isEmptyPosition) && openingOn && tradingOn) { //标的池构建 //选择目标期权品种放入标的池: //四个头寸(1)short当月平价认购(2)short当月平价认沽(3)long下月平价认购(4)long下月平价认沽 OptionInfo callCandidateFront = OptionUtilities.getSpecifiedOption(list, endDate[0], "认购", strikeTodayArr[0])[0]; OptionInfo putCandidateFront = OptionUtilities.getSpecifiedOption(list, endDate[0], "认沽", strikeTodayArr[0])[0]; OptionInfo callCandidateNext = OptionUtilities.getSpecifiedOption(list, endDate[1], "认购", strikeTodayArr[0])[0]; OptionInfo putCandidateNext = OptionUtilities.getSpecifiedOption(list, endDate[1], "认沽", strikeTodayArr[0])[0]; //检查四个标的strike是否相同,若相同则开仓,若不相同是,说明下月平价期权尚未挂出,则continue bool isSameStrike = callCandidateFront.strike == callCandidateFront.strike; //生成开仓信号 if (isSameStrike) { //查询可用资金 double nowFreeCash = myAccount.freeCash; //计算每个头寸的建仓量,原则:尽量使各头寸等金额 double openVolumeOfCallFront = Math.Floor(nowFreeCash / 4 / data[callCandidateFront.optionCode][index].close / optionContractTimes) * optionContractTimes; double openVolumeOfPutFront = Math.Floor(nowFreeCash / 4 / data[putCandidateFront.optionCode][index].close / optionContractTimes) * optionContractTimes; double openVolumeOfCallNext = Math.Floor(nowFreeCash / 4 / data[callCandidateNext.optionCode][index].close / optionContractTimes) * optionContractTimes; double openVolumeOfPutNext = Math.Floor(nowFreeCash / 4 / data[putCandidateNext.optionCode][index].close / optionContractTimes) * optionContractTimes; MinuteSignal callFront = new MinuteSignal() { code = callCandidateFront.optionCode, volume = -openVolumeOfCallFront, time = now, tradingVarieties = "option", price = data[callCandidateFront.optionCode][index].close, minuteIndex = index }; MinuteSignal putFront = new MinuteSignal() { code = putCandidateFront.optionCode, volume = -openVolumeOfPutFront, time = now, tradingVarieties = "option", price = data[putCandidateFront.optionCode][index].close, minuteIndex = index }; MinuteSignal callNext = new MinuteSignal() { code = callCandidateNext.optionCode, volume = openVolumeOfCallNext, time = now, tradingVarieties = "option", price = data[callCandidateNext.optionCode][index].close, minuteIndex = index }; MinuteSignal putNext = new MinuteSignal() { code = putCandidateNext.optionCode, volume = openVolumeOfPutNext, time = now, tradingVarieties = "option", price = data[putCandidateNext.optionCode][index].close, minuteIndex = index }; signal.Add(callFront.code, callFront); signal.Add(putFront.code, putFront); signal.Add(callNext.code, callNext); signal.Add(putNext.code, putNext); DateTime next = MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, data, ref positions, ref myAccount, slipPoint: slipPoint, now: now); nextIndex = Math.Max(nextIndex, TimeListUtility.MinuteToIndex(next)); } } //账户信息更新 AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, now, data); } catch (Exception) { throw; } index = nextIndex; } //账户信息记录By Day //用于记录的临时账户 BasicAccount tempAccount = new BasicAccount(); tempAccount.time = myAccount.time; tempAccount.freeCash = myAccount.freeCash; tempAccount.margin = myAccount.margin; tempAccount.positionValue = myAccount.positionValue; tempAccount.totalAssets = myAccount.totalAssets; accountHistory.Add(tempAccount); } //遍历输出到console foreach (var account in accountHistory) { Console.WriteLine("time:{0},netWorth:{1,8:F3}\n", account.time, account.totalAssets / initialCapital); } //将accountHistory输出到csv var resultPath = ConfigurationManager.AppSettings["CacheData.ResultPath"] + "accountHistory.csv"; var dt = DataTableUtils.ToDataTable(accountHistory); // List<MyModel> -> DataTable CsvFileUtils.WriteToCsvFile(resultPath, dt); // DataTable -> CSV File Console.ReadKey(); }
protected void Page_Load(object sender, EventArgs e) { string pageChecker = Request.QueryString["page"]; if (string.IsNullOrEmpty(pageChecker)) { Session.Remove("WithdrawalSearch"); } using (NpgsqlConnection conn = Classes.DB.InstBTCDB("instbtc")) { NpgsqlCommand command = new NpgsqlCommand(); string query = string.Empty; if (Request.HttpMethod == "POST") { string status = txtStatus.Value; string clientId = txtClientId.Value; string clientEmail = txtClientEmail.Value; string clientName = txtClientName.Value; string walletId = txtWalletId.Value; string documentId = txtWalletId.Value; string createdDate = txtCreatedDate.Value; string docStatus = txtDocStatus.Value; string refferenceHash = txtHash.Value; WithdrawalSearchModel searchModel = new WithdrawalSearchModel() { Status = status, ClientModel = new ClientModelSearch { Id = clientId, Email = clientEmail }, DocumentStatus = docStatus, ClientName = clientName, WalletId = walletId, DocumentId = documentId, CreatedDate = createdDate, refference_hash = refferenceHash }; Session["WithdrawalSearch"] = searchModel; } if (Session["WithdrawalSearch"] != null) { WithdrawalSearchModel sessionSearchModel = (WithdrawalSearchModel)Session["WithdrawalSearch"]; //Get SQLCommand Parameters and Query Parameters KeyValuePair <NpgsqlCommand, string> pair = getParameter(sessionSearchModel); command = pair.Key; query = pair.Value; } //Get Withdrawals Datas List <MWithdrawal> withdrawalRequest = new List <MWithdrawal>(); withdrawalRequest = WithdrawalUtilities.WithdrawalRequest(conn, command, query); #region Pagination Dictionary <string, int> pagerDictionary = new Dictionary <string, int>(); string paginationString = "page"; DocListPager.initialize(paginationString, 10); TotalRequest = WithdrawalUtilities.GetWithdrawalCount(conn, command, query); pagerDictionary = DocListPager.paginate(TotalRequest); withdrawalRequest = WithdrawalUtilities.WithdrawalRequest(conn, command, query, pagerDictionary["offset"], pagerDictionary["limit"]); rowCount.InnerText = $"{TotalRequest} Records Found"; resultBody.InnerHtml = BuildHtmlTable(withdrawalRequest).ToString(); #endregion //Get Document Statuses Selection OptionUtilities optionUtilities = new OptionUtilities(); txtDocStatus.Items.Clear(); txtDocStatus.Items.AddRange(optionUtilities.GetDocumentStatusOptions()); //Return Values to their Fields ReturnFieldValues(); } }
protected void Page_Load(object sender, EventArgs e) { string username = Classes.Cookie.GetCookie("ggZurkVKwLIM+SQ2NMcfsra8/nnrhm9u5sl4TMYTE2Y", false); var roles = Utilities.AccountUtilities.getUserRoles(username); RoleUtilities roleUtilities = new RoleUtilities(); List <RolesModel> rolesModels = new List <RolesModel>(); List <string> roleLists = roles.Split(',').ToList(); if (roleLists.Find(f => f.ToLower() == "12") == null) { Response.Redirect("/dashboard.aspx"); } if (Request.HttpMethod == "POST") { #region Assign Post Variables string ClientId = Request.Form["clientId"]; string PaymentReferrence = Request.Form["pspRef"]; string PaymentStatus = Request.Form["pspStatus"]; string CreditedStatus = Request.Form["creditedStatus"]; string PspId = Request.Form["ctl00$ctl00$MainContent$LMainContent$pspId"]; string Amount = Request.Form["amount"]; string Currency = Request.Form["currency"]; string Note = Request.Form["note"]; string CardLast4 = Request.Form["cardLast4"]; string CardExpiry = Request.Form["cardExpiry"]; string CardHolderName = Request.Form["cardHolder"]; string TradingAccountId = Request.Form["PIN"]; #endregion string PIN = !string.IsNullOrEmpty(TradingAccountId) ? TradingAccountId : string.Empty; #region Get Client Info ClientModel Info = new ClientModel(); using (NpgsqlConnection conn = Classes.DB.InstBTCDB("instbtc")) { Info = Clients.FindById(conn, int.Parse(ClientId)); } #endregion if (Info != null) { #region Creating the Transaction Object Classes.Instbtc.Models.TransactionModel Transaction = new Classes.Instbtc.Models.TransactionModel { Psp_ID = decimal.Parse(PspId), Deposit_Currency = Currency, Deposit_Amount = Convert.ToDecimal(Amount), Exchange_Currency = "BTC", Exchange_Amount = Convert.ToDecimal(Classes.Instbtc.Utilities.Conversion.GetBTCAmountRestSharp(Amount, Currency)), Created_Date = DateTime.UtcNow, Client_ID = Convert.ToDecimal(ClientId), Psp_Status = PaymentStatus, Credited_Status = CreditedStatus, PaymentReference = PaymentReferrence, Notes = Note, type = Classes.Instbtc.Models.TransactionType.DEPOSIT, CardLast4 = !string.IsNullOrEmpty(CardLast4) ? CardLast4 : "", CardHolderName = !string.IsNullOrEmpty(CardHolderName) ? CardHolderName : "", Transaction_Currency = "BTC" }; #endregion #region Create The Transaction var TransactionCheck = Utilities.Transactions.CheckTransactionIfExist(Transaction.PaymentReference); object result = new object(); if (string.IsNullOrEmpty(TransactionCheck)) { result = Classes.Instbtc.Create.Transactions.CreateTransaction(Transaction); } else { toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("error", $"Cant Create Transaction, Existing Detected!")); Response.Redirect("/transaction-lists.aspx"); } #endregion if (result.ToString() != "Internal Error" || result != null) { decimal OriginalDepositTransactionAmount = decimal.Parse(Transaction.Deposit_Amount.ToString()); decimal OriginalBtcExchangeAmount = decimal.Parse(Transaction.Exchange_Amount.ToString()); EmailTemplateUtilities.SendNotificationDeposit(Info.Id.ToString(), OriginalDepositTransactionAmount.ToString(), OriginalBtcExchangeAmount.ToString(), Transaction); //if (Info.Referral.ToLower() == "lblv" || Info.Referral.ToLower() == "tradershome" || Info.Referral.ToLower() == "profitix" || Info.Referral.ToLower() == "vlom" || Info.Referral.ToLower() == "uptos" || Info.Referral.ToLower() == "fundiza" || Info.Referral.ToLower() == "kiplar" || Info.Referral.ToLower() == "investigram" || Info.Referral.ToLower() == "commercewealth") //{ // try // { // var pspList = PspUtilities.GetPspById(Transaction.Psp_ID.ToString()); // var pspName = pspList.Where(w => w.Id == int.Parse(Transaction.Psp_ID.ToString()))?.FirstOrDefault()?.Name; // pspName = pspName.Replace("USD ", "").Replace("EUR ", "").Replace("AUD ", ""); // var res = BrandsIntegration.PushToBrand(Info, Transaction, PaymentStatus, result, $"[{pspName}] " + Note, PIN, pspName); // toastrUtilities.SessionPush("toast", new KeyValuePair<string, string>("success", $"{res}")); // } // catch (Exception ex) // { // toastrUtilities.SessionPush("toast", new KeyValuePair<string, string>("error", $"{ex.Message.ToString()}")); // } //} //else //{ // decimal OriginalDepositTransactionAmount = decimal.Parse(Transaction.Deposit_Amount.ToString()); // decimal OriginalBtcExchangeAmount = decimal.Parse(Transaction.Exchange_Amount.ToString()); // EmailTemplateUtilities.SendNotificationDeposit(Info.Id.ToString(), OriginalDepositTransactionAmount.ToString(), OriginalBtcExchangeAmount.ToString(), Transaction); //} } else { toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("error", $"An Error Occured Cant Create Transaction")); } } else { //Redirect Invalid Client Id; toastrUtilities.SessionPush("toast", new KeyValuePair <string, string>("error", $"No Client With this Id: {ClientId}")); Response.Redirect("/transaction-lists.aspx"); } } OptionUtilities optionUtilities = new OptionUtilities(); pspId.Items.AddRange(optionUtilities.GetPSPOptions()); }
public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate)); var repo = Platforms.container.Resolve <OptionInfoRepository>(); optionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1); Caches.put("OptionInfo", optionInfoList); //初始化头寸信息 SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.initialAssets = initialCapital; myAccount.totalAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //初始化持仓信息 StranglePair holdPair = new StranglePair(); //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); List <double> benchmark = new List <double>(); //可以变动的策略参数 int length = 40; double range = 0.04; //50ETF的日线数据准备,从回测期开始之前100个交易开始取 int number = 100; List <StockDaily> dailyData = new List <StockDaily>(); dailyData = Platforms.container.Resolve <StockDailyRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, DateUtils.PreviousTradeDay(startDate, number), endDate); var closePrice = dailyData.Select(x => x.close).ToArray(); var ETFMA = TA_MA.SMA(closePrice, length); //获取中国波指的数据 List <StockDaily> iVix = Platforms.container.Resolve <StockDailyRepository>().fetchFromLocalCsvOrWindAndSave("000188.SH", startDate, endDate); //按交易日回测 for (int day = 0; day < tradeDays.Count(); day++) { benchmark.Add(closePrice[day + number]); double MAyesterday = ETFMA[day + number - 1]; double lastETFPrice = dailyData[number + day - 1].close; var today = tradeDays[day]; //获取当日上市的期权合约列表 var optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today); //初始化信号的数据结构 Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); //获取今日日内50ETF数据 var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, tradeDays[day]); //初始化行情信息,将50ETF的价格放入dataToday Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(targetVariety, etfData.Cast <KLine>().ToList()); //记录今日账户信息 myAccount.time = today; //获取今日期权的到期日期 var dateStructure = OptionUtilities.getDurationStructure(optionInfoList, tradeDays[day]); //选定到日期在40个交易日至60个交易日的合约 double duration = 0; //for (int i = 0; i < dateStructure.Count(); i++) //{ // if (dateStructure[i] >= 40 && dateStructure[i] <= 80) // { // duration = dateStructure[i]; // break; // } //} duration = dateStructure[1]; //如果没有持仓就开仓 if (holdPair.endDate == new DateTime()) { if (duration == 0) { continue; } //按照开盘1分钟的价格来开平仓。 int index = 0; DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(tradeDays[day]), index); double etfPriceNow = etfData[index].open; if (etfPriceNow > MAyesterday * (1 + range))//看涨,卖出虚值的put { var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽"), etfPriceNow - 0.1, etfPriceNow - 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => - x.strike).ToList(); if (list.Count == 0) { continue; } OptionInfo put = list[0]; if (put.strike != 0 && (put.modifiedDate > today.AddDays(10) || put.modifiedDate < today)) //开仓 { tradeAssistant(ref dataToday, ref signal, put.optionCode, -put.contractMultiplier, today, now, index); holdPair = new StranglePair() { callCode = "", putCode = put.optionCode, callPosition = 0, putPosition = -put.contractMultiplier, endDate = put.endDate, etfPrice = etfPriceNow, callStrike = 0, putStrike = put.strike }; MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } } else if (etfPriceNow < MAyesterday * (1 - range))//看跌,卖出虚值的call { var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow + 0.1, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); if (list.Count == 0) { continue; } OptionInfo call = list[0]; if (call.strike != 0 && (call.modifiedDate > today.AddDays(10) || call.modifiedDate < today)) //开仓 { tradeAssistant(ref dataToday, ref signal, call.optionCode, -call.contractMultiplier, today, now, index); holdPair = new StranglePair() { callCode = call.optionCode, putCode = "", callPosition = -call.contractMultiplier, putPosition = 0, endDate = call.endDate, etfPrice = etfPriceNow, callStrike = call.strike, putStrike = 0 }; MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } } else//不涨不跌,卖出宽跨式期权 { var putList = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽"), etfPriceNow - 0.1, etfPriceNow - 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => - x.strike).ToList(); var callList = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow + 0.1, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList(); if (putList.Count == 0 || callList.Count == 0) { continue; } OptionInfo call = callList[0]; OptionInfo put = putList[0]; if (put.strike != 0 && (put.modifiedDate > today.AddDays(10) || put.modifiedDate < today) && call.strike != 0 && (call.modifiedDate > today.AddDays(10) || call.modifiedDate < today)) //开仓 { tradeAssistant(ref dataToday, ref signal, put.optionCode, -put.contractMultiplier, today, now, index); tradeAssistant(ref dataToday, ref signal, call.optionCode, -call.contractMultiplier, today, now, index); holdPair = new StranglePair() { callCode = call.optionCode, putCode = put.optionCode, callPosition = -call.contractMultiplier, putPosition = -put.contractMultiplier, endDate = put.endDate, etfPrice = etfPriceNow, callStrike = call.strike, putStrike = put.strike }; MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); } } } else //如果有持仓就判断需不需要移仓 { double durationNow = DateUtils.GetSpanOfTradeDays(today, holdPair.endDate); int index = 234; DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(tradeDays[day]), index); if (holdPair.callPosition != 0) { tradeAssistant(ref dataToday, ref signal, holdPair.callCode, -holdPair.callPosition, today, now, index); } if (holdPair.putPosition != 0) { tradeAssistant(ref dataToday, ref signal, holdPair.putCode, -holdPair.putPosition, today, now, index); } if (durationNow <= 10) //强制平仓 { //按照收盘前5分钟的价格来开平仓。 MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index); holdPair = new StranglePair(); } } if (etfData.Count > 0) { //更新当日属性信息 AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, etfData.Last().time, etfData.Count() - 1, dataToday); //记录历史仓位信息 accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets)); benchmark.Add(etfData.Last().close); if (netValue.Count() == 0) { netValue.Add(new NetValue { time = today, netvalueReturn = 0, benchmarkReturn = 0, netvalue = myAccount.totalAssets, benchmark = etfData.Last().close }); } else { var netValueLast = netValue.Last(); netValue.Add(new NetValue { time = today, netvalueReturn = myAccount.totalAssets / netValueLast.netvalue - 1, benchmarkReturn = etfData.Last().close / netValueLast.benchmark - 1, netvalue = myAccount.totalAssets, benchmark = etfData.Last().close }); } } } //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //记录净值数据 RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "ShortOptionByMA", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtil.Transfer(positions); RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "ShortOptionByMA", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "ShortOptionByMA", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //统计指标在console 上输出 Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate); Console.WriteLine("-----------------------------------------------\n"); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("50ETF", netWorthOfBenchmark.ToArray()); // ivix数据 double[] iVixClose = iVix.Select(x => x.close / iVix[0].close).ToArray(); //line.Add("iVix", iVixClose); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); //maoheng 画图 //Application.Run(new PLChart(line, datestr)); //cuixun 画图 //绘制图形的标题 string formTitle = this.startDate.ToShortDateString() + "--" + this.endDate.ToShortDateString() + " " + this.targetVariety + " 净值曲线" + "\r\n" + "\r\n" + "净利润:" + myStgStats.netProfit + " " + "夏普率:" + myStgStats.anualSharpe + " " + "最大回撤:" + myStgStats.maxDrawDown + "\r\n" + "\r\n"; //生成图像 PLChart plc = new PLChart(line, datestr, formTitle: formTitle); //运行图像 //Application.Run(plc); plc.LoadForm(); //保存图像 plc.SaveZed(GetType().FullName, this.targetVariety, this.startDate, this.endDate, myStgStats.netProfit.ToString(), myStgStats.anualSharpe.ToString(), myStgStats.maxDrawDown.ToString()); }
public void record(DateTime startDate, DateTime endDate) { var tradedays = dateRepo.GetStockTransactionDate(startDate, endDate); CreateDBOrTableIfNecessary(startDate); CreateDBOrTableIfNecessary(startDate.AddYears(1)); var start = startDate; while (start < endDate) { if (!ExistInSqlServer(start)) { CreateDBOrTableIfNecessary(start); } start = start.AddYears(1); } if (!ExistInSqlServer(endDate)) { CreateDBOrTableIfNecessary(endDate); } foreach (var date in tradedays) { DataTable dt = new DataTable(); dt = initializeDataTable(dt); double[] sigma1Ask = new double[28802]; double[] sigma1Bid = new double[28802]; double[] sigma2Ask = new double[28802]; double[] sigma2Bid = new double[28802]; double[] vixAsk = new double[28802]; double[] vixBid = new double[28802]; var list = infoRepo.GetStockOptionInfo(underlying, date, date); list = OptionUtilities.modifyOptionListByETFBonus(list, date); List <StockOptionInformation> callListThisMonth = new List <StockOptionInformation>(); List <StockOptionInformation> callListNextMonth = new List <StockOptionInformation>(); List <StockOptionInformation> putListThisMonth = new List <StockOptionInformation>(); List <StockOptionInformation> putListNextMonth = new List <StockOptionInformation>(); var durationList = OptionUtilities.getDurationStructure(list, date); double durationThisMonth = 0; double durationNextMonth = 0; if (durationList[0] > 7) { durationThisMonth = durationList[0]; durationNextMonth = durationList[1]; } else { durationThisMonth = durationList[1]; durationNextMonth = durationList[2]; } foreach (var item in list) { if (OptionUtilities.getDuration(item, date) == durationThisMonth && item.unit == 10000) { if (item.type == "认购") { callListThisMonth.Add(item); } else { putListThisMonth.Add(item); } } else if (OptionUtilities.getDuration(item, date) == durationNextMonth && item.unit == 10000) { if (item.type == "认购") { callListNextMonth.Add(item); } else { putListNextMonth.Add(item); } } } callListThisMonth = callListThisMonth.OrderBy(x => x.strike).ToList(); callListNextMonth = callListNextMonth.OrderBy(x => x.strike).ToList(); putListThisMonth = putListThisMonth.OrderBy(x => x.strike).ToList(); putListNextMonth = putListNextMonth.OrderBy(x => x.strike).ToList(); //获取当日ETF及期权数据 List <StockTickTransaction> etf = new List <StockTickTransaction>(); etf = DataTimeStampExtension.ModifyStockTickData(stockRepo.GetStockTransaction("510050.SH", date, date.AddHours(17))); Dictionary <double, List <StockOptionTickTransaction> > callDataThisMonth = new Dictionary <double, List <StockOptionTickTransaction> >(); Dictionary <double, List <StockOptionTickTransaction> > putDataThisMonth = new Dictionary <double, List <StockOptionTickTransaction> >(); Dictionary <double, List <StockOptionTickTransaction> > callDataNextMonth = new Dictionary <double, List <StockOptionTickTransaction> >(); Dictionary <double, List <StockOptionTickTransaction> > putDataNextMonth = new Dictionary <double, List <StockOptionTickTransaction> >(); List <double> strikeListThisMonth = new List <double>(); List <double> strikeListNextMonth = new List <double>(); foreach (var item in callListThisMonth) { strikeListThisMonth.Add(item.strike); var call = DataTimeStampExtension.ModifyOptionTickData(optionRepo.GetStockTransaction(item.code, date, date.AddHours(17))); callDataThisMonth.Add(item.strike, call); } foreach (var item in putListThisMonth) { var put = DataTimeStampExtension.ModifyOptionTickData(optionRepo.GetStockTransaction(item.code, date, date.AddHours(17))); putDataThisMonth.Add(item.strike, put); } foreach (var item in callListNextMonth) { strikeListNextMonth.Add(item.strike); var call = DataTimeStampExtension.ModifyOptionTickData(optionRepo.GetStockTransaction(item.code, date, date.AddHours(17))); callDataNextMonth.Add(item.strike, call); } foreach (var item in putListNextMonth) { //2016-2-17数据有缺失 var put = DataTimeStampExtension.ModifyOptionTickData(optionRepo.GetStockTransaction(item.code, date, date.AddHours(17))); putDataNextMonth.Add(item.strike, put); } strikeListThisMonth = strikeListThisMonth.OrderBy(x => x).ToList(); strikeListNextMonth = strikeListNextMonth.OrderBy(x => x).ToList(); for (int index = 0; index < 28802; index++) { bool hasData = true; foreach (var item in strikeListThisMonth) { if (callDataThisMonth[item] == null || putDataThisMonth[item] == null || callDataThisMonth[item][index] == null || putDataThisMonth[item][index] == null || (callDataThisMonth[item][index].AskV1 == 0 && callDataThisMonth[item][index].BidV1 == 0) || (putDataThisMonth[item][index].AskV1 == 0 && putDataThisMonth[item][index].BidV1 == 0)) { hasData = false; break; } } //if (durationThisMonth <= 30) { foreach (var item in strikeListNextMonth) { if (callDataNextMonth[item] == null || putDataNextMonth[item] == null || callDataNextMonth[item][index] == null || putDataNextMonth[item][index] == null || callDataNextMonth[item][index].AskV1 == 0 || putDataNextMonth[item][index].AskV1 == 0 || callDataNextMonth[item][index].BidV1 == 0 || putDataNextMonth[item][index].BidV1 == 0) { hasData = false; break; } } } if (hasData == false) { continue; } //初始化记录合约信息的列表 List <iVixInfo> thisMonthInfo = new List <iVixInfo>(); List <iVixInfo> nextMonthInfo = new List <iVixInfo>(); DataRow dr = dt.NewRow(); var now = callDataThisMonth[strikeListThisMonth[0]][index].TransactionDateTime; var expiredate1 = callListThisMonth[0].expireDate; var expiredate2 = callListNextMonth[0].expireDate; var span = date.AddHours(15) - now; //计算时间T NT:近月合约剩余到期时间(以分钟计) T:NT/365 double T1 = (durationThisMonth - 1 + (span.Hours * 60 + span.Minutes) / 840.0) / 365.0; //找到认购期权价格与认沽期权价格相差最小的执行价的K //计算远期价格F S+exp(RT)×[认购期权价格 S −认沽期权价格 S ] double distance1 = 100; double kThisMonth = 0; double F = 0; for (int i = 0; i < strikeListThisMonth.Count(); i++) { double distance0 = Math.Abs((callDataThisMonth[strikeListThisMonth[i]][index].Ask1 + callDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2 - (putDataThisMonth[strikeListThisMonth[i]][index].Ask1 + putDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2); if (distance0 < distance1) { distance1 = distance0; F = strikeListThisMonth[i] + Math.Exp(rate * T1) * ((callDataThisMonth[strikeListThisMonth[i]][index].Ask1 + callDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2 - (putDataThisMonth[strikeListThisMonth[i]][index].Ask1 + putDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2); } } //找到K0 for (int i = 0; i < strikeListThisMonth.Count() - 1; i++) { kThisMonth = strikeListThisMonth[i]; if (strikeListThisMonth[i + 1] > F) { break; } } //计算近月ivix for (int i = 0; i < strikeListThisMonth.Count(); i++) { iVixInfo info = new iVixInfo(); double ask = 0; double bid = 0; double dK = 0; double k = strikeListThisMonth[i]; if (i == strikeListThisMonth.Count() - 1) { dK = strikeListThisMonth[strikeListThisMonth.Count() - 1] - strikeListThisMonth[strikeListThisMonth.Count() - 2]; } else { dK = strikeListThisMonth[i + 1] - strikeListThisMonth[i]; } info.strike = k; info.duration = T1; info.coefficient = 2 / info.duration * dK / Math.Pow(info.strike, 2) * Math.Exp(rate * info.duration); if (strikeListThisMonth[i] < kThisMonth) { ask = putDataThisMonth[strikeListThisMonth[i]][index].Ask1; bid = putDataThisMonth[strikeListThisMonth[i]][index].Bid1; var mid = (ask + bid) / 2; info.sigma = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid, info.strike, info.duration, rate, "认沽"), 4); info.vega = ImpliedVolatilityExtension.ComputeOptionVega(info.strike, info.duration, rate, 0, info.sigma, F * Math.Exp(-rate * info.duration)) / 100.0; info.ask = ask; info.askv = putDataThisMonth[strikeListThisMonth[i]][index].AskV1; info.bid = bid; info.bidv = putDataThisMonth[strikeListThisMonth[i]][index].BidV1; info.minutelyVolume = ComputeMinutelyVolume(putDataThisMonth[strikeListThisMonth[i]], index); } else if (strikeListThisMonth[i] == kThisMonth) { ask = (putDataThisMonth[strikeListThisMonth[i]][index].Ask1 + callDataThisMonth[strikeListThisMonth[i]][index].Ask1) / 2; bid = (putDataThisMonth[strikeListThisMonth[i]][index].Bid1 + callDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2; var mid1 = (putDataThisMonth[strikeListThisMonth[i]][index].Ask1 + putDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2; var mid2 = (callDataThisMonth[strikeListThisMonth[i]][index].Ask1 + callDataThisMonth[strikeListThisMonth[i]][index].Bid1) / 2; var sigma1 = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid1, info.strike, info.duration, rate, "认沽"), 4); var sigma2 = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid2, info.strike, info.duration, rate, "认购"), 4); var vega1 = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, sigma1, F * Math.Exp(-rate * info.duration)) / 100.0; var vega2 = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, sigma2, F * Math.Exp(-rate * info.duration)) / 100.0; info.sigma = (sigma1 + sigma2) / 2; info.vega = (vega1 + vega2) / 2; info.ask = ask; info.askv = Math.Min(putDataThisMonth[strikeListThisMonth[i]][index].AskV1, callDataThisMonth[strikeListThisMonth[i]][index].AskV1) * 2; info.bid = bid; info.bidv = Math.Min(putDataThisMonth[strikeListThisMonth[i]][index].BidV1, callDataThisMonth[strikeListThisMonth[i]][index].BidV1) * 2; var volumeCall = ComputeMinutelyVolume(callDataThisMonth[strikeListThisMonth[i]], index); var volumePut = ComputeMinutelyVolume(putDataThisMonth[strikeListThisMonth[i]], index); info.minutelyVolume = Math.Min(volumeCall, volumePut) * 2; } else { ask = callDataThisMonth[strikeListThisMonth[i]][index].Ask1; bid = callDataThisMonth[strikeListThisMonth[i]][index].Bid1; var mid = (ask + bid) / 2; info.sigma = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid, info.strike, info.duration, rate, "认购"), 4); info.vega = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, info.sigma, F * Math.Exp(-rate * info.duration)) / 100.0; info.ask = ask; info.askv = callDataThisMonth[strikeListThisMonth[i]][index].AskV1; info.bid = bid; info.bidv = callDataThisMonth[strikeListThisMonth[i]][index].BidV1; info.minutelyVolume = ComputeMinutelyVolume(callDataThisMonth[strikeListThisMonth[i]], index); } sigma1Ask[index] += (2 / T1) * dK / (k * k) * Math.Exp(rate * T1) * ask; sigma1Bid[index] += (2 / T1) * dK / (k * k) * Math.Exp(rate * T1) * bid; thisMonthInfo.Add(info); } sigma1Ask[index] += -1 / T1 * Math.Pow((F / kThisMonth) - 1, 2); sigma1Bid[index] += -1 / T1 * Math.Pow((F / kThisMonth) - 1, 2); sigma1Ask[index] = Math.Sqrt(sigma1Ask[index]); sigma1Bid[index] = Math.Sqrt(sigma1Bid[index]); if (durationThisMonth > 30) { vixAsk[index] = sigma1Ask[index]; vixBid[index] = sigma1Bid[index]; } //计算时间T NT:近月合约剩余到期时间(以分钟计) T:NT/365 double T2 = (durationNextMonth - 1 + (span.Minutes) / 840) / 365.0; //找到认购期权价格与认沽期权价格相差最小的执行价的K //计算远期价格F S+exp(RT)×[认购期权价格 S −认沽期权价格 S ] distance1 = 100; double kNextMonth = 0; F = 0; for (int i = 0; i < strikeListNextMonth.Count(); i++) { double distance0 = Math.Abs((callDataNextMonth[strikeListNextMonth[i]][index].Ask1 + callDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2 - (putDataNextMonth[strikeListNextMonth[i]][index].Ask1 + putDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2); if (distance0 < distance1) { distance1 = distance0; F = strikeListNextMonth[i] + Math.Exp(rate * T2) * ((callDataNextMonth[strikeListNextMonth[i]][index].Ask1 + callDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2 - (putDataNextMonth[strikeListNextMonth[i]][index].Ask1 + putDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2); } } //找到K0 for (int i = 0; i < strikeListNextMonth.Count() - 1; i++) { kNextMonth = strikeListNextMonth[i]; if (strikeListNextMonth[i + 1] > F) { break; } } //计算远月ivix for (int i = 0; i < strikeListNextMonth.Count(); i++) { iVixInfo info = new iVixInfo(); double ask = 0; double bid = 0; double dK = 0; double k = strikeListNextMonth[i]; if (i == strikeListNextMonth.Count() - 1) { dK = strikeListNextMonth[strikeListNextMonth.Count() - 1] - strikeListNextMonth[strikeListNextMonth.Count() - 2]; } else { dK = strikeListNextMonth[i + 1] - strikeListNextMonth[i]; } info.strike = k; info.duration = T2; info.coefficient = 2 / info.duration * dK / Math.Pow(info.strike, 2) * Math.Exp(rate * info.duration); if (strikeListNextMonth[i] < kNextMonth) { ask = putDataNextMonth[strikeListNextMonth[i]][index].Ask1; bid = putDataNextMonth[strikeListNextMonth[i]][index].Bid1; var mid = (ask + bid) / 2; info.sigma = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid, info.strike, info.duration, rate, "认沽"), 4); info.vega = ImpliedVolatilityExtension.ComputeOptionVega(info.strike, info.duration, rate, 0, info.sigma, F * Math.Exp(-rate * info.duration)) / 100.0; info.ask = ask; info.askv = putDataNextMonth[strikeListNextMonth[i]][index].AskV1; info.bid = bid; info.bidv = putDataNextMonth[strikeListNextMonth[i]][index].BidV1; info.minutelyVolume = ComputeMinutelyVolume(putDataNextMonth[strikeListNextMonth[i]], index); } else if (strikeListNextMonth[i] == kNextMonth) { ask = (putDataNextMonth[strikeListNextMonth[i]][index].Ask1 + callDataNextMonth[strikeListNextMonth[i]][index].Ask1) / 2; bid = (putDataNextMonth[strikeListNextMonth[i]][index].Bid1 + callDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2; var mid1 = (putDataNextMonth[strikeListNextMonth[i]][index].Ask1 + putDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2; var mid2 = (callDataNextMonth[strikeListNextMonth[i]][index].Ask1 + callDataNextMonth[strikeListNextMonth[i]][index].Bid1) / 2; var sigma1 = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid1, info.strike, info.duration, rate, "认沽"), 4); var sigma2 = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid2, info.strike, info.duration, rate, "认购"), 4); var vega1 = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, sigma1, F * Math.Exp(-rate * info.duration)) / 100.0; var vega2 = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, sigma2, F * Math.Exp(-rate * info.duration)) / 100.0; info.sigma = (sigma1 + sigma2) / 2; info.vega = (vega1 + vega2) / 2; info.ask = ask; info.askv = Math.Min(putDataNextMonth[strikeListNextMonth[i]][index].AskV1, callDataNextMonth[strikeListNextMonth[i]][index].AskV1) * 2; info.bid = bid; info.bidv = Math.Min(putDataNextMonth[strikeListNextMonth[i]][index].BidV1, callDataNextMonth[strikeListNextMonth[i]][index].BidV1) * 2; var volumeCall = ComputeMinutelyVolume(callDataNextMonth[strikeListNextMonth[i]], index); var volumePut = ComputeMinutelyVolume(putDataNextMonth[strikeListNextMonth[i]], index); info.minutelyVolume = Math.Min(volumeCall, volumePut) * 2; } else { ask = callDataNextMonth[strikeListNextMonth[i]][index].Ask1; bid = callDataNextMonth[strikeListNextMonth[i]][index].Bid1; var mid = (ask + bid) / 2; info.sigma = Math.Round(ImpliedVolatilityExtension.sigmaByFuture(F, mid, info.strike, info.duration, rate, "认购"), 4); info.vega = ImpliedVolatilityExtension.ComputeOptionVega(k, info.duration, rate, 0, info.sigma, F * Math.Exp(-rate * info.duration)) / 100.0; info.ask = ask; info.askv = callDataNextMonth[strikeListNextMonth[i]][index].AskV1; info.bid = bid; info.bidv = callDataNextMonth[strikeListNextMonth[i]][index].BidV1; info.minutelyVolume = ComputeMinutelyVolume(callDataNextMonth[strikeListNextMonth[i]], index); } sigma2Ask[index] += (2 / T2) * dK / (k * k) * Math.Exp(rate * T2) * ask; sigma2Bid[index] += (2 / T2) * dK / (k * k) * Math.Exp(rate * T2) * bid; nextMonthInfo.Add(info); } sigma2Ask[index] += -1 / T2 * Math.Pow((F / kNextMonth) - 1, 2); sigma2Bid[index] += -1 / T2 * Math.Pow((F / kNextMonth) - 1, 2); sigma2Ask[index] = Math.Sqrt(sigma2Ask[index]); sigma2Bid[index] = Math.Sqrt(sigma2Bid[index]); if (durationThisMonth <= 30) { vixAsk[index] = Math.Sqrt((T1 * Math.Pow(sigma1Ask[index], 2) * (T2 - 30.0 / 365.0) / (T2 - T1) + T2 * Math.Pow(sigma2Ask[index], 2) * (30.0 / 365.0 - T1) / (T2 - T1)) * 365.0 / 30.0); vixBid[index] = Math.Sqrt((T1 * Math.Pow(sigma1Bid[index], 2) * (T2 - 30.0 / 365.0) / (T2 - T1) + T2 * Math.Pow(sigma2Bid[index], 2) * (30.0 / 365.0 - T1) / (T2 - T1)) * 365.0 / 30.0); foreach (var item in thisMonthInfo) { item.coefficient *= T1 * (T2 - 30.0 / 365.0) / (T2 - T1) * 365.0 / 30.0; } foreach (var item in nextMonthInfo) { item.coefficient *= T2 * (30.0 / 365.0 - T1) / (T2 - T1) * 365.0 / 30.0; } } //计算整体的vega,以及盘口的量 double vegaTotal = 0; double number = 0; double percentAskMax = 0; double percentAskMin = 1; double percentBidMax = 0; double percentBidMin = 1; double percentVolumeMax = 0; double percentVolumeMin = 1; if (durationThisMonth > 30) { foreach (var item in thisMonthInfo) { vegaTotal += item.vega * item.coefficient * 10000; } number = cashVega / vegaTotal; foreach (var item in thisMonthInfo) { double percentAsk = item.askv / number; double percentBid = item.bidv / number; double percentVolume = item.minutelyVolume / number; if (percentAsk > percentAskMax) { percentAskMax = percentAsk; } if (percentAsk < percentAskMin) { percentAskMin = percentAsk; } if (percentBid > percentBidMax) { percentBidMax = percentBid; } if (percentBid < percentBidMin) { percentBidMin = percentBid; } if (percentVolume > percentVolumeMax) { percentVolumeMax = percentVolume; } if (percentVolume < percentVolumeMin) { percentVolumeMin = percentVolume; } } } else { foreach (var item in thisMonthInfo) { vegaTotal += item.vega * item.coefficient * 10000; } foreach (var item in nextMonthInfo) { vegaTotal += item.vega * item.coefficient * 10000; } number = cashVega / 2 / vegaTotal; foreach (var item in thisMonthInfo) { double percentAsk = item.askv / number / item.coefficient; double percentBid = item.bidv / number / item.coefficient; double percentVolume = item.minutelyVolume / number; if (percentAsk > percentAskMax) { percentAskMax = percentAsk; } if (percentAsk < percentAskMin) { percentAskMin = percentAsk; } if (percentBid > percentBidMax) { percentBidMax = percentBid; } if (percentBid < percentBidMin) { percentBidMin = percentBid; } if (percentVolume > percentVolumeMax) { percentVolumeMax = percentVolume; } if (percentVolume < percentVolumeMin) { percentVolumeMin = percentVolume; } } foreach (var item in nextMonthInfo) { double percentAsk = item.askv / number / item.coefficient; double percentBid = item.bidv / number / item.coefficient; double percentVolume = item.minutelyVolume / number; if (percentAsk > percentAskMax) { percentAskMax = percentAsk; } if (percentAsk < percentAskMin) { percentAskMin = percentAsk; } if (percentBid > percentBidMax) { percentBidMax = percentBid; } if (percentBid < percentBidMin) { percentBidMin = percentBid; } if (percentVolume > percentVolumeMax) { percentVolumeMax = percentVolume; } if (percentVolume < percentVolumeMin) { percentVolumeMin = percentVolume; } } } dr["tdatetime"] = now; dr["expiredate1"] = expiredate1; dr["expiredate2"] = expiredate2; dr["duration1"] = Math.Round(T1, 6); dr["duration2"] = Math.Round(T2, 6); dr["sigma1Ask"] = Math.Round(sigma1Ask[index] * 100, 4); dr["sigma1Bid"] = Math.Round(sigma1Bid[index] * 100, 4); dr["sigma2Ask"] = Math.Round(sigma2Ask[index] * 100, 4); dr["sigma2Bid"] = Math.Round(sigma2Bid[index] * 100, 4); dr["sigmaAsk"] = Math.Round(vixAsk[index] * 100, 4); dr["sigmaBid"] = Math.Round(vixBid[index] * 100, 4); dr["vegaTotal"] = Math.Round(vegaTotal, 4); dr["number"] = Math.Round(number, 4); dr["percentAskMax"] = Math.Round(percentAskMax, 4); dr["percentAskMin"] = Math.Round(percentAskMin, 4); dr["percentBidMax"] = Math.Round(percentBidMax, 4); dr["percentBidMin"] = Math.Round(percentBidMin, 4); dr["percentVolumeMax"] = Math.Round(percentVolumeMax, 4); dr["percentVolumeMin"] = Math.Round(percentVolumeMin, 4); if (now < date.Date + new TimeSpan(14, 57, 00)) { dt.Rows.Add(dr); } } SaveResultToMssql(date, dt); } }