/// <summary> /// 从wind或本地CSV获取当天数据 /// </summary> /// <param name="today">今天的日期</param> /// <param name="code">代码</param> /// <returns></returns> private List <FuturesMinute> getData(DateTime today, string code) { List <FuturesMinute> orignalList = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(code, today); List <FuturesMinute> data = KLineDataUtils.leakFilling(orignalList); //从本地csv 或者 wind获取数据,从wind拿到额数据会保存在本地 //List<FuturesMinute> data = KLineDataUtils.leakFilling(Platforms.container.Resolve<FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(code, today)); #region 20170118更新 //下面这行需要注释掉,因为可能data的count为0 var dataModified = FreqTransferUtils.minuteToNMinutes(data, frequency); #endregion return(dataModified); }
/// <summary> /// 计算参数 /// </summary> private void computeParameters() { List <FuturesMinute> data = new List <FuturesMinute>(); #region 20170118更新 //回测开始的首个日期,在tradeDays数组的第一个索引值: //当回测期第一个交易日的前三天没有一天有数据,会引发后续代码出现数组索引为负值的情况。 //bool OutOfRange = false; #endregion //获取首个回测日之前的三日(日)数据 data = getData(DateUtils.PreviousTradeDay(tradeDays[0], 3), underlying); data.AddRange(getData(DateUtils.PreviousTradeDay(tradeDays[0], 2), underlying)); data.AddRange(getData(DateUtils.PreviousTradeDay(tradeDays[0], 1), underlying)); #region 20170118更新 //当无法获取回测日之前的三日(日)数据,也就是data.count=0时,就需要... //if (data.Count == 0) //{ // Console.WriteLine("回测开始日期错误,自动调整到有数据记录的开始日期..."); // OutOfRange = true; //} #endregion //如果回测开始时间早于该品种有数据的时间, //逐日获取K线数据(频率为1分钟) for (int i = 0; i < tradeDays.Count(); i++) { var data0 = getData(tradeDays[i], underlying); data.AddRange(data0); } //var dataModified = FreqTransferUtils.minuteToNMinutes(data, frequency); //按交易日逐日计算,每日遍历所有的参数,结果记入字典结构的变量中 ParaPairs pairs = new ParaPairs(); #region debug数据,请勿删除 //int[] frequencySet = new int[] { 3 }; //int[] numbersSet = new int[] { 5 }; //double[] lossPercentSet = new double[] { 0.015 }; //double[] longERSet = new double[] { 0.7 }; //double[] shortERSet = new double[] {-0.7 }; //int[] frequencySet = new int[] { 3, 4, 5, 6, 7, 8 }; //int[] numbersSet = new int[] { 3, 4, 5, 6, 8, 10, 15 }; //double[] lossPercentSet = new double[] { 0.005, 0.01, 0.015, 0.02 }; //double[] longERSet = new double[] { 0.5, 0.6, 0.7, 0.8, 0.9 }; //double[] shortERSet = new double[] { -0.5, -0.6, -0.7, -0.8, -0.9 }; #endregion int[] frequencySet = new int[] { 3, 4, 5, 6, 7, 8 }; int[] numbersSet = new int[] { 3, 4, 5, 6, 8, 10, 15 }; double[] lossPercentSet = new double[] { 0.005, 0.01, 0.015, 0.02 }; double[] longERSet = new double[] { 0.5, 0.6, 0.7, 0.8, 0.9 }; double[] shortERSet = new double[] { -0.5, -0.6, -0.7, -0.8, -0.9 }; //记录frequency的边际分布 List <double> frequencyDistrbution = new List <double>(); foreach (var fre in frequencySet) { frequency = fre; //给定K线周期 pairs.frequency = frequency; //data是(1分钟)K线数据,dataModified是(frequency周期)K线数据 var dataModified = FreqTransferUtils.minuteToNMinutes(data, frequency); foreach (var num in numbersSet) { numbers = num; //给定前推的(frequency周期)K线数量 pairs.numbers = numbers; foreach (var loss in lossPercentSet) { lossPercent = loss; //给定追踪止损的参数 pairs.lossPercent = lossPercent; foreach (var er in longERSet) { longER = er; //给定ER比例的参数 pairs.longER = longER; foreach (var shortEr in shortERSet) { shortER = shortEr; pairs.shortER = shortER; #region ... double profitInDay = 0; double positionVolume = 0; double openPrice = 0; double maxIncomeIndividual = 0; Console.WriteLine("开始回测参数--> K线:{0}, 回望时间:{1},追踪止损:{2},longER值:{3}, shortER值:{4}", pairs.frequency, pairs.numbers, pairs.lossPercent, pairs.longER, pairs.shortER); //[新版]记录该组参数对应的, 所有交易日的收益 FiveParameterPairs newPairs0 = new FiveParameterPairs { longER = pairs.longER, shortER = pairs.shortER, frequency = pairs.frequency, lossPercent = pairs.lossPercent, numbers = pairs.numbers }; //用来记录同一套策略情况下,不同交易日的盈利情况 SortedDictionary <DateTime, double> sortedDic0 = new SortedDictionary <DateTime, double>(); for (int i = 0; i < dataModified.Count(); i++) //开始按日期遍历 { var now = dataModified[i]; #region 20170118更新 //if (OutOfRange) //{ // if (now.tradeday <DateUtils.NextTradeDay(dataModified[0].tradeday)) // { // continue; // } //} //else //{ //} //在5分钟K线数据表dataModified中,找到首个交易日 tradeDays[0]开始位置对应的index if (now.tradeday < tradeDays[0]) { continue; } #endregion pairs.tradeday = now.tradeday; //当日最后一根K线,进入结算。 if (i == dataModified.Count() - 1 || (i + 1 < dataModified.Count() && dataModified[i + 1].tradeday > now.tradeday)) { //强制平仓 if (positionVolume != 0) { //减去2倍的滑点,是因为买入和卖出均有手续费 profitInDay += positionVolume * (now.open - openPrice) - 2 * slipPoint; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } //记录该组参数当日收益(一个交易日记录一次数据) sortedDic0.Add(pairs.tradeday, profitInDay); //重置数据 profitInDay = 0; positionVolume = 0; maxIncomeIndividual = 0; } else { double[] prices = new double[numbers]; for (int k = i - numbers; k < i; k++) { //导入(前numbersK线)的收盘价 prices[k - (i - numbers)] = dataModified[k].close; } //计算出ER值 double ER = computeER(prices); if (positionVolume == 0) //持空仓 { if (ER > longER && now.open > now.low) //开多仓,且能够开仓 { openPrice = now.open; positionVolume = 1; // Console.WriteLine("时间:{0},价格:{1}, volume:1", dataModified[i].time, now.open); } if (ER < shortER && now.open < now.high) //开空仓 { openPrice = now.open; positionVolume = -1; // Console.WriteLine("时间:{0},价格:{1}, volume:-1", dataModified[i].time, now.open); } } else if (positionVolume == 1) //持多仓 { if ((now.open - openPrice) > maxIncomeIndividual) { maxIncomeIndividual = now.open - openPrice; } //追踪止损,强制平仓 else if (((now.open - openPrice) - maxIncomeIndividual) < -lossPercent * now.open) { profitInDay += now.open - openPrice - 2 * slipPoint; //重置数据 positionVolume = 0; maxIncomeIndividual = 0; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } } else if (positionVolume == -1) //持空仓 { if ((openPrice - now.open) > maxIncomeIndividual) { maxIncomeIndividual = (openPrice - now.open); } else if (((openPrice - now.open) - maxIncomeIndividual) < -lossPercent * now.open) { profitInDay += openPrice - now.open - 2 * slipPoint; positionVolume = 0; maxIncomeIndividual = 0; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } } } } //写入每一套参数,对应的所有交易日的收益情况 newResult.Add(newPairs0, sortedDic0); #endregion } } } } //第一层循环底部.... } }
/// <summary> /// 计算参数 /// </summary> private void computeParameters() { List <FuturesMinute> data = new List <FuturesMinute>(); //获取首个回测日之前的三日(日)数据 data = getData(DateUtils.PreviousTradeDay(tradeDays[0], 3), underlying); data.AddRange(getData(DateUtils.PreviousTradeDay(tradeDays[0], 2), underlying)); data.AddRange(getData(DateUtils.PreviousTradeDay(tradeDays[0], 1), underlying)); //逐日获取K线数据(频率为1分钟) for (int i = 0; i < tradeDays.Count(); i++) { var data0 = getData(tradeDays[i], underlying); data.AddRange(data0); } //var dataModified = FreqTransferUtils.minuteToNMinutes(data, frequency); //按交易日逐日计算,每日遍历所有的参数,结果记入字典结构的变量中 ParameterPairs pairs = new ParameterPairs(); #region debug数据,请勿删除 //int[] frequencySet = new int[] { 5 }; //int[] numbersSet = new int[] { 3 }; //double[] lossPercentSet = new double[] { 0.015 }; //double[] ERRatioSet = new double[] { 0.6 }; //int[] frequencySet = new int[] { 3, 5, 7, 10 }; //int[] numbersSet = new int[] { 3, 4, 5, 6, 8, 10, 15 }; //double[] lossPercentSet = new double[] { 0.000625, 0.00125, 0.0025, 0.005, 0.01, 0.015 }; //double[] ERRatioSet = new double[] { 0.5, 0.6, 0.7, 0.8, 0.9 }; #endregion int[] frequencySet = new int[] { 3, 5, 7, 10 }; int[] numbersSet = new int[] { 3, 4, 5, 6, 8, 10, 15 }; double[] lossPercentSet = new double[] { 0.00125, 0.0025, 0.005, 0.01 }; double[] ERRatioSet = new double[] { 0.5, 0.6, 0.7, 0.8, 0.9 }; //int[] frequencySet = new int[] { 3, 4, 5, 6, 7, 8 }; //int[] numbersSet = new int[] { 3, 4, 5, 6, 8, 10, 15 }; //double[] lossPercentSet = new double[] { 0.0025,0.005, 0.01,0.015,0.02}; //double[] ERRatioSet = new double[] { 0.5, 0.6, 0.7, 0.8, 0.9 }; //记录frequency的边际分布 List <double> frequencyDistrbution = new List <double>(); foreach (var fre in frequencySet) { frequency = fre; //给定K线周期 pairs.frequency = frequency; //data是(1分钟)K线数据,dataModified是(frequency周期)K线数据 var dataModified = FreqTransferUtils.minuteToNMinutes(data, frequency); foreach (var num in numbersSet) { numbers = num; //给定前推的(frequency周期)K线数量 pairs.numbers = numbers; foreach (var loss in lossPercentSet) { lossPercent = loss; //给定追踪止损的参数 pairs.lossPercent = lossPercent; foreach (var er in ERRatioSet) { ERRatio = er; //给定ER比例的参数 pairs.ERRatio = ERRatio; double profitInDay = 0; double positionVolume = 0; double openPrice = 0; double maxIncomeIndividual = 0; Console.WriteLine("开始回测参数,K线:{0},回望时间:{1},追踪止损:{2},ER值:{3}", pairs.frequency, pairs.numbers, pairs.lossPercent, pairs.ERRatio); //[新版]记录该组参数对应的, 所有交易日的收益 FourParameterPairs newPairs0 = new FourParameterPairs { ERRatio = pairs.ERRatio, frequency = pairs.frequency, lossPercent = pairs.lossPercent, numbers = pairs.numbers }; //用来记录同一套策略情况下,不同交易日的盈利情况 SortedDictionary <DateTime, double> sortedDic0 = new SortedDictionary <DateTime, double>(); for (int i = 0; i < dataModified.Count(); i++) //开始按日期遍历 { var now = dataModified[i]; //在5分钟K线数据表dataModified中,找到首个交易日 tradeDays[0]开始位置对应的index if (now.tradeday < tradeDays[0]) { continue; } pairs.tradeday = now.tradeday; //当日最后一根K线,进入结算。 if (i == dataModified.Count() - 1 || (i + 1 < dataModified.Count() && dataModified[i + 1].tradeday > now.tradeday)) { //强制平仓 if (positionVolume != 0) { //减去2倍的滑点,是因为买入和卖出均有手续费 profitInDay += positionVolume * (now.open - openPrice) - 2 * slipPoint; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } //记录该组参数当日收益(一个交易日记录一次数据) sortedDic0.Add(pairs.tradeday, profitInDay); //重置数据 profitInDay = 0; positionVolume = 0; maxIncomeIndividual = 0; } else { double[] prices = new double[numbers]; for (int k = i - numbers; k < i; k++) { //导入(前numbersK线)的收盘价 prices[k - (i - numbers)] = dataModified[k].close; } //计算出ER值 double ER = computeER(prices); if (positionVolume == 0) //持空仓 { if (ER > ERRatio && now.open > now.low) //开多仓,且能够开仓 { openPrice = now.open; positionVolume = 1; // Console.WriteLine("时间:{0},价格:{1}, volume:1", dataModified[i].time, now.open); } if (ER < -ERRatio && now.open < now.high) //开空仓 { openPrice = now.open; positionVolume = -1; // Console.WriteLine("时间:{0},价格:{1}, volume:-1", dataModified[i].time, now.open); } } else if (positionVolume == 1) //持多仓 { if ((now.open - openPrice) > maxIncomeIndividual) { maxIncomeIndividual = now.open - openPrice; } //追踪止损,强制平仓 else if (((now.open - openPrice) - maxIncomeIndividual) < -lossPercent * now.open) { profitInDay += now.open - openPrice - 2 * slipPoint; //重置数据 positionVolume = 0; maxIncomeIndividual = 0; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } } else if (positionVolume == -1) //持空仓 { if ((openPrice - now.open) > maxIncomeIndividual) { maxIncomeIndividual = (openPrice - now.open); } else if (((openPrice - now.open) - maxIncomeIndividual) < -lossPercent * now.open) { profitInDay += openPrice - now.open - 2 * slipPoint; positionVolume = 0; maxIncomeIndividual = 0; // Console.WriteLine("时间:{0},价格:{1}, volume:0", dataModified[i].time, now.open); } } } } //写入每一套参数,对应的所有交易日的收益情况 newResult.Add(newPairs0, sortedDic0); } } } //第一层循环底部.... } }