/// <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);
            }
        }
        /// <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>
        private void closeStrangle(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)
        {
            DateTime            now         = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), index);
            double              etfPriceNow = dataToday[targetVariety][index].open;
            List <StranglePair> holdPairs   = pairs.Last().Value;
            StranglePair        pair        = holdPairs.Last();

            //关闭当前组合,为挪仓做准备
            tradeAssistant(ref dataToday, ref signal, pair.callCode, -pair.callPosition, today, now, index);
            tradeAssistant(ref dataToday, ref signal, pair.putCode, -pair.putPosition, today, now, index);
            pair.closeDate  = now;
            pair.closePrice = dataToday[pair.callCode][index].open + dataToday[pair.putCode][index].open;
            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());
        }
        /// <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 modifyStrangle(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, ref StranglePair newPair, DateTime today, int index)
        {
            DateTime now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), index);

            tradeAssistant(ref dataToday, ref signal, newPair.callCode, newPair.callPosition, today, now, index);
            tradeAssistant(ref dataToday, ref signal, newPair.putCode, newPair.putPosition, today, now, index);
            pairs.Last().Value.Add(newPair);
            MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: index);
        }
Example #5
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());
        }