/// <summary>
        /// 获取对应的期权合约
        /// </summary>
        /// <param name="duration"></param>
        /// <param name="strike"></param>
        /// <param name="etfPriceNow"></param>
        /// <param name="type"></param>
        /// <param name="today"></param>
        /// <returns></returns>
        private OptionInfo getOptionCode(double duration, double etfPriceNow, string type, DateTime today)
        {
            OptionInfo option         = new OptionInfo();
            var        optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today);

            if (type == "认购")
            {
                //选取指定的看涨期权
                var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList();
                if (list.Count > 0)
                {
                    option = list[0];
                }
            }
            else if (type == "认沽")
            {
                //选取指定的看跌期权
                var list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽"), etfPriceNow - 0.5, etfPriceNow), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList();
                if (list.Count > 0)
                {
                    option = list[0];
                }
            }
            return(option);
        }
        /// <summary>
        /// 跨式期权开仓
        /// </summary>
        /// <param name="dataToday"></param>
        /// <param name="signal"></param>
        /// <param name="positions"></param>
        /// <param name="myAccount"></param>
        /// <param name="pairs"></param>
        /// <param name="today"></param>
        /// <param name="index"></param>
        /// <param name="duration"></param>
        private void openStrangle(ref Dictionary <string, List <KLine> > dataToday, ref Dictionary <string, MinuteSignal> signal, ref SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions, ref BasicAccount myAccount, ref SortedDictionary <DateTime, List <StranglePair> > pairs, DateTime today, int index, double duration)
        {
            DateTime now            = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), index);
            double   etfPriceNow    = dataToday[targetVariety][index].open;
            var      optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today);
            //选取指定的看涨期权
            var        list = OptionUtilities.getOptionListByDate(OptionUtilities.getOptionListByStrike(OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购"), etfPriceNow, etfPriceNow + 0.5), Kit.ToInt_yyyyMMdd(today)).OrderBy(x => x.strike).ToList();
            OptionInfo call = list[0];
            //根据给定的看涨期权选取对应的看跌期权
            OptionInfo put = OptionUtilities.getCallByPutOrPutByCall(optionInfoList, call);

            if (call.strike != 0 && put.strike != 0 && (call.modifiedDate > today.AddDays(10) || call.modifiedDate < today)) //跨式期权组合存在进行开仓
            {
                tradeAssistant(ref dataToday, ref signal, call.optionCode, call.contractMultiplier, today, now, index);
                tradeAssistant(ref dataToday, ref signal, put.optionCode, put.contractMultiplier, today, now, index);
                StranglePair openPair = new StranglePair()
                {
                    callCode = call.optionCode, putCode = put.optionCode, callPosition = call.contractMultiplier, putPosition = put.contractMultiplier, endDate = call.endDate, etfPrice = etfPriceNow, callStrike = call.strike, putStrike = put.strike, modifiedDate = now, strangleOpenPrice = dataToday[call.optionCode][index].open + dataToday[put.optionCode][index].open, closeDate = new DateTime(), closePrice = 0
                };
                List <StranglePair> pairList = new List <StranglePair>();
                pairList.Add(openPair);
                pairs.Add(now, pairList);
                MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index);
            }
        }
        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());
        }
示例#4
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());
        }
示例#5
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;
            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++)
            {
                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.callCode)
                        {
                            holdingStatus.strike        = item.strike;
                            holdingStatus.callPosition *= item.contractMultiplier / standardContractMultiplier;
                            holdingStatus.putPosition  *= 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;
                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 (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.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());
                    }
                    if (holdingStatus.callPosition * orignalSignal < 0) //仓位和信号相反,强制平仓
                    {
                        Console.WriteLine("平仓!");

                        MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint);
                        holdingStatus = new Straddle();
                    }
                    if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓
                    {
                        Console.WriteLine("平仓!");
                        MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint);
                        holdingStatus = new Straddle();
                    }
                }
                //指定开仓时间为开盘第10分钟。错开开平仓的时间。
                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].open, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, 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].open;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].open;
                        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].open, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, 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].open;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].open + putPrice[openIndex].open;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    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.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());
                    }
                    //计算期权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, nowIndex: thisIndex);
                    holdingStatus.etfPosition += etfChangeVolume;
                    // AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                }

                //更新当日属性信息
                AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, thisTime, thisIndex, 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}, delta: {7}, etfVolume: {8}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPosition, dataToday[holdingStatus.callCode][thisIndex].close, dataToday[holdingStatus.putCode][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));
        }