/// <summary>
        /// 获得所有参数对的评分
        /// </summary>
        /// <param name="result"></param>
        /// <param name="pair"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public double getMarks(Dictionary <FiveParameterPairs, SortedDictionary <DateTime, double> > result, FiveParameterPairs 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.longER == pair.longER && item.Key.shortER == pair.shortER && 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);
                        }
                    }
                    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.0 ? 4 : average * 252 / MDD);//Calmar比率

            return((0.3 * sharpe + 0.7 * 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;
        }
Beispiel #2
0
        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));
        }
Beispiel #3
0
        /// <summary>
        /// 获得单个参数对的评分
        /// </summary>
        /// <param name="parameterPair"></param>
        /// <returns></returns>
        public double getKeyValuePairMarks(KeyValuePair <FourParameterPairs, SortedDictionary <DateTime, double> > parameterPair)
        {
            List <double> netvalue    = new List <double>(); //净值
            List <double> returnRatio = new List <double>(); //收益率

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

            foreach (var num in parameterPair.Value)
            {
                if (num.Key >= startDate && num.Key <= endDate)
                {
                    total += num.Value;
                    netvalue.Add(total / initialCapital);
                    returnRatio.Add(num.Value / initialCapital);
                }
            }

            //获取最大回撤率
            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;

            //只用年化收益率来打分(*100,变为百分制)
            return(average * 252 * 100);
        }
        public void compute()
        {
            SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >();
            //初始化Account信息
            BasicAccount myAccount = new BasicAccount();

            myAccount.initialAssets = initialCapital;
            myAccount.totalAssets   = initialCapital;
            myAccount.freeCash      = myAccount.totalAssets;
            //记录历史账户信息
            List <BasicAccount> accountHistory = new List <BasicAccount>();
            List <double>       benchmark      = new List <double>();
            //标记当日跨式组合多空信号。1表示多头,0表示无信号,-1表示空头。
            double   orignalSignal = 0;
            Straddle holdingStatus = new Straddle();

            //统计历史波动率分位数,从回测期开始前一天,统计到最后一天
            double[][] fractile = new double[backTestingDuration + 1][];
            fractile = computeRollingFractile(startIndex - 1, etfDailyData.Count() - 1, 100);
            //统计隐含波动率
            computeImpv();
            //统计隐含波动率和历史波动率之差 epsilon=max[E(IV-HV),0]
            computeEpsilon();
            //按时间遍历,2015年02月09日50ETF期权上市开始,2月10日开始昨日收盘的隐含波动率数据。
            for (int i = startIndex + 1; i < startIndex + backTestingDuration; i++)
            {
                Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                double fractile90Yesterday = fractile[i - 1][9]; //昨日历史波动率90分位数
                double fractile70Yesterday = fractile[i - 1][7]; //昨日历史波动率70分位数
                double fractile50Yesterday = fractile[i - 1][5]; //昨日历史波动率50分位数
                double fractile30Yesterday = fractile[i - 1][3]; //昨日历史波动率30分位数
                double volYesterday        = etfVol[i - 1];      //昨日历史波动率
                double impvYesterday       = optionVol[i - 1];   //昨日隐含波动率
                //获取当日ATM期权合约代码
                DateTime today    = etfDailyData[i].time;
                double   etfPrice = etfDailyData[i].close;
                double   duration;
                //获取当日期限结构,选取当月合约,若当日合约到日期小于等于3天,直接开仓下月合约
                List <double> dateStructure = OptionUtilities.getDurationStructure(optionInfoList, today);
                double        duration0     = dateStructure[0] <= 3 ? dateStructure[1] : dateStructure[0];
                duration = duration0;
                var call      = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList();
                var callATM   = call[0];
                var callPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today);
                // double callImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(callATM.strike, duration / 252.0, 0.04, 0, callATM.optionType, callPrice[0].close, etfPrice);
                var put      = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList();
                var putATM   = put[0];
                var putPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today);
                //double putImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(putATM.strike, duration / 252.0, 0.04, 0, putATM.optionType, putPrice[0].close, etfPrice);
                //整合当日分钟线数据
                Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >();
                var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", today);
                dataToday.Add("510050.SH", etfData.Cast <KLine>().ToList());
                dataToday.Add(callATM.optionCode, callPrice.Cast <KLine>().ToList());
                dataToday.Add(putATM.optionCode, putPrice.Cast <KLine>().ToList());

                //策略信号处理
                ////信号1
                //orignalSignal = 0;
                //if (volYesterday >= fractile70Yesterday)
                //{
                //    //卖出跨式期权
                //    orignalSignal = -1;
                //}
                //else if (optionVol[i - 1] < volYesterday)
                //{
                //    //买入跨式期权
                //    orignalSignal = 1;
                //}
                //else if (optionVol[i - 1] - volYesterday > epsilon[i - 1])
                //{
                //    //卖出跨式期权
                //    orignalSignal = -1;
                //}
                //信号2
                orignalSignal = 0;
                if (volYesterday - impvYesterday > 0 && volYesterday <= fractile70Yesterday)
                {
                    //买入跨式期权
                    //orignalSignal = 1;
                }
                else if (impvYesterday - volYesterday > 2 * epsilon[i - 1])
                {
                    //卖出跨式期权
                    orignalSignal = -1;
                }

                //指定平仓时间为开盘第一个分钟。
                DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), 0);
                Console.WriteLine("time: {0}, 昨日历史波动率: {1}, 历史波动率70分位数: {2}, 昨日隐含波动率: {3}", now, volYesterday.ToString("N"), fractile70Yesterday.ToString("N"), optionVol[i - 1].ToString("N"));
                //如果有持仓先判断持仓状态和信号方向是否相同,如果不同先平仓
                if (holdingStatus.callPosition != 0)
                {
                    //平仓之前获取IH数据
                    if (holdingStatus.IHCode != null)
                    {
                        var IHData = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.IHCode, today);
                        dataToday.Add(holdingStatus.IHCode, IHData.Cast <KLine>().ToList());
                    }
                    if (dataToday.ContainsKey(holdingStatus.callCode) == false)
                    {
                        var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today);
                        dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList());
                    }
                    if (dataToday.ContainsKey(holdingStatus.putCode) == false)
                    {
                        var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today);
                        dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList());
                    }
                    if (holdingStatus.callPosition * orignalSignal < 0) //仓位和信号相反,强制平仓
                    {
                        Console.WriteLine("平仓!");

                        MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint);
                        holdingStatus = new Straddle();
                    }
                    if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓
                    {
                        Console.WriteLine("平仓!");
                        MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint);
                        holdingStatus = new Straddle();
                    }
                }
                //指定开仓时间为开盘第10分钟。错开开平仓的时间。
                int openIndex = 10;
                now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex);
                if (holdingStatus.callPosition == 0 && orignalSignal != 0) //无仓位有信号,开仓
                {
                    if (orignalSignal == 1)                                //做多跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex
                        };
                        Console.WriteLine("做多跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = optionVolume;
                        holdingStatus.putPosition        = optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].close;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    else if (orignalSignal == -1) //做空跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex
                        };
                        Console.WriteLine("做空跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = -optionVolume;
                        holdingStatus.putPosition        = -optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].close;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, capitalVerification: false);
                }
                //每日收盘前,整理持仓情况
                int thisIndex = 239;
                var thisTime  = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex);
                benchmark.Add(etfData[thisIndex].close);
                if (holdingStatus.callPosition != 0)
                {
                    if (dataToday.ContainsKey(holdingStatus.callCode) == false)
                    {
                        var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today);
                        dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList());
                    }
                    if (dataToday.ContainsKey(holdingStatus.putCode) == false)
                    {
                        var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today);
                        dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList());
                    }
                    AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                }
                //更新当日属性信息
                AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                //记录历史仓位信息
                accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                //在控制台上数据每日持仓信息
                if (holdingStatus.callPosition != 0)
                {
                    Console.WriteLine("time: {0},etf: {1}, strike: {2}, position: {3}, call: {4}, put: {5}, endDate: {6}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPosition, dataToday[holdingStatus.callCode][thisIndex].close, dataToday[holdingStatus.putCode][thisIndex].close, holdingStatus.endDate);
                }
                //Console.WriteLine("time: {0}, total: {1}, cash: {2}, option: {3}, margin: {4}", thisTime, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin);
            }
            //策略绩效统计及输出
            PerformanceStatisics myStgStats = new PerformanceStatisics();

            myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions, benchmark.ToArray());
            //画图
            Dictionary <string, double[]> line = new Dictionary <string, double[]>();

            double[] netWorth = accountHistory.Select(a => a.totalAssets / initialCapital).ToArray();
            line.Add("NetWorth", netWorth);
            //记录净值数据
            RecordUtil.recordToCsv(accountHistory, GetType().FullName, "account", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_"));
            //记录持仓变化
            var positionStatus = OptionRecordUtil.Transfer(positions);

            RecordUtil.recordToCsv(positionStatus, GetType().FullName, "positions", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_"));
            //记录统计指标
            var performanceList = new List <PerformanceStatisics>();

            performanceList.Add(myStgStats);
            RecordUtil.recordToCsv(performanceList, GetType().FullName, "performance", parameters: "straddle", performance: myStgStats.anualSharpe.ToString("N").Replace(".", "_"));
            //统计指标在console 上输出
            Console.WriteLine("--------Strategy Performance Statistics--------\n");
            Console.WriteLine(" netProfit:{0,5:F4} \n totalReturn:{1,-5:F4} \n anualReturn:{2,-5:F4} \n anualSharpe :{3,-5:F4} \n winningRate:{4,-5:F4} \n PnLRatio:{5,-5:F4} \n maxDrawDown:{6,-5:F4} \n maxProfitRatio:{7,-5:F4} \n informationRatio:{8,-5:F4} \n alpha:{9,-5:F4} \n beta:{10,-5:F4} \n averageHoldingRate:{11,-5:F4} \n", myStgStats.netProfit, myStgStats.totalReturn, myStgStats.anualReturn, myStgStats.anualSharpe, myStgStats.winningRate, myStgStats.PnLRatio, myStgStats.maxDrawDown, myStgStats.maxProfitRatio, myStgStats.informationRatio, myStgStats.alpha, myStgStats.beta, myStgStats.averageHoldingRate);
            Console.WriteLine("-----------------------------------------------\n");
            //benchmark净值
            List <double> netWorthOfBenchmark = benchmark.Select(x => x / benchmark[0]).ToList();

            line.Add("Base", netWorthOfBenchmark.ToArray());
            string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray();
            Application.Run(new PLChart(line, datestr));
        }
        public void compute()
        {
            log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startDate), Kit.ToInt_yyyyMMdd(endDate));
            var repo = Platforms.container.Resolve <OptionInfoRepository>();

            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());
        }
Beispiel #6
0
        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));
        }
Beispiel #7
0
        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());
        }
Beispiel #8
0
        /// <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");
        }
Beispiel #9
0
        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);
        }
        /// <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.freeCash    = myAccount.totalAssets;
            //记录历史账户信息
            List <BasicAccount> accountHistory = new List <BasicAccount>();
            //记录benchmark数据
            List <double> benchmark = new List <double>();

            ///数据准备
            //交易日信息
            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());
                }
            }

            //频率转换测试
            //List<KLine> data_5min = MinuteFrequencyTransferUtils.MinuteToNPeriods(data[targetVariety], "Minutely", 3);
            //List<KLine> data_1Day = MinuteFrequencyTransferUtils.MinuteToNPeriods(data[targetVariety], "Daily", 1);
            //List<KLine> data_1Month = MinuteFrequencyTransferUtils.MinuteToNPeriods(data[targetVariety], "Monthly", 1);
            // List<KLine> data_1Week = MinuteFrequencyTransferUtils.MinuteToNPeriods(data[targetVariety], "Weekly", 1);
            //计算需要指标
            //(1)回看长度内的高低极值点(值)
            //(2)各级别高低拐点的位置(值)
            List <double> upReversionPoint   = new List <double>();
            List <double> downReversionPoint = new List <double>();

            upReversionPoint   = ComputeReversionPoint.findUpReversionPoint(data[targetVariety], NDays, lengthOfBackLooking);
            downReversionPoint = ComputeReversionPoint.findDownReversionPoint(data[targetVariety], NDays, lengthOfBackLooking);
            int indexOfNow = -1;//记录整个data的索引

            ///回测循环
            //回测循环--By Day
            foreach (var day in tradeDays)
            {
                //取出当天的数据
                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));
                }


                int index = 0;
                //交易开关设置,控制day级的交易开关
                bool tradingOn = true; //总交易开关
                bool openingOn = true; //开仓开关
                bool closingOn = true; //平仓开关

                //是否为回测最后一天
                bool isLastDayOfBackTesting = day.Equals(endDate);

                //回测循环 -- By Minute
                //不允许在同一根1minBar上开平仓
                while (index < 240)
                {
                    int nextIndex = index + 1;
                    indexOfNow++;
                    DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(day), index);
                    Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                    DateTime next = new DateTime();
                    //int indexOfNow = data[targetVariety].FindIndex(s => s.time == now);
                    myAccount.time = now;
                    double nowClose              = dataToday[targetVariety][index].close;
                    double nowUpReversionPoint   = upReversionPoint[indexOfNow];
                    double nowDownReversionPoint = downReversionPoint[indexOfNow];
                    //实际操作从第一个回望期后开始
                    if (indexOfNow < lengthOfBackLooking - 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-容忍度),平多
                            //(1)若当前为 回测结束日 或 tradingOn 为false,平仓
                            if (isLastDayOfBackTesting || tradingOn == false)
                            {
                                next = MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now: now, slipPoint: slipPoint);
                            }
                            //(2)若当前下穿下反转点*(1-容忍度),平多
                            else if (data[targetVariety][indexOfNow - 1].close >= nowDownReversionPoint * (1 - toleranceDegree) && nowClose < nowDownReversionPoint * (1 - toleranceDegree))
                            {
                                next = MinuteCloseAllPositonsWithSlip.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].close / contractTimes) * contractTimes;
                            //若剩余资金至少购买一手 且 出上反转信号 开仓
                            if (openVolume >= 1 && data[targetVariety][indexOfNow - 1].close <= nowUpReversionPoint * (1 + toleranceDegree) && nowClose > nowUpReversionPoint * (1 + toleranceDegree))
                            {
                                MinuteSignal openSignal = new MinuteSignal()
                                {
                                    code = targetVariety, volume = openVolume, time = now, tradingVarieties = "stock", price = dataToday[targetVariety][index].close, minuteIndex = index
                                };
                                signal.Add(targetVariety, openSignal);
                                next = MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now);
                                //当天买入不可卖出
                                closingOn = false;
                            }
                        }

                        //账户信息更新
                        AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, now, dataToday);
                    }

                    catch (Exception)
                    {
                        throw;
                    }
                    nextIndex = Math.Max(nextIndex, TimeListUtility.MinuteToIndex(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().close);

                //显示当前信息
                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);
             */
            //策略绩效统计及输出
            PerformanceStatisics myStgStats = new PerformanceStatisics();

            myStgStats = PerformanceStatisicsUtils.compute(accountHistory, positions, benchmark.ToArray());

            //统计指标在console 上输出
            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));

            /*
             * //将accountHistory输出到csv
             * var resultPath = ConfigurationManager.AppSettings["CacheData.ResultPath"] + "accountHistory.csv";
             * var dt = DataTableUtils.ToDataTable(accountHistory);          // List<MyModel> -> DataTable
             * CsvFileUtils.WriteToCsvFile(resultPath, dt);	// DataTable -> CSV File
             */

            Console.ReadKey();
        }
Beispiel #11
0
        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));
        }
        /// <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();
        }