void OnGUI() { if (GUILayout.Button("<size=50>获取存档</size>")) { m_StringSeting = RecordUtil.Get("setting"); //m_Setting = JsonUtility.FromJson<Setting> (RecordUtil.Get ("setting")); } if (!string.IsNullOrEmpty(m_StringSeting)) { GUILayout.Label(string.Format("<size=50> {0} </size>", m_StringSeting)); } if (!string.IsNullOrEmpty(m_StringSeting)) { SettArray set = JsonUtility.FromJson <SettArray>(m_StringSeting); GUILayout.Label(string.Format("<size=50>成员数量:{0},第一个数据{1}{2},第二个数据{3}{4}/size>", set.setList.Count, set.setList[0].intValue, set.setList[0].stringValue, set.setList[1].intValue, set.setList[1].stringValue)); } if (GUILayout.Button("<size=50>显示第二条设置数据</size>")) { m_Setting2 = RecordUtil.Get("setting2"); } if (!string.IsNullOrEmpty(m_Setting2)) { GUILayout.Label(string.Format("<size=50> {0}</size>", m_Setting2)); } if (GUILayout.Button("<size=50>删除setting存档</size>")) { RecordUtil.Delete("setting"); } }
void OnApplicationPause(bool pauseStatus) { //当游戏即将进入后台时,保存存档 if (pauseStatus) { RecordUtil.Save(); } }
void Start() { Setting setting = new Setting(); setting.stringValue = "测试字符串"; setting.intValue = 10000; RecordUtil.Set("setting", JsonUtility.ToJson(setting)); }
public void showChart(List <BasicAccount> accountHistory, SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions, List <double> benchmark, string underlying, double initialCapital, List <NetValue> netValue, DateTime startDate, DateTime endDate, int frequency, string tag) { //策略绩效统计及输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); //TODO:了解该函数中计算出了那些评价标准 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); string recordName = underlying.Replace(".", "_") + "_DH_" /*+ "numbers_" + numbers.ToString()*/ + "_frequency_" + frequency.ToString(); //记录净值数据 RecordUtil.recordToCsv(accountHistory, tag, "account", parameters: recordName, performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); RecordUtil.recordToCsv(netValue, tag, "netvalue", parameters: recordName, performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录持仓变化 var positionStatus = OptionRecordUtility_50ETF.Transfer(positions); RecordUtil.recordToCsv(positionStatus, tag, "positions", parameters: recordName, performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_")); //记录统计指标 var performanceList = new List <PerformanceStatisics>(); performanceList.Add(myStgStats); RecordUtil.recordToCsv(performanceList, tag, "performance", parameters: recordName, 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(); //绘制图形的标题 string formTitle = startDate.ToShortDateString() + "--" + endDate.ToShortDateString() + " " + underlying + " 净值曲线" + "\r\n" + "\r\n" + "净利润:" + myStgStats.netProfit + " " + "夏普率:" + myStgStats.anualSharpe + " " + "最大回撤:" + myStgStats.maxDrawDown + "\r\n" + "\r\n" + "参数包含: frequency,numbers,lossPercent,K1,K2"; //生成图像 PLChart plc = new PLChart(line, datestr, formTitle: formTitle); //运行图像 if (Caches.DisplayNetWorth == true) { Application.Run(plc); } //保存图像 plc.SaveZed(tag, underlying, startDate, endDate, myStgStats.netProfit.ToString(), myStgStats.anualSharpe.ToString(), myStgStats.maxDrawDown.ToString()); //Application.Run(new PLChart(line, datestr)); }
void OnGUI() { if (GUILayout.Button("<size=50>获取存档</size>")) { m_Setting = JsonUtility.FromJson <Setting> (RecordUtil.Get("setting")); } if (m_Setting != null) { GUILayout.Label(string.Format("<size=50> {0},{1} </size>", m_Setting.intValue, m_Setting.stringValue)); } }
public ActionResult SubmitTime(string categoryUrlName, string gameTime, string escapeGameTime, string realTime, string videoLink, string comment, string username) { var category = CategoryService.GetCategoryByUrlName(categoryUrlName); if (category == null) { return(new HttpNotFoundResult()); } if ((category.GameTime && string.IsNullOrWhiteSpace(gameTime)) || (category.EscapeGameTime && string.IsNullOrWhiteSpace(escapeGameTime)) || (category.RealTime && string.IsNullOrWhiteSpace(realTime))) { return(GetSubmitTimeView(category, "Please fill out the time fields")); } var isModeratorAction = false; if (IsModerator && !string.IsNullOrWhiteSpace(username)) { // Allow moderators to submit for any username username = username.Trim(); isModeratorAction = true; _logger.Info($"Moderator submitting record for another user: [{username}]"); } else { username = Username; } var record = RecordUtil.CreateRecord(category, username, gameTime, escapeGameTime, realTime, videoLink, comment, UserId); if (record == null) { return(GetSubmitTimeView(category, "Invalid time")); } _leaderboardService.AddRecord(UserContext, record, isModeratorAction); _logger.Debug($"Record submitted: [{categoryUrlName}], [{gameTime}], [{escapeGameTime}], [{realTime}], [{videoLink}], [{comment}]"); var viewModel = CreateViewModel <PageViewModel>(); return(View("SubmitSuccess", viewModel)); }
public ActionResult SubmitRecord(string username, string category, string realTime, string gameTime, string escapeGameTime, string videoUrl, string comment) { if (string.IsNullOrWhiteSpace(username)) { throw new ApiException(HttpStatusCode.BadRequest, "Username required"); } if (string.IsNullOrWhiteSpace(category)) { throw new ApiException(HttpStatusCode.BadRequest, "Category required"); } var categoryObj = CategoryService.GetCategoryByUrlName(category); if (categoryObj == null) { throw new ApiException(HttpStatusCode.BadRequest, "Invalid category"); } if ((categoryObj.GameTime && string.IsNullOrWhiteSpace(gameTime)) || (categoryObj.EscapeGameTime && string.IsNullOrWhiteSpace(escapeGameTime)) || (categoryObj.RealTime && string.IsNullOrWhiteSpace(realTime))) { throw new ApiException(HttpStatusCode.BadRequest, "Time required"); } var record = RecordUtil.CreateRecord(categoryObj, username, gameTime, escapeGameTime, realTime, videoUrl, comment, UserId); if (record == null) { throw new ApiException(HttpStatusCode.BadRequest, "Invalid time"); } _leaderboardService.AddRecord(UserContext, record, true); _logger.Debug($"Record submitted: [{category}], [{gameTime}], [{escapeGameTime}], [{realTime}], [{videoUrl}], [{comment}]"); var mappedRecord = MapRecord(record); return(Json(mappedRecord)); }
void Start() { Setting setting = new Setting(); setting.stringValue = "测试字符串"; setting.intValue = 10000; Setting setting1 = new Setting(); setting1.stringValue = "增加测试字符串"; setting1.intValue = 20000; SettArray array = new SettArray(); array.setList.Add(setting); array.setList.Add(setting1); RecordUtil.Set("setting", JsonUtility.ToJson(array)); string setting2 = "添加第二条数据了。。。。"; RecordUtil.Set("setting2", setting2); }
/// <summary> /// 选择最优参数对。用前choicePeriod个交易日的数据计算出最优参数对,作为之后serviceLife个交易日的交易参数 /// </summary> /// <param name="result"></param> /// <param name="startDate"></param> /// <param name="endDate"></param> /// <param name="choicePeriod"></param> /// <param name="serviceLife"></param> /// <returns></returns> private Dictionary <DateTime, FiveParameterPairs> chooseParameters(Dictionary <FiveParameterPairs, SortedDictionary <DateTime, double> > result, DateTime startDate, DateTime endDate, int choicePeriod, int serviceLife) { DateTime start = startDate; DateTime end = DateUtils.NextTradeDay(start, choicePeriod - 1); DateTime paraStart = DateUtils.NextTradeDay(start, choicePeriod); DateTime paraEnd = DateUtils.NextTradeDay(start, choicePeriod + serviceLife - 1); //如果choicePeriod的数值,超过了回测周期的长度。做一些特殊处理。 if (end > endDate) { end = endDate; paraStart = start; paraEnd = end; } ///最高分。因为参数对计算得分可能会出现负数,所以这里初始默认值不能为0。 ///否则会出现循环所有参数对结束之后,最优参数对bestPara依然没有复制的情况。 double marks = -1000000; //用来记录(在choicePeriod时间段中)得分最高的参数对 FiveParameterPairs bestPara = new FiveParameterPairs(); //记录每个交易日的最佳参数对(Dictionary类型) Dictionary <DateTime, FiveParameterPairs> paras = new Dictionary <DateTime, FiveParameterPairs>(); //记录每个交易日使用的参数对(List类型),用于输出到CSV文件 List <ParaPairsWithScore> parasWithScore = new List <ParaPairsWithScore>(); #region debug专用 //debug专用:存贮所有参数组合在规定start, end期间的得分... Dictionary <FiveParameterPairs, double> scoreWithParameterPair = new Dictionary <FiveParameterPairs, double>(); #endregion while (end <= endDate) { //循环所有的参数组合,选出(在choicePeriod时间段中)得分最高的参数对 foreach (var item in result) { double mark0 = getMarks(result, item.Key, start, end); #region debug专用 //debug专用:存贮debug信息... if (mark0 > 0.1) { scoreWithParameterPair.Add(item.Key, mark0); } #endregion if (mark0 > marks) { marks = mark0; bestPara = item.Key; } } if (paras.Count() == 0) { List <DateTime> dates0 = DateUtils.GetTradeDays(start, end); foreach (var item in dates0) { paras.Add(item, bestPara); parasWithScore.Add(new ParaPairsWithScore { tradeday = item, frequency = bestPara.frequency, numbers = bestPara.numbers, lossPercent = bestPara.lossPercent, longER = bestPara.longER, shortER = bestPara.shortER, Score = marks }); } } //将选出的(choicePeriod时间段中)最优参数对,作为最优解保存到( choicePeriod + serviceLife时间段) List <DateTime> dates = DateUtils.GetTradeDays(paraStart, paraEnd); foreach (var item in dates) { paras.Add(item, bestPara); parasWithScore.Add(new ParaPairsWithScore { tradeday = item, frequency = bestPara.frequency, numbers = bestPara.numbers, lossPercent = bestPara.lossPercent, longER = bestPara.longER, shortER = bestPara.shortER, Score = marks }); } //计算日期往后顺延serviceLife时间段,重置相关数据 start = DateUtils.NextTradeDay(start, serviceLife); end = DateUtils.NextTradeDay(end, serviceLife); paraStart = DateUtils.NextTradeDay(paraStart, serviceLife); paraEnd = DateUtils.NextTradeDay(paraEnd, serviceLife); marks = -1000000; bestPara = new FiveParameterPairs(); if (end >= endDate) { break; } #region debug专用 scoreWithParameterPair = new Dictionary <FiveParameterPairs, double>(); #endregion } //每个交易日使用的策略,写入CSV文件 //List<ParameterPairs> tradeDaysWithBestParas = convertType(paras); string recordName = underlying.Replace(".", "_") + "_" + startDate.ToShortDateString().Replace("/", "_") + "_to_" + endDate.ToShortDateString().Replace("/", "_"); RecordUtil.recordToCsv(data: parasWithScore, type: "tradeDaysWithBestParas", tag: GetType().FullName, parameters: recordName, performance: ""); return(paras); }
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)); }
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()); }
/// <summary> /// Creates a shallow copy using <see cref="object.MemberwiseClone"/>. /// </summary> /// <returns>a new instance of a <typeparamref name="T"/> record.</returns> T IRecord <T> .ShallowCopy() { return(RecordUtil.MemberwiseClone(RecordType)); }
/// <summary> /// 50ETF择时策略测试,N-Days Reversion /// </summary> public void compute() { log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate)); //将来可以把这些初始化操作从程序中分离,写在外面 ///账户初始化 //初始化positions SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.totalAssets = initialCapital; myAccount.initialAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //将账户当前时间定为下一天,因为交易总是在下一天开始 //int nextDay = tradeDays.FindIndex(date => date == startDate) + 1; myAccount.time = startDate; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); ///数据准备 //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); //分钟数据准备,取全回测期的数据存放于data Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >(); foreach (var tempDay in tradeDays) { var stockData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(stockCode, tempDay); if (!data.ContainsKey(stockCode)) { data.Add(stockCode, stockData.Cast <KLine>().ToList()); } else { data[stockCode].AddRange(stockData.Cast <KLine>().ToList()); } } //交易开关设置,控制day级的交易开关,tradingOn还没有使用 bool tradingOn = true; //总交易开关 bool openingOn = true; //开仓开关 bool closingOn = false; //平仓开关 //定义交易信号数组 Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); ///回测循环 //回测循环--By Day foreach (var day in tradeDays) { //取出当天的数据,列表类型,包含high,low,amt等数据 //取出当天的数据 Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); foreach (var variety in data) { dataToday.Add(variety.Key, data[variety.Key].FindAll(s => s.time.Year == day.Year && s.time.Month == day.Month && s.time.Day == day.Day)); } //是否为回测最后一天 bool isLastDayOfBackTesting = day.Equals(endDate); //先测试15K数据,直接将策略写在程序中,将来可以尝试分离过程,将部分策略以函数或者类的形式写在外面 //现将1分钟数据调整为15分钟数据,15K数据占用16个数组空间 List <KLine> data15K = Get15KData(dataToday[stockCode]); //交易信号判断,用信号来判断开仓还是平仓,但是在交易单元,还要靠平均价和当前价,进行止损平仓 List <int> tradeSignal = ClimbMountain(data15K); //回测循环 -- By Minute //不允许在同一根1minBar上开平仓 int index = 0; while (index < 16) { int nextIndex = index + 1; if (nextIndex == 16) { break; } DateTime now = data15K[index].time; DateTime next = data15K[nextIndex].time; if ((tradeSignal[index] == 0) && openingOn) { //设置signal信号,设置时间等参数 MinuteSignal openSignal = new MinuteSignal() { code = stockCode, //开仓只开90%,下一阶段可以分批加仓,全部减仓 volume = myAccount.freeCash / data15K[nextIndex].open * 0.9, time = data15K[nextIndex].time, tradingVarieties = "stock", price = data15K[nextIndex].open, minuteIndex = nextIndex }; openingOn = false; closingOn = true; volumeNow = myAccount.freeCash / data15K[nextIndex].open * 0.9; signal.Add(stockCode, openSignal); //开仓下单 MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: data15K[nextIndex].time, capitalVerification: false); signal.Clear(); } else if (((tradeSignal[index] == 1) || (positions[now][stockCode].ShortPosition.averagePrice < data15K[index].close)) && (volumeNow > 0) && closingOn) { //设置signal信号,设置时间等参数 MinuteSignal closeSignal = new MinuteSignal() { code = stockCode, volume = 0 - volumeNow, time = data15K[nextIndex].time, tradingVarieties = "stock", price = data15K[nextIndex].open, minuteIndex = 0 }; openingOn = true; closingOn = false; volumeNow = 0; signal.Add(stockCode, closeSignal); //平仓下单 MinuteTransactionWithSlip.computeMinuteClosePositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: data15K[nextIndex].time); signal.Clear(); } 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); //账户信息更新 AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, myAccount.time, dataToday); } //策略绩效统计及输出 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(); //初始化净值曲线类 PLChart plc = new PLChart(line, datestr); Application.Run(plc); plc.SaveZed("D:\\BTP\\Result\\BackTestingPlatform.Strategies.Stock.StockSample.MABreak\\aa.png"); }
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() { 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(); 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 int compute() { //如果MA周期不对,直接返回 if (MA1 < 0 || MA2 < 0) { log.Info("MA周期出错,MA1:{0}, MA2:{1}", MA1, MA2); return(-1); } log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate)); //将来可以把这些初始化操作从程序中分离,写在外面 //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); ///账户初始化 //初始化positions SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >(); //初始化Account信息 BasicAccount myAccount = new BasicAccount(); myAccount.totalAssets = initialCapital; myAccount.initialAssets = initialCapital; myAccount.freeCash = myAccount.totalAssets; //将账户当前时间定为下一天,因为交易总是在下一天开始 //int nextDay = tradeDays.FindIndex(date => date == startDate) + 1; myAccount.time = startDate; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); //benchmark数据记录 List <double> benchmark = new List <double>(); ///数据准备 //日线数据准备,取全回测期的数据存放于data List <StockDaily> stockData = new List <StockDaily>(); stockData = Platforms.container.Resolve <StockDailyRepository>().fetchFromLocalCsvOrWindAndSave(stockCode, startDate, endDate); //建立close price数组,从stockData里面取出close price int stockData_length = stockData.Count; double[] closePrice = new double[stockData_length]; for (int count = 0; count < stockData_length; ++count) { closePrice[count] = stockData[count].close; } //取两个MA的数组 double[] MA1_array = MA.compute(closePrice, MA1); double[] MA2_array = MA.compute(closePrice, MA2); //****数据准备完毕回测开始****** log.Info("数据准备完毕回测开始"); //交易开关设置,控制day级的交易开关,开始时只能开仓,不能平仓 bool tradingOn = true; //总交易开关 bool openingOn = true; //开仓开关 bool closingOn = false; //平仓开关 //定义交易信号数组 Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>(); //获得交易信号 for (int count = MA1; count < stockData_length; ++count) { //获取当前时间,供回测信号使用 DateTime timeNow = stockData[count].time; //找出下一个交易日 int nextTradeDay = tradeDays.FindIndex(date => date == timeNow) + 1; if (nextTradeDay == stockData_length) { break; } //分钟数据准备,做交易执行使用 var minuteData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(stockCode, tradeDays[nextTradeDay]); Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >(); dataToday.Add(stockCode, minuteData.Cast <KLine>().ToList()); //上穿买入信号 if ((MA1_array[count] > MA2_array[count]) && openingOn && tradingOn) { //设置signal信号,设置时间等参数 MinuteSignal openSignal = new MinuteSignal() { code = stockCode, volume = myAccount.freeCash / MA1_array[count] * 0.9, time = minuteData[0].time, tradingVarieties = "stock", price = MA1_array[count], minuteIndex = 0 }; openingOn = false; closingOn = true; volumeNow = myAccount.freeCash / MA1_array[count] * 0.9; signal.Add(stockCode, openSignal); //开仓下单 MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: minuteData[0].time, capitalVerification: false); signal.Clear(); } ////下穿卖出信号,当存量volumeNow大于0时做卖出操作 if ((MA1_array[count] < MA2_array[count]) && closingOn && tradingOn && (volumeNow > 0)) { //设置signal信号,设置时间等参数 MinuteSignal closeSignal = new MinuteSignal() { code = stockCode, volume = 0 - volumeNow, time = minuteData[0].time, tradingVarieties = "stock", price = MA1_array[count], minuteIndex = 0 }; openingOn = true; closingOn = false; volumeNow = 0; signal.Add(stockCode, closeSignal); //平仓下单 MinuteTransactionWithSlip.computeMinuteClosePositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: minuteData[0].time); signal.Clear(); } //将交易记录记录到历史 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); //账户信息更新 AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, minuteData[0].time, dataToday); } //策略绩效统计及输出 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(); //初始化净值曲线类 PLChart plc = new PLChart(line, datestr); Application.Run(plc); plc.SaveZed("D:\\BTP\\Result\\BackTestingPlatform.Strategies.Stock.StockSample.MABreak\\aa.png"); return(0); }