示例#1
0
        /// <summary>
        /// 策略回测部分,期权时间价值策略
        /// 在日循环上判断(1)选择操作标的(2)是否开平仓,在分钟循环上进行具体操作
        /// 分钟上的操作:(1)开仓(2)到期平仓(3)调仓平值期权(4)止盈止损
        /// (1)若直接开平仓,在开盘15分钟时进行操作(2)若判断止盈止损,在收盘15分钟时进行操作
        /// </summary>
        public void compute()
        {
            log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startdate), Kit.ToInt_yyyyMMdd(endDate));
            var repo           = Platforms.container.Resolve <OptionInfoRepository>();
            var optionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1);

            Caches.put("OptionInfo", optionInfoList);
            List <DateTime> tradeDays = DateUtils.GetTradeDays(startdate, endDate);
            //var ETFDaily = Platforms.container.Resolve<StockDailyRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", Kit.ToDate(20150101),Kit.ToDate(20160731));

            ///账户初始化
            //初始化position
            SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >();
            //初始化Account信息
            BasicAccount myAccount = new BasicAccount();

            myAccount.totalAssets = initialCapital;
            myAccount.freeCash    = myAccount.totalAssets;
            //记录历史账户信息
            List <BasicAccount> accountHistory = new List <BasicAccount>();

            ///回测循环
            //回测循环--By Day
            foreach (var day in tradeDays)
            {
                //日内数据准备
                Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >();
                var             list     = OptionUtilities.getOptionListByDate(optionInfoList, Kit.ToInt_yyyyMMdd(day));
                List <DateTime> endDate  = OptionUtilities.getEndDateListByAscending(list);
                var             ETFtoday = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", day);
                data.Add("510050.SH", ETFtoday.Cast <KLine>().ToList());
                foreach (var info in list)
                {
                    string IHCode      = OptionUtilities.getCorrespondingIHCode(info, Kit.ToInt_yyyyMMdd(day));
                    var    repoOption  = Platforms.container.Resolve <OptionMinuteRepository>();
                    var    optionToday = repoOption.fetchFromLocalCsvOrWindAndSave(info.optionCode, day);
                    data.Add(info.optionCode, optionToday.Cast <KLine>().ToList());
                }
                int index = 0;
                //交易开关设置,控制day级的交易开关
                bool tradingOn = true; //总交易开关
                bool openingOn = true; //开仓开关
                bool closingOn = true; //平仓开关

                //是否为交割日
                bool isExpiredDay = day.Equals(endDate[0]);
                //是否为回测最后一天
                bool isLastDayOfBackTesting = day.Equals(endDate);

                //回测循环 -- By Minute
                //不允许在同一根1minBar上开平仓
                while (index < 240)
                {
                    int      nextIndex = index + 1;
                    DateTime now       = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(day), index);
                    Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                    double etfPrice = ETFtoday[index].close;
                    //按strike price与etf价格的接近程度排序
                    List <double> strikeTodayArr = OptionUtilities.getStrikeListByAscending(list).OrderBy(x => Math.Abs(x - etfPrice)).ToList();
                    try
                    {
                        /*
                         * if (index != 225)
                         * {
                         *  index = nextIndex;
                         *  continue;
                         * }
                         */
                        //持仓查询,先平后开
                        //若当前有持仓 且 允许平仓
                        //是否是空仓,若position中所有品种volum都为0,则说明是空仓
                        bool isEmptyPosition = positions.Count != 0 ? positions[positions.Keys.Last()].Values.Sum(x => Math.Abs(x.volume)) == 0 : true;

                        if ((positions.Count != 0 && !isEmptyPosition) && tradingOn)
                        {
                            //平仓条件
                            //(1)若当天为交割日或回测结束日,平仓,且关闭开仓开关,次日才能开仓;
                            //(2)若closingOn为false,平仓;
                            //(3)检查持仓期权是否为平价期权,若否,清掉当前头寸并建立新的持仓;
                            //--------------------------------------------------------------------
                            //(1)若当天为交割日或回测结束日,平仓,且关闭开仓开关,次日才能开仓;
                            //(2)若closingOn为false,平仓;
                            //取出当前持仓期权的strike
                            double strikePriceOfPositions = optionInfoList[optionInfoList.FindIndex(a => a.optionCode == positions[positions.Keys.Last()].Values.First().code)].strike;
                            bool   isParPriceOption       = strikePriceOfPositions == strikeTodayArr[0];
                            //--------------------------------------------------------------------
                            if (!isEmptyPosition && (isExpiredDay || isLastDayOfBackTesting || closingOn == false))
                            {
                                //全部平仓
                                DateTime next = MinuteCloseAllPositonsWithSlip.closeAllPositions(data, ref positions, ref myAccount, now: now, slipPoint: slipPoint);
                                //当天不可再开仓
                                openingOn = false;
                            }
                            //(3)检查持仓期权是否为平价期权,若否,清掉当前头寸并建立新的持仓;
                            else if (!isEmptyPosition && !isParPriceOption)
                            {
                                //全部平仓
                                DateTime next = MinuteCloseAllPositonsWithSlip.closeAllPositions(data, ref positions, ref myAccount, now: now, slipPoint: slipPoint);
                                //当天不可再开仓
                                openingOn = false;
                            }
                        }
                        //若当前无持仓 且 允许开仓
                        //若当前为交割日,则不开仓
                        if (isExpiredDay == true)
                        {
                            openingOn = false;
                        }
                        else if ((positions.Count == 0 || isEmptyPosition) && openingOn && tradingOn)
                        {
                            //标的池构建
                            //选择目标期权品种放入标的池:
                            //四个头寸(1)short当月平价认购(2)short当月平价认沽(3)long下月平价认购(4)long下月平价认沽
                            OptionInfo callCandidateFront = OptionUtilities.getSpecifiedOption(list, endDate[0], "认购", strikeTodayArr[0])[0];
                            OptionInfo putCandidateFront  = OptionUtilities.getSpecifiedOption(list, endDate[0], "认沽", strikeTodayArr[0])[0];
                            OptionInfo callCandidateNext  = OptionUtilities.getSpecifiedOption(list, endDate[1], "认购", strikeTodayArr[0])[0];
                            OptionInfo putCandidateNext   = OptionUtilities.getSpecifiedOption(list, endDate[1], "认沽", strikeTodayArr[0])[0];

                            //检查四个标的strike是否相同,若相同则开仓,若不相同是,说明下月平价期权尚未挂出,则continue
                            bool isSameStrike = callCandidateFront.strike == callCandidateFront.strike;
                            //生成开仓信号
                            if (isSameStrike)
                            {
                                //查询可用资金
                                double nowFreeCash = myAccount.freeCash;
                                //计算每个头寸的建仓量,原则:尽量使各头寸等金额
                                double openVolumeOfCallFront = Math.Floor(nowFreeCash / 4 / data[callCandidateFront.optionCode][index].close / optionContractTimes) * optionContractTimes;
                                double openVolumeOfPutFront  = Math.Floor(nowFreeCash / 4 / data[putCandidateFront.optionCode][index].close / optionContractTimes) * optionContractTimes;
                                double openVolumeOfCallNext  = Math.Floor(nowFreeCash / 4 / data[callCandidateNext.optionCode][index].close / optionContractTimes) * optionContractTimes;
                                double openVolumeOfPutNext   = Math.Floor(nowFreeCash / 4 / data[putCandidateNext.optionCode][index].close / optionContractTimes) * optionContractTimes;

                                MinuteSignal callFront = new MinuteSignal()
                                {
                                    code = callCandidateFront.optionCode, volume = -openVolumeOfCallFront, time = now, tradingVarieties = "option", price = data[callCandidateFront.optionCode][index].close, minuteIndex = index
                                };
                                MinuteSignal putFront = new MinuteSignal()
                                {
                                    code = putCandidateFront.optionCode, volume = -openVolumeOfPutFront, time = now, tradingVarieties = "option", price = data[putCandidateFront.optionCode][index].close, minuteIndex = index
                                };
                                MinuteSignal callNext = new MinuteSignal()
                                {
                                    code = callCandidateNext.optionCode, volume = openVolumeOfCallNext, time = now, tradingVarieties = "option", price = data[callCandidateNext.optionCode][index].close, minuteIndex = index
                                };
                                MinuteSignal putNext = new MinuteSignal()
                                {
                                    code = putCandidateNext.optionCode, volume = openVolumeOfPutNext, time = now, tradingVarieties = "option", price = data[putCandidateNext.optionCode][index].close, minuteIndex = index
                                };
                                signal.Add(callFront.code, callFront);
                                signal.Add(putFront.code, putFront);
                                signal.Add(callNext.code, callNext);
                                signal.Add(putNext.code, putNext);
                                DateTime next = MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, data, ref positions, ref myAccount, slipPoint: slipPoint, now: now);
                                nextIndex = Math.Max(nextIndex, TimeListUtility.MinuteToIndex(next));
                            }
                        }
                        //账户信息更新
                        AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, now, data);
                    }

                    catch (Exception)
                    {
                        throw;
                    }

                    index = nextIndex;
                }
                //账户信息记录By Day
                //用于记录的临时账户
                BasicAccount tempAccount = new BasicAccount();
                tempAccount.time          = myAccount.time;
                tempAccount.freeCash      = myAccount.freeCash;
                tempAccount.margin        = myAccount.margin;
                tempAccount.positionValue = myAccount.positionValue;
                tempAccount.totalAssets   = myAccount.totalAssets;
                accountHistory.Add(tempAccount);
            }

            //遍历输出到console
            foreach (var account in accountHistory)
            {
                Console.WriteLine("time:{0},netWorth:{1,8:F3}\n", account.time, account.totalAssets / initialCapital);
            }

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

            CsvFileUtils.WriteToCsvFile(resultPath, dt);                 // DataTable -> CSV File
            Console.ReadKey();
        }
示例#2
0
        public void compute()
        {
            log.Info("开始回测(回测期{0}到{1})", Kit.ToInt_yyyyMMdd(startdate), Kit.ToInt_yyyyMMdd(endDate));
            var repo           = Platforms.container.Resolve <OptionInfoRepository>();
            var OptionInfoList = repo.fetchFromLocalCsvOrWindAndSaveAndCache(1);

            Caches.put("OptionInfo", OptionInfoList);
            List <DateTime> tradeDays = DateUtils.GetTradeDays(startdate, endDate);

            //var ETFDaily = Platforms.container.Resolve<StockDailyRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", Kit.ToDate(20150101),Kit.ToDate(20160731));
            foreach (var day in tradeDays)
            {
                Dictionary <string, List <KLine> > data = new Dictionary <string, List <KLine> >();
                var             list        = OptionUtilities.getOptionListByDate(OptionInfoList, Kit.ToInt_yyyyMMdd(day));
                List <DateTime> durationArr = OptionUtilities.getEndDateListByAscending(list);
                var             ETFtoday    = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", day);
                data.Add("510050.SH", ETFtoday.Cast <KLine>().ToList());
                foreach (var info in list)
                {
                    string IHCode      = OptionUtilities.getCorrespondingIHCode(info, Kit.ToInt_yyyyMMdd(day));
                    var    repoOption  = Platforms.container.Resolve <OptionMinuteRepository>();
                    var    optionToday = repoOption.fetchFromLocalCsvOrWindAndSave(info.optionCode, day);
                    data.Add(info.optionCode, optionToday.Cast <KLine>().ToList());
                }
                int index = 0;
                SortedDictionary <DateTime, Dictionary <string, MinutePositions> > positions = new SortedDictionary <DateTime, Dictionary <string, MinutePositions> >();
                while (index < 240)
                {
                    int      nextIndex = index + 1;
                    DateTime now       = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(day), index);
                    Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                    double        etfPrice       = ETFtoday[index].close;
                    List <double> strikeTodayArr = OptionUtilities.getStrikeListByAscending(list).OrderBy(x => Math.Abs(x - etfPrice)).ToList();
                    try
                    {
                        OptionInfo   callCandidateFront = OptionUtilities.getSpecifiedOption(list, durationArr[0], "认购", strikeTodayArr[0])[0];
                        OptionInfo   putCandidateFront  = OptionUtilities.getSpecifiedOption(list, durationArr[0], "认沽", strikeTodayArr[0])[0];
                        OptionInfo   callCandidateNext  = OptionUtilities.getSpecifiedOption(list, durationArr[1], "认购", strikeTodayArr[0])[0];
                        OptionInfo   putCandidateNext   = OptionUtilities.getSpecifiedOption(list, durationArr[1], "认沽", strikeTodayArr[0])[0];
                        MinuteSignal callFront          = new MinuteSignal()
                        {
                            code = callCandidateFront.optionCode, volume = -1, time = now, tradingVarieties = "option", price = data[callCandidateFront.optionCode][index].close, minuteIndex = index
                        };
                        MinuteSignal putFront = new MinuteSignal()
                        {
                            code = putCandidateFront.optionCode, volume = -1, time = now, tradingVarieties = "option", price = data[putCandidateFront.optionCode][index].close, minuteIndex = index
                        };
                        MinuteSignal callNext = new MinuteSignal()
                        {
                            code = callCandidateNext.optionCode, volume = 1, time = now, tradingVarieties = "option", price = data[callCandidateNext.optionCode][index].close, minuteIndex = index
                        };
                        MinuteSignal putNext = new MinuteSignal()
                        {
                            code = putCandidateNext.optionCode, volume = 1, time = now, tradingVarieties = "option", price = data[putCandidateNext.optionCode][index].close, minuteIndex = index
                        };
                        signal.Add(callFront.code, callFront);
                        signal.Add(putFront.code, putFront);
                        signal.Add(callNext.code, callNext);
                        signal.Add(putNext.code, putNext);
                        DateTime next = MinuteTransactionWithSlip.computeMinutePositions(signal, data, ref positions, slipPoint: 0.01, now: now);
                        nextIndex = Math.Max(nextIndex, TimeListUtility.MinuteToIndex(next));
                    }
                    catch (Exception)
                    {
                        throw;
                    }

                    index = nextIndex;
                }
            }
        }
示例#3
0
        public void compute()
        {
            SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >();
            //初始化Account信息
            BasicAccount myAccount = new BasicAccount();

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

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

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

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

                        MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint);
                        holdingStatus = new Straddle();
                    }
                    if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓
                    {
                        Console.WriteLine("平仓!");
                        MinuteCloseAllPositonsWithSlip.closeAllPositions(dataToday, ref positions, ref myAccount, now, slipPoint);
                        holdingStatus = new Straddle();
                    }
                }
                //指定开仓时间为开盘第10分钟。错开开平仓的时间。
                int openIndex = 10;
                now = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex);
                if (holdingStatus.callPosition == 0 && orignalSignal != 0) //无仓位有信号,开仓
                {
                    if (orignalSignal == 1)                                //做多跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex
                        };
                        Console.WriteLine("做多跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = optionVolume;
                        holdingStatus.putPosition        = optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].close;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    else if (orignalSignal == -1) //做空跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].close, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].close, minuteIndex = openIndex
                        };
                        Console.WriteLine("做空跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = -optionVolume;
                        holdingStatus.putPosition        = -optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].close;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].close;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, capitalVerification: false);
                    //开仓进行对冲
                    string IHCode  = OptionUtilities.getCorrespondingIHCode(OptionUtilities.getSpecifiedOption(optionInfoList, holdingStatus.endDate, "认购", holdingStatus.strike)[0], Kit.ToInt_yyyyMMdd(today));
                    var    IHToday = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(IHCode, today);
                    if (dataToday.ContainsKey(IHCode) == false)
                    {
                        dataToday.Add(IHCode, IHToday.Cast <KLine>().ToList());
                    }
                    double cashDelta = computeOptionCashDelta(positions.Last().Value, holdingStatus, today, dataToday, openIndex);
                    //double increment = Math.Round(-cashDelta / 1000) - holdingStatus.IHPosition;
                    //if (Math.Abs(increment) >= 0)
                    //{
                    //    MinuteSignal IHSignal = new MinuteSignal() { code = IHCode, volume = increment, time = now, tradingVarieties = "futures", price = IHToday[openIndex].close, minuteIndex = openIndex };
                    //    signal = new Dictionary<string, MinuteSignal>();
                    //    signal.Add(IHCode, IHSignal);
                    //    holdingStatus.IHCode = IHCode;
                    //    holdingStatus.IHPosition += increment;
                    //    MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, capitalVerification: false);
                    //}
                }
                //每日收盘前,整理持仓情况
                int thisIndex = 239;
                var thisTime  = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex);
                benchmark.Add(etfData[thisIndex].close);
                if (holdingStatus.callPosition != 0)
                {
                    if (dataToday.ContainsKey(holdingStatus.callCode) == false)
                    {
                        var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today);
                        dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList());
                    }
                    if (dataToday.ContainsKey(holdingStatus.putCode) == false)
                    {
                        var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today);
                        dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList());
                    }
                }
                if (holdingStatus.callPosition != 0)
                {
                    //获取对应的IH的数据
                    string IHCode  = OptionUtilities.getCorrespondingIHCode(OptionUtilities.getSpecifiedOption(optionInfoList, holdingStatus.endDate, "认购", holdingStatus.strike)[0], Kit.ToInt_yyyyMMdd(today));
                    var    IHToday = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(IHCode, today);
                    if (dataToday.ContainsKey(IHCode) == false)
                    {
                        dataToday.Add(IHCode, IHToday.Cast <KLine>().ToList());
                    }
                    if (IHCode != holdingStatus.IHCode && holdingStatus.IHPosition != 0) //期权对应的期货合约发生变化,那么进行移仓操作
                    {
                        var IHLast = Platforms.container.Resolve <FuturesMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.IHCode, today);
                        if (dataToday.ContainsKey(holdingStatus.IHCode) == false)
                        {
                            dataToday.Add(holdingStatus.IHCode, IHLast.Cast <KLine>().ToList());
                        }
                        MinuteSignal IHCloseSignal = new MinuteSignal()
                        {
                            code = holdingStatus.IHCode, volume = -holdingStatus.IHPosition, time = thisTime, tradingVarieties = "futures", price = IHLast[thisIndex].close, minuteIndex = thisIndex
                        };
                        signal = new Dictionary <string, MinuteSignal>();
                        signal.Add(holdingStatus.IHCode, IHCloseSignal);
                        holdingStatus.IHCode     = "";
                        holdingStatus.IHPosition = 0;
                        MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: thisTime, capitalVerification: false);
                        AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                    }

                    //计算期权持仓的delta值
                    double cashDelta = computeOptionCashDelta(positions.Last().Value, holdingStatus, today, dataToday, thisIndex);
                    //收盘前对冲
                    if (cashDelta != 0)
                    {
                        Console.WriteLine("time; {0}, delta: {1}", today, cashDelta);
                        //delta = 0;
                    }
                    //double increment = Math.Round(-cashDelta / 1000) - holdingStatus.IHPosition;
                    //if (Math.Abs(increment)>0)
                    //{
                    //    MinuteSignal IHSignal = new MinuteSignal() { code = IHCode, volume = increment, time = thisTime, tradingVarieties = "futures", price = IHToday[thisIndex].close, minuteIndex = thisIndex };
                    //    signal = new Dictionary<string, MinuteSignal>();
                    //    signal.Add(IHCode, IHSignal);
                    //    holdingStatus.IHCode = IHCode;
                    //    holdingStatus.IHPosition += increment;
                    //    MinuteTransactionWithSlip.computeMinuteOpenPositions(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: thisTime, capitalVerification: false);
                    //}
                }
                //更新当日属性信息
                AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                //记录历史仓位信息
                accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                //在控制台上数据每日持仓信息
                if (holdingStatus.callPosition != 0)
                {
                    Console.WriteLine("time: {0},etf: {1}, strike: {2}, position: {3}, call: {4}, put: {5}, endDate: {6}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPosition, dataToday[holdingStatus.callCode][thisIndex].close, dataToday[holdingStatus.putCode][thisIndex].close, holdingStatus.endDate);
                }
                Console.WriteLine("time: {0}, total: {1}, cash: {2}, option: {3}, margin: {4}", thisTime, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin);
            }
            //策略绩效统计及输出
            PerformanceStatisics myStgStats = new PerformanceStatisics();

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

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

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

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

            line.Add("Base", netWorthOfBenchmark.ToArray());
            string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray();
            Application.Run(new PLChart(line, datestr));
        }