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>();
            //持仓量
            double positionVolume = 0;
            //最大收入值
            double maxIncome = 0;

            //第一层循环:所有交易日循环一遍...
            for (int i = 0; i < tradeDays.Count(); i++)
            {
                DateTime today = tradeDays[i];
                //找到当日参数
                FourParameterPairs pair = parameters[today];
                frequency   = pair.frequency;
                numbers     = pair.numbers;
                ERRatio     = pair.ERRatio;
                lossPercent = pair.lossPercent;
                //从wind或本地CSV获取相应交易日的数据list,并转换成FuturesMinute分钟线频率
                var dataOnlyToday = getData(today, underlying);                                //一个交易日里有多条分钟线数据
                var data          = getData(DateUtils.PreviousTradeDay(today, 3), underlying); //前3交易日的分钟线频率数据list
                data.AddRange(getData(DateUtils.PreviousTradeDay(today, 2), underlying));
                data.AddRange(getData(DateUtils.PreviousTradeDay(today, 1), underlying));
                int indexStart = data.Count();
                if (indexStart == 0) //前一天没数据
                {
                    indexStart = numbers;
                }
                data.AddRange(dataOnlyToday);//将当天的数据add到前一天的数据之后
                //将获取的数据,储存为KLine格式
                Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >();
                dataToday.Add(underlying, data.Cast <KLine>().ToList());

                //第二层循环:只循环某当天的数据(开始的索引值为前一天数据的List.count)
                #region 第二层循环

                //这里减1:最后一个周期只平仓,不开仓
                for (int j = indexStart; j < data.Count() - 1; j++)
                {
                    DateTime now    = data[j].time;
                    double[] prices = new double[numbers];
                    for (int k = j - numbers; k < j; k++)
                    {
                        //导入收盘价
                        prices[k - (j - numbers)] = data[k].close;
                    }
                    //计算出ER值
                    double ER = computeER(prices);

                    # region 追踪止损判断 触发止损平仓

                    //追踪止损判断 触发止损平仓
                    if (positionVolume != 0) //头寸量不为0,额外要做的操作
                    {
                        //计算开盘价和头寸当前价的差价
                        double incomeNow = individualIncome(positions.Last().Value[underlying], data[j].open);
                        //若当前收入大于最大收入值,则更新最大收入值
                        if (incomeNow > maxIncome)
                        {
                            maxIncome = incomeNow;
                        }
                        //若盈利回吐大于5个点 或者 最大收入大于45,则进行平仓
                        //&& ((positionVolume>0 && ER<longLevel) || (positionVolume<0 && ER>shortLevel))

                        else if ((maxIncome - incomeNow) > lossPercent * Math.Abs(data[j].open) || incomeNow < -lossPercent * Math.Abs(data[j].open)) //从最高点跌下来3%,就止损

                        {
                            positionVolume = 0;
                            Console.WriteLine("追踪止损!平仓价格: {0}", data[j].open);
                            MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                            maxIncome = 0;
                        }
                        //if (positionVolume>0 && ER<0 && incomeNow<-10)
                        //{
                        //    positionVolume = 0;
                        //    Console.WriteLine("信号止损!平仓价格: {0}", data[j].open);
                        //    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        //    maxIncome = 0;
                        //}
                        //else if (positionVolume < 0 && ER >0 && incomeNow < -10)
                        //{
                        //    positionVolume = 0;
                        //    Console.WriteLine("信号止损!平仓价格: {0}", data[j].open);
                        //    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        //    maxIncome = 0;
                        //}
                    }

                    #endregion
                    if (ER >= ERRatio && positionVolume == 0) //多头信号,无头寸,则开多仓

                    {
                        double volume = 1;
                        //长头寸信号
                        MinuteSignal longSignal = new MinuteSignal()
                        {
                            code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = data[j].open, minuteIndex = j
                        };
                        //signal保存长头寸longSignal信号
                        Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                        signal.Add(underlying, longSignal);
                        MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: longSignal.minuteIndex);
                        Console.WriteLine("做多期货!多头开仓价格: {0}", data[j].open);
                        //头寸量叠加
                        positionVolume += volume;
                        //单笔最大收益重置
                        maxIncome = 0;
                    }
                    else if (ER <= -ERRatio && positionVolume == 0) //空头信号,无头寸,则开空仓
                    {
                        double volume = -1;
                        maxIncome = 0;
                        MinuteSignal shortSignal = new MinuteSignal()
                        {
                            code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = data[j].open, minuteIndex = j
                        };
                        Console.WriteLine("做空期货!空头开仓价格: {0}", data[j].open);
                        Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                        signal.Add(underlying, shortSignal);
                        positionVolume += volume;
                        //分钟级交易
                        MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: shortSignal.minuteIndex);
                    }
                }

                #endregion

                int closeIndex = data.Count() - 1;

                if (positionVolume != 0)
                {
                    positionVolume = 0;
                    maxIncome      = 0;
                    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, data[closeIndex].time, closeIndex, slipPoint);
                    Console.WriteLine("{2}   每日收盘前强制平仓,平仓价格:{0},账户价值:{1}", data[closeIndex].open, myAccount.totalAssets, today);
                }
                if (data.Count > 0)
                {
                    //更新当日属性信息

                    AccountOperator.Minute.maoheng.AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, data.Last().time, data.Count() - 1, dataToday);

                    //记录历史仓位信息
                    accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                    benchmark.Add(data.Last().close);
                    if (netValue.Count() == 0)
                    {
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = 0, benchmarkReturn = 0, netvalue = myAccount.totalAssets, benchmark = data.Last().close
                        });
                    }
                    else
                    {
                        var netValueLast = netValue.Last();
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = myAccount.totalAssets / netValueLast.netvalue - 1, benchmarkReturn = data.Last().close / netValueLast.benchmark - 1, netvalue = myAccount.totalAssets, benchmark = data.Last().close
                        });
                    }
                }
            }
Exemplo n.º 2
0
        /// <summary>
        /// 获得所有参数对的评分
        /// </summary>
        /// <param name="result"></param>
        /// <param name="pair"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public double getMarks(Dictionary <FourParameterPairs, SortedDictionary <DateTime, double> > result, FourParameterPairs pair, DateTime startDate, DateTime endDate)
        {
            List <double> netvalue    = new List <double>(); //净值
            List <double> returnRatio = new List <double>(); //收益率

            netvalue.Add(1);                                 //【问题】netvalue这里为什么要先add一个1,而returnRatio没有?
            double total = initialCapital;

            foreach (var item in result)
            {
                if (item.Key.ERRatio == pair.ERRatio && item.Key.frequency == pair.frequency && item.Key.lossPercent == pair.lossPercent && item.Key.numbers == pair.numbers)
                {
                    foreach (var num in item.Value)
                    {
                        if (num.Key >= startDate && num.Key <= endDate)
                        {
                            total += num.Value;
                            netvalue.Add(total / initialCapital);
                            returnRatio.Add(num.Value / initialCapital);

                            #region debug专用
                            //Console.WriteLine("chiocePeriod数据-->时间:{0},利润:{1}",num.Key,num.Value);
                            #endregion
                        }
                    }
                    break;
                }
            }
            //获取最大回撤率
            double MDD = PerformanceStatisicsUtils.computeMaxDrawDown(netvalue);

            double sum       = 0; //和
            double squareSum = 0; //平方和
            for (int i = 0; i < returnRatio.Count(); i++)
            {
                sum       += returnRatio[i];
                squareSum += Math.Pow(returnRatio[i], 2);
            }
            double average = sum / returnRatio.Count();
            double std     = Math.Sqrt(squareSum / returnRatio.Count() - average * average);
            double sharpe  = average * 252 / (std * Math.Sqrt(252));//夏普率   //average * 252=年化收益

            //处理MDD为0的情况
            double calmar = (MDD == 0 ? 4 : average * 252 / MDD);//Calmar比率
            return((0.5 * sharpe + 0.5 * calmar) / 8);

            //不处理MDD为0的情况
            //double calmar = (MDD == 0 ? 4 : average  / MDD);//Calmar比率
            //return (0.5 * sharpe + 0.5 * calmar)/8;

            //只用年化收益率来打分
            //return average * 252;

            //年化收益率 / 最大回撤
            //return average * 252 / MDD;
        }
Exemplo n.º 3
0
        /// <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);
                        }
                    }
                }
                //第一层循环底部....
            }
        }
Exemplo n.º 4
0
        /// <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, FourParameterPairs> chooseParameters(Dictionary <FourParameterPairs, 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时间段中)得分最高的参数对
            FourParameterPairs bestPara = new FourParameterPairs();
            //记录每个交易日的最佳参数对(Dictionary类型)
            Dictionary <DateTime, FourParameterPairs> paras = new Dictionary <DateTime, FourParameterPairs>();
            //记录每个交易日使用的参数对(List类型),用于输出到CSV文件
            List <ParameterPairsWithScore> parasWithScore = new List <ParameterPairsWithScore>();

            #region debug专用
            //debug专用:存贮所有参数组合在规定start, end期间的得分...
            Dictionary <FourParameterPairs, double> scoreWithParameterPair = new Dictionary <FourParameterPairs, double>();
            #endregion

            while (end <= endDate)
            {
                //循环所有的参数组合,选出(在choicePeriod时间段中)得分最高的参数对
                foreach (var item in result)
                {
                    var 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 ParameterPairsWithScore
                        {
                            tradeday    = item,
                            frequency   = bestPara.frequency,
                            numbers     = bestPara.numbers,
                            lossPercent = bestPara.lossPercent,
                            ERRatio     = bestPara.ERRatio,
                            Score       = marks
                        });
                    }
                }
                //将选出的(choicePeriod时间段中)最优参数对,作为最优解保存到( choicePeriod + serviceLife时间段)
                List <DateTime> dates = DateUtils.GetTradeDays(paraStart, paraEnd);
                foreach (var item in dates)
                {
                    paras.Add(item, bestPara);
                    parasWithScore.Add(new ParameterPairsWithScore
                    {
                        tradeday    = item,
                        frequency   = bestPara.frequency,
                        numbers     = bestPara.numbers,
                        lossPercent = bestPara.lossPercent,
                        ERRatio     = bestPara.ERRatio,
                        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 FourParameterPairs();
                if (end >= endDate)
                {
                    break;
                }
                #region debug专用
                scoreWithParameterPair = new Dictionary <FourParameterPairs, 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);
        }