public void compute() { ///数据准备 //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); //50etf分钟数据准备,取全回测期的数据存放于data Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >(); foreach (var tempDay in tradeDays) { var ETFData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, tempDay); if (!data.ContainsKey(targetVariety)) { data.Add(targetVariety, ETFData.Cast <KLine>().ToList()); } else { data[targetVariety].AddRange(ETFData.Cast <KLine>().ToList()); } } ///指标计算 var closePrice = data[targetVariety].Select(x => x.close).ToArray(); /* * Stopwatch stopwatch = new Stopwatch(); * stopwatch.Start(); // 开始监视代码运行时间 * //----------------------------------------- * TicTacTec.TA.Library.Core.RetCode retCode = new TicTacTec.TA.Library.Core.RetCode(); * retCode = TicTacTec.TA.Library.Core.RetCode.InternalError; * * int outBegIdx = -1; * int outNbElement = -1; * int lookback = -1; * double[] output = new double[closePrice.Length]; * lookback = TicTacTec.TA.Library.Core.MovingAverageLookback(periods, TicTacTec.TA.Library.Core.MAType.Sma); * retCode = TicTacTec.TA.Library.Core.MovingAverage(0, closePrice.Length - 1, closePrice, lookback + 1, TicTacTec.TA.Library.Core.MAType.Sma, out outBegIdx, out outNbElement, output); * //----------------------------------------- * stopwatch.Stop(); // 停止监视 * TimeSpan timespan = stopwatch.Elapsed; // 获取当前实例测量得出的总时间 * Console.WriteLine("Calc MA Running Time: {0}", timespan.TotalSeconds); */ Stopwatch stopwatch2 = new Stopwatch(); stopwatch2.Start(); // 开始监视代码运行时间 var MAValue = TA_MA.SMA(closePrice, periods); stopwatch2.Stop(); // 停止监视 TimeSpan timespan2 = stopwatch2.Elapsed; // 获取当前实例测量得出的总时间 Console.WriteLine("Calc MA2 Running Time: {0}", timespan2.TotalSeconds); Console.ReadKey(); }
public void compute() { ///数据准备 //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); //50etf分钟数据准备,取全回测期的数据存放于data Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >(); foreach (var tempDay in tradeDays) { var ETFData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave(targetVariety, tempDay); if (!data.ContainsKey(targetVariety)) { data.Add(targetVariety, ETFData.Cast <KLine>().ToList()); } else { data[targetVariety].AddRange(ETFData.Cast <KLine>().ToList()); } } var tick = Platforms.container.Resolve <StockTickRepository>().fetchFromMssql(targetVariety, Kit.ToDate(20160905)); List <StockTickFromMssql> tick2 = SequentialUtils.ResampleAndAlign(tick, Constants.timeline500ms, Kit.ToDate(20160905)); ///指标计算 var closePrice = tick2.Select(x => x.bid[1].price).ToArray(); Stopwatch stopwatch2 = new Stopwatch(); stopwatch2.Start(); // 开始监视代码运行时间 var MAValue = TA_MA.SMA(closePrice, periods); stopwatch2.Stop(); // 停止监视 TimeSpan timespan2 = stopwatch2.Elapsed; // 获取当前实例测量得出的总时间 Console.WriteLine("Calc MA2 Running Time: {0}", timespan2.TotalSeconds); Console.ReadKey(); }
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> /// 50ETF,Tick级双均线策略 /// </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.freeCash = myAccount.totalAssets; //记录历史账户信息 List <BasicAccount> accountHistory = new List <BasicAccount>(); ///数据准备 //交易日信息 List <DateTime> tradeDays = DateUtils.GetTradeDays(startDate, endDate); Dictionary <string, List <TickFromMssql> > data = new Dictionary <string, List <TickFromMssql> >(); foreach (var tempDay in tradeDays) { var tick = Platforms.container.Resolve <FuturesTickRepository>().fetchFromMssql(targetVariety, tempDay); List <FuturesTickFromMssql> tick2 = SequentialUtils.ResampleAndAlign(tick, Constants.timeline500ms, tempDay); if (!data.ContainsKey(targetVariety)) { data.Add(targetVariety, tick2.Cast <TickFromMssql>().ToList()); } else { data[targetVariety].AddRange(tick2.Cast <TickFromMssql>().ToList()); } } //计算需要指标 //(1)回测期长均线 //(2)回测期短均线 List <double> longMA = new List <double>(); List <double> shortMA = new List <double>(); List <double> benchmark = new List <double>(); var lastPrice = data[targetVariety].Select(x => x.lastPrice).ToArray(); longMA = TA_MA.SMA(lastPrice, longLength).ToList(); shortMA = TA_MA.SMA(lastPrice, shortLength).ToList(); // double[] dif = new double[lastPrice.Length]; // double[] dea = new double[lastPrice.Length]; // double[] macdHist = new double[lastPrice.Length]; // TA_MACD.compute(lastPrice, new int[] { 26, 12, 9 }, out dif, out dea, out macdHist); int indexOfNow = -1;//记录整个data的索引 /**/ ///回测循环 //回测循环--By Day foreach (var day in tradeDays) { //取出当天的数据 Dictionary <string, List <TickFromMssql> > dataToday = new Dictionary <string, List <TickFromMssql> >(); 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)); } int dayLength = dataToday[targetVariety].Count; int index = 0; //交易开关设置,控制day级的交易开关 bool tradingOn = true; //总交易开关 bool openingOn = true; //开仓开关 bool closingOn = true; //平仓开关 //是否为回测最后一天 bool isLastDayOfBackTesting = day.Equals(endDate); //回测循环 -- By Tick while (index < dayLength) { int nextIndex = index + 1; indexOfNow++; DateTime now = TimeListUtility.IndexToTickDateTime(Kit.ToInt_yyyyMMdd(day), index); Dictionary <string, TickSignal> signal = new Dictionary <string, TickSignal>(); DateTime next = new DateTime(); // int indexOfNow = data[targetVariety].FindIndex(s => s.time == now); double nowPrice = dataToday[targetVariety][index].lastPrice; myAccount.time = now; //实际操作从第一个回望期后开始 if (indexOfNow < longLength - 1) { index = nextIndex; continue; } try { //持仓查询,先平后开. //若当前有持仓 且 允许平仓 //是否是空仓,若position中所有品种volum都为0,则说明是空仓 bool isEmptyPosition = positions.Count != 0 ? positions[positions.Keys.Last()].Values.Sum(x => Math.Abs(x.volume)) == 0 : true; //若当前有持仓且允许交易 if (!isEmptyPosition && closingOn) { ///平仓条件 /// (1)若当前为 回测结束日 或 tradingOn 为false,平仓 /// (2)若短均线下穿长均线,平多 //(1)若当前为 回测结束日 或 tradingOn 为false,平仓 if (isLastDayOfBackTesting || tradingOn == false) { next = TickCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now: now, slipPoint: slipPoint); break; } //(2)若短均线下穿长均线,平多 else if (Cross.crossDown(shortMA, longMA, indexOfNow)) { next = TickCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now: now, slipPoint: slipPoint); } } //空仓 且可交易 可开仓 else if (isEmptyPosition && tradingOn && openingOn) { ///开仓条件 /// 可用资金足够,且短均线上传长均线 double nowFreeCash = myAccount.freeCash; //开仓量,满仓梭哈 double openVolume = Math.Truncate(nowFreeCash / data[targetVariety][indexOfNow].lastPrice / contractTimes) * contractTimes; //若剩余资金至少购买一手 且 出上反转信号 开仓 if (openVolume >= 1 && Cross.crossUp(shortMA, longMA, indexOfNow)) { TickSignal openSignal = new TickSignal() { code = targetVariety, volume = openVolume, time = now, tradingVarieties = "stock", price = dataToday[targetVariety][index].lastPrice, tickIndex = index }; signal.Add(targetVariety, openSignal); next = TickTransactionWithSlip.computeTickOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now); //当天买入不可卖出 closingOn = false; } } //账户信息更新 AccountUpdatingForTick.computeAccountUpdating(ref myAccount, positions, now, dataToday); } catch (Exception) { throw; } nextIndex = Math.Max(nextIndex, TimeListUtility.TickToIndex(next)); 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); //抓取benchmark benchmark.Add(dataToday[targetVariety].Last().lastPrice); //显示当前信息 Console.WriteLine("Time:{0,-8:F},netWorth:{1,-8:F3}", day, myAccount.totalAssets / initialCapital); } /* * //遍历输出到console * foreach (var account in accountHistory) * Console.WriteLine("time:{0,-8:F}, 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 上输出 PerformanceStatisics myStgStats = new PerformanceStatisics(); myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions, benchmark.ToArray()); Console.WriteLine("--------Strategy Performance Statistics--------\n"); Console.WriteLine(" netProfit:{0,-3:F} \n totalReturn:{1,-3:F} \n anualReturn:{2,-3:F} \n anualSharpe :{3,-3:F} \n winningRate:{4,-3:F} \n PnLRatio:{5,-3:F} \n maxDrawDown:{6,-3:F} \n maxProfitRatio:{7,-3:F} \n informationRatio:{8,-3:F} \n alpha:{9,-3:F} \n beta:{10,-3:F} \n averageHoldingRate:{11,-3:F} \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"); //画图 Dictionary <string, double[]> line = new Dictionary <string, double[]>(); double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray(); line.Add("NetWorth", netWorth); //benchmark净值 List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList(); line.Add("50ETF", netWorthOfBenchmark.ToArray()); string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray(); Application.Run(new PLChart(line, datestr)); Console.ReadKey(); }