/// <summary>
        /// 估算未来的期权价格
        /// </summary>
        /// <param name="days"></param>
        /// <param name="code"></param>
        /// <param name="etfPrice"></param>
        /// <returns></returns>
        private double computeFuturePrice(int days, string code, double etfPrice)
        {
            double price  = 0;
            var    option = OptionUtilities.getOptionByCode(optionList, code);
            int    x      = Convert.ToInt32(optionPrice[code].duration - days);
            int    y      = Convert.ToInt32(Math.Round(1000 * Math.Log(option.strike / etfPrice), 0) + 500);
            double vol    = volSurface[x, y];

            price = ImpliedVolatilityUtilities.ComputeOptionPrice(option.strike, (optionPrice[code].duration - days) / 252.0, riskFreeRate, 0, option.optionType, vol, etfPrice);
            return(price);
        }
        /// <summary>
        /// 以收益损失比给期权组合打分
        /// </summary>
        /// <param name="code1"></param>
        /// <param name="code2"></param>
        /// <param name="days"></param>
        /// <param name="upperPrice"></param>
        /// <param name="lowerPrice"></param>
        /// <param name="up"></param>
        /// <param name="low"></param>
        /// <returns></returns>
        private double computeMarkOfPairs(string code1, string code2, int days, double upperPrice, double lowerPrice, ref double up, ref double low)
        {
            double marks           = -100;
            double initialValue    = optionPrice[code1].lastPrice - optionPrice[code2].lastPrice;
            double upperValue      = computeFuturePrice(days, code1, upperPrice) - computeFuturePrice(days, code2, upperPrice);
            double lowerValue      = computeFuturePrice(days, code1, lowerPrice) - computeFuturePrice(days, code2, lowerPrice);
            var    option2         = OptionUtilities.getOptionByCode(optionList, code2);
            double margin          = OptionMargin.ComputeOpenMargin(etfPrice, optionPrice[code2].lastPrice, option2.strike, option2.optionType, 1, etfPrice) + (upperPrice - lowerPrice) / 2;
            double capitalOccupied = margin - initialValue;

            up    = (upperValue - initialValue) / capitalOccupied;
            low   = (lowerValue - initialValue) / capitalOccupied;
            marks = up / low;
            return(marks);
        }
 private void displayPairs()
 {
     foreach (var item in pairs)
     {
         var    option1 = OptionUtilities.getOptionByCode(optionList, item.option1);
         var    option2 = OptionUtilities.getOptionByCode(optionList, item.option2);
         var    price1  = optionPrice[option1.optionCode];
         var    price2  = optionPrice[option2.optionCode];
         double delta   = price1.delta - price2.delta;
         double gamma   = price1.gamma - price2.gamma;
         double vega    = price1.vega - price2.vega;
         double theta   = price1.theta - price2.theta;
         Console.WriteLine("代码:{0}, 名称:{1}, 价格:{2} ask:{3}  bid:{4}, 波动率:{5}", option1.optionCode, option1.optionName, optionPrice[option1.optionCode].lastPrice, optionPrice[option1.optionCode].ask, optionPrice[option1.optionCode].bid, price1.impv);
         Console.WriteLine("代码:{0}, 名称:{1}, 价格:{2} ask:{3}  bid:{4}, 波动率:{5}", option2.optionCode, option2.optionName, optionPrice[option2.optionCode].lastPrice, optionPrice[option2.optionCode].ask, optionPrice[option2.optionCode].bid, price2.impv);
         Console.WriteLine("mark:{0}, delta:{1}, gamma:{2}, vega:{3}, theta:{4}, profit:{5}, loss:{6}", item.mark, delta, gamma, vega, theta, item.profit, item.loss);
         Console.WriteLine("==============================================");
     }
 }
        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 #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;
            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));
        }