예제 #1
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>();
            //持仓量
            double positionVolume = 0;
            //最大收入值
            double maxIncome = 0;


            //螺纹钢的日线数据准备,从回测期开始之前10个交易开始取
            int    number = 10;
            double k1     = 0.2;
            double k2     = -0.1;
            List <FuturesDaily> dailyData = new List <FuturesDaily>();

            dailyData = Platforms.container.Resolve <FuturesDailyDataService>().fetchFromLocalCsvOrWindAndSave(underlying, startDate, endDate);

            List <double> highList     = new List <double>();
            List <double> closeList    = new List <double>();
            List <double> lowList      = new List <double>();
            List <double> buyLineList  = new List <double>();
            List <double> sellLineList = new List <double>();
            List <double> rangeList    = new List <double>();

            for (int i = 0; i < dailyData.Count; i++)
            {
                if (highList.Count == number)
                {
                    highList.RemoveAt(0);
                    closeList.RemoveAt(0);
                    lowList.RemoveAt(0);
                }
                highList.Add(dailyData[i].high);
                closeList.Add(dailyData[i].close);
                lowList.Add(dailyData[i].low);

                //N日High的最高价HH
                double hightestHigh = highList.Max();
                //N日Close的最高价HC
                double hightestClose = closeList.Max();
                //N日Close的最低价LC
                double lowestClose = closeList.Min();
                //N日Low的最低价LL
                double open      = dailyData[i].open;
                double lowestLow = lowList.Min();
                double range     = Math.Max(hightestHigh - lowestClose, hightestClose - lowestLow);
                double buyLine   = open + k1 * range;
                double sellLine  = open + k2 * range;

                rangeList.Add(range);
                buyLineList.Add(buyLine);
                sellLineList.Add(sellLine);
            }

            //第一层循环:所有交易日循环一遍...
            for (int i = 0; i < tradeDays.Count(); i++)
            {
                if (i == 513)
                {
                    double buyLine1  = buyLineList[i];
                    double sellLine1 = sellLineList[i];
                }
                DateTime today = tradeDays[i];
                //从wind或本地CSV获取相应交易日的数据list
                //这里不能直接去调用FreqTransferUtils.minuteToNMinutes(data, frequency),因为data.count可能为0
                var dataOnlyToday = getData(today, underlying);//一个交易日里有多条分钟线数据

                //将获取的数据,储存为KLine格式
                Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >();
                dataToday.Add(underlying, dataOnlyToday.Cast <KLine>().ToList());

                //第二层循环:只循环某当天的数据(开始的索引值为前一天数据的List.count)
                for (int j = 15; j < dataOnlyToday.Count() - 30; j++)
                {
                    DateTime now = dataOnlyToday[j].time;

                    # region 追踪止损判断 触发止损平仓

                    //追踪止损判断 触发止损平仓
                    if (positionVolume != 0) //头寸量不为0,额外要做的操作
                    {
                        //计算开盘价和头寸当前价的差价
                        double incomeNow = individualIncome(positions.Last().Value[underlying], dataOnlyToday[j].open);
                        //若当前收入大于最大收入值,则更新最大收入值
                        if (incomeNow > maxIncome)
                        {
                            maxIncome = incomeNow;
                        }

                        //若盈利回吐大于5个点 或者 最大收入大于45,则进行平仓
                        else if ((maxIncome - incomeNow) > lossPercent * Math.Abs(dataOnlyToday[j].open) || incomeNow < -lossPercent * Math.Abs(dataOnlyToday[j].open)) //从最高点跌下来3%,就止损
                        {
                            positionVolume = 0;
                            // Console.WriteLine("追踪止损!平仓价格: {0}", dataOnlyToday[j].open);
                            MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                            maxIncome = 0;
                        }
                    }

                    #endregion

                    var price = dataOnlyToday[j].open;
                    //价格向上突破上轨
                    if (price > buyLineList[i])
                    {
                        //如果持有空仓,则先平仓
                        if (positionVolume == -1)
                        {
                            positionVolume = 0;
                            //Console.WriteLine("追踪止损!平仓价格: {0}", dataOnlyToday[j].open);
                            MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        }
                        //如果没有仓位,则直接多仓
                        if (positionVolume == 0)
                        {
                            double volume = 1;
                            //长头寸信号
                            MinuteSignal longSignal = new MinuteSignal()
                            {
                                code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = dataOnlyToday[j].open, minuteIndex = j
                            };
                            //signal保存长头寸longSignal信号
                            Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                            signal.Add(underlying, longSignal);
                            MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: longSignal.minuteIndex);
                            //Console.WriteLine("做多期货!多头开仓价格: {0}", dataOnlyToday[j].open);
                            //头寸量叠加
                            positionVolume += volume;
                        }
                    }

                    //价格向下突破下轨
                    if (price < sellLineList[i])
                    {
                        //如果持有多仓,则先平仓
                        if (positionVolume == 1)
                        {
                            positionVolume = 0;
                            // Console.WriteLine("追踪止损!平仓价格: {0}", dataOnlyToday[j].open);
                            MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        }
                        //如果没有仓位,则直接空仓
                        if (positionVolume == 0)
                        {
                            double       volume      = -1;
                            MinuteSignal shortSignal = new MinuteSignal()
                            {
                                code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = dataOnlyToday[j].open, minuteIndex = j
                            };
                            // Console.WriteLine("做空期货!空头开仓价格: {0}", dataOnlyToday[j].open);
                            Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                            signal.Add(underlying, shortSignal);
                            positionVolume += volume;
                            //分钟级交易
                            MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: shortSignal.minuteIndex);
                        }
                    }
                }
                int closeIndex = dataOnlyToday.Count() - 1;

                if (positionVolume != 0)
                {
                    positionVolume = 0;
                    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, dataOnlyToday[closeIndex].time, closeIndex, slipPoint);
                    // Console.WriteLine("{2}   每日收盘前强制平仓,平仓价格:{0},账户价值:{1}", dataOnlyToday[closeIndex].open, myAccount.totalAssets, today);
                }

                if (dataOnlyToday.Count > 0)
                {
                    //更新当日属性信息
                    AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, dataOnlyToday.Last().time, dataOnlyToday.Count() - 1, dataToday);

                    //记录历史仓位信息
                    accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                    benchmark.Add(dataOnlyToday.Last().close);
                    if (netValue.Count() == 0)
                    {
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = 0, benchmarkReturn = 0, netvalue = myAccount.totalAssets, benchmark = dataOnlyToday.Last().close
                        });
                    }
                    else
                    {
                        var netValueLast = netValue.Last();
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = myAccount.totalAssets / netValueLast.netvalue - 1, benchmarkReturn = dataOnlyToday.Last().close / netValueLast.benchmark - 1, netvalue = myAccount.totalAssets, benchmark = dataOnlyToday.Last().close
                        });
                    }
                }
            }
예제 #2
0
        public void compute()
        {
            SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions = new SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> >();
            //初始化Account信息
            BasicAccount myAccount = new BasicAccount(initialAssets: initialCapital, totalAssets: initialCapital, freeCash: initialCapital);
            //记录历史账户信息
            List <BasicAccount> accountHistory = new List <BasicAccount>();
            List <double>       benchmark      = new List <double>();
            //标记当日跨式组合多空信号。1表示多头,0表示无信号,-1表示空头。
            double   orignalSignal = 0;
            Straddle holdingStatus = new Straddle();

            //统计历史波动率分位数,从回测期开始前一天,统计到最后一天
            double[][] fractile = new double[backTestingDuration + 1][];
            fractile = computeRollingFractile(startIndex - 1, etfDailyData.Count() - 1, 100);
            //统计隐含波动率
            computeImpv();
            //统计隐含波动率和历史波动率之差 epsilon=max[E(IV-HV),0]
            computeEpsilon();
            //按时间遍历,2015年02月09日50ETF期权上市开始,2月10日开始昨日收盘的隐含波动率数据。
            for (int i = startIndex + 1; i < startIndex + backTestingDuration; i++)
            {
                DateTime today = etfDailyData[i].time;
                //获取当日上市的期权合约列表
                var optionInfoList = OptionUtilities.getUnmodifiedOptionInfoList(this.optionInfoList, today);
                //若当日发生50ETF分红派息,myAccount要加上分红的钱,并且需要调整持有头寸的strike
                if (today == timeOf50ETFDividend2016 && positions.Count > 0 && positions.Last().Value.ContainsKey("510050.SH"))
                {
                    //50ETF的头寸中记入分红
                    positions.Last().Value["510050.SH"].totalCashFlow += positions.Last().Value["510050.SH"].volume * bonusOf50ETFDividend2016;
                    //期权持仓行权价调整
                    foreach (var item in optionInfoList)
                    {
                        if (item.optionCode == holdingStatus.callCode)
                        {
                            holdingStatus.strike        = item.strike;
                            holdingStatus.callPosition *= item.contractMultiplier / standardContractMultiplier;
                            holdingStatus.putPosition  *= item.contractMultiplier / standardContractMultiplier;
                        }
                    }
                }
                Dictionary <string, MinuteSignal> signal = new Dictionary <string, MinuteSignal>();
                double fractile90Yesterday = fractile[i - 1][9]; //昨日历史波动率90分位数
                double fractile70Yesterday = fractile[i - 1][7]; //昨日历史波动率70分位数
                double fractile50Yesterday = fractile[i - 1][5]; //昨日历史波动率50分位数
                double fractile30Yesterday = fractile[i - 1][3]; //昨日历史波动率30分位数
                double volYesterday        = etfVol[i - 1];      //昨日历史波动率
                double impvYesterday       = optionVol[i - 1];   //昨日隐含波动率
                //获取当日ATM期权合约代码

                double etfPrice = etfDailyData[i].close;
                double duration;
                //获取当日期限结构,选取当月合约,若当日合约到日期小于等于3天,直接开仓下月合约
                List <double> dateStructure = OptionUtilities.getDurationStructure(optionInfoList, today);
                double        duration0     = dateStructure[0] <= 3 ? dateStructure[1] : dateStructure[0];
                duration = duration0;
                var call      = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认购").OrderBy(x => Math.Abs(x.strike - etfPrice)).Where(x => x.startDate <= today).ToList();
                var callATM   = call[0];
                var callPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(callATM.optionCode, today);
                // double callImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(callATM.strike, duration / 252.0, 0.04, 0, callATM.optionType, callPrice[0].close, etfPrice);
                var put      = OptionUtilities.getOptionListByOptionType(OptionUtilities.getOptionListByDuration(optionInfoList, today, duration), "认沽").OrderBy(x => Math.Abs(x.strike - callATM.strike)).ToList();
                var putATM   = put[0];
                var putPrice = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(putATM.optionCode, today);
                //double putImpv = ImpliedVolatilityUtilities.ComputeImpliedVolatility(putATM.strike, duration / 252.0, 0.04, 0, putATM.optionType, putPrice[0].close, etfPrice);
                //整合当日分钟线数据
                Dictionary <string, List <KLine> > dataToday = new Dictionary <string, List <KLine> >();
                var etfData = Platforms.container.Resolve <StockMinuteRepository>().fetchFromLocalCsvOrWindAndSave("510050.SH", today);
                dataToday.Add("510050.SH", etfData.Cast <KLine>().ToList());
                dataToday.Add(callATM.optionCode, callPrice.Cast <KLine>().ToList());
                dataToday.Add(putATM.optionCode, putPrice.Cast <KLine>().ToList());

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

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

                        MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint);
                        holdingStatus = new Straddle();
                    }
                    if (DateUtils.GetSpanOfTradeDays(today, holdingStatus.endDate) <= 3) //有仓位无信号,判断是否移仓
                    {
                        Console.WriteLine("平仓!");
                        MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, openIndex, slipPoint);
                        holdingStatus = new Straddle();
                    }
                }
                //指定开仓时间为开盘第10分钟。错开开平仓的时间。
                openIndex = 10;
                now       = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), openIndex);
                if (holdingStatus.callPosition == 0 && orignalSignal != 0) //无仓位有信号,开仓
                {
                    if (orignalSignal == 1)                                //做多跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].open, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, minuteIndex = openIndex
                        };
                        Console.WriteLine("做多跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = optionVolume;
                        holdingStatus.putPosition        = optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].open;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].close + putPrice[openIndex].open;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    else if (orignalSignal == -1) //做空跨式期权
                    {
                        MinuteSignal openSignalCall = new MinuteSignal()
                        {
                            code = callATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = callPrice[openIndex].open, minuteIndex = openIndex
                        };
                        MinuteSignal openSignalPut = new MinuteSignal()
                        {
                            code = putATM.optionCode, volume = -optionVolume, time = now, tradingVarieties = "option", price = putPrice[openIndex].open, minuteIndex = openIndex
                        };
                        Console.WriteLine("做空跨式期权!");
                        signal.Add(callATM.optionCode, openSignalCall);
                        signal.Add(putATM.optionCode, openSignalPut);
                        //变更持仓状态
                        holdingStatus.callCode           = callATM.optionCode;
                        holdingStatus.putCode            = putATM.optionCode;
                        holdingStatus.callPosition       = -optionVolume;
                        holdingStatus.putPosition        = -optionVolume;
                        holdingStatus.etfPrice_open      = etfData[openIndex].open;
                        holdingStatus.straddlePrice_open = callPrice[openIndex].open + putPrice[openIndex].open;
                        holdingStatus.straddleOpenDate   = today;
                        holdingStatus.endDate            = callATM.endDate;
                        holdingStatus.strike             = callATM.strike;
                    }
                    MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: now, nowIndex: openIndex);
                }
                //每日收盘前,整理持仓情况
                int    thisIndex = 239;
                double delta     = 0;
                var    thisTime  = TimeListUtility.IndexToMinuteDateTime(Kit.ToInt_yyyyMMdd(today), thisIndex);
                if (today >= timeOf50ETFDividend2016)
                {
                    benchmark.Add(etfData[thisIndex].close + bonusOf50ETFDividend2016);
                }
                else
                {
                    benchmark.Add(etfData[thisIndex].close);
                }
                if (holdingStatus.callPosition != 0)
                {
                    if (dataToday.ContainsKey(holdingStatus.callCode) == false)
                    {
                        var callLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.callCode, today);
                        dataToday.Add(holdingStatus.callCode, callLastDay.Cast <KLine>().ToList());
                    }
                    if (dataToday.ContainsKey(holdingStatus.putCode) == false)
                    {
                        var putLastDay = Platforms.container.Resolve <OptionMinuteRepository>().fetchFromLocalCsvOrWindAndSave(holdingStatus.putCode, today);
                        dataToday.Add(holdingStatus.putCode, putLastDay.Cast <KLine>().ToList());
                    }
                    //计算期权delta值,并用50ETF对冲
                    var positionLast = positions.Last().Value;
                    delta = computeOptionDelta(positionLast, holdingStatus, today, dataToday, thisIndex);
                    double       etfChangeVolume = Math.Round(-delta - holdingStatus.etfPosition);
                    MinuteSignal openSignalETF   = new MinuteSignal()
                    {
                        code = "510050.SH", volume = etfChangeVolume, time = thisTime, tradingVarieties = "stock", price = dataToday["510050.SH"][thisIndex].open, minuteIndex = thisIndex
                    };
                    signal = new Dictionary <string, MinuteSignal>();
                    signal.Add("510050.SH", openSignalETF);
                    MinuteTransactionWithBar.ComputePosition(signal, dataToday, ref positions, ref myAccount, slipPoint: slipPoint, now: thisTime, nowIndex: thisIndex);
                    holdingStatus.etfPosition += etfChangeVolume;
                    // AccountUpdatingForMinute.computeAccountUpdating(ref myAccount, positions, thisTime, dataToday);
                }

                //更新当日属性信息
                AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, thisTime, thisIndex, dataToday);

                //记录历史仓位信息
                accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                //在控制台上数据每日持仓信息
                if (holdingStatus.callPosition != 0)
                {
                    Console.WriteLine("time: {0},etf: {1}, strike: {2}, position: {3}, call: {4}, put: {5}, endDate: {6}, delta: {7}, etfVolume: {8}", thisTime, etfData[thisIndex].close, holdingStatus.strike, holdingStatus.callPosition, dataToday[holdingStatus.callCode][thisIndex].close, dataToday[holdingStatus.putCode][thisIndex].close, holdingStatus.endDate, delta, holdingStatus.etfPosition);
                }
                //Console.WriteLine("time: {0}, total: {1}, cash: {2}, option: {3}, margin: {4}", thisTime, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin);
            }
            //策略绩效统计及输出
            PerformanceStatisics myStgStats = new PerformanceStatisics();

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

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

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

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

            line.Add("Base", netWorthOfBenchmark.ToArray());
            string[] datestr = accountHistory.Select(a => a.time.ToString("yyyyMMdd")).ToArray();
            Application.Run(new PLChart(line, datestr));
        }
예제 #3
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());
        }
예제 #4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="signal">交易信号(正向/反向)</param>
        /// <param name="data">KLine格式的交易数据</param>
        /// <param name="positions">头寸信息</param>
        /// <param name="myAccount">账户信息</param>
        /// <param name="now">交易日的时间信息</param>
        /// <param name="nowIndex">当前索引值(不知道什么意思)</param>
        /// <param name="slipPoint">滑点</param>
        /// <returns></returns>
        public static Dictionary <string, ExecutionReport> ComputePosition(Dictionary <string, MinuteSignal> signal, Dictionary <string, List <KLine> > data, ref SortedDictionary <DateTime, Dictionary <string, PositionsWithDetail> > positions, ref BasicAccount myAccount, DateTime now, int nowIndex, double slipPoint = 0.00)
        {
            //初始化记录成交回报的变量
            Dictionary <string, ExecutionReport> tradingFeedback = new Dictionary <string, ExecutionReport>();
            //初始化上一次头寸记录时间
            DateTime lastTime = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);

            //如果signal无信号,无法成交,直接返回空的成交回报。
            if (signal == null || signal.Count == 0)
            {
                return(tradingFeedback);
            }
            if (positions.Count != 0)
            {
                lastTime = positions.Keys.Last();
            }
            //新建头寸变量,作为接受新仓位的容器
            Dictionary <string, PositionsWithDetail> positionShot = new Dictionary <string, PositionsWithDetail>();

            //如果持仓最后状态时间大于signal信号的时间,无成交,直接返回空的成交回报。
            if (lastTime > now)
            {
                return(tradingFeedback);
            }
            //如果两者时间相等,则把总仓位变化数组positions中的最后一项,添加进新仓位的容器positionShot中
            if (now == lastTime)
            {
                positionShot = positions[positions.Keys.Last()];
            }
            //如果交易信号时间在最后一次持仓变化时间点之后,则需要重新把最后持仓的仓位变化信息手工copy一份;
            //然后添加进新仓位的容器positionShot中。
            else if (positions.Count > 0)                              //如果持仓大于0
            {
                foreach (var item in positions[positions.Keys.Last()]) //循环 持仓最后状态时间的持仓数据
                {
                    //这里必须手动copy一份,不可以直接传引用。因为最后持仓变化节点的仓位信息是不应该被改变的;
                    //如果直接传引用,交易信号时间点仓位变化会同时改变最后持仓变化节点的仓位信息。
                    PositionsWithDetail position0 = new PositionsWithDetail().myClone(item.Value);//复制一份新的
                    positionShot.Add(position0.code, position0);
                }
            }
            //获取前一步的头寸信息,如果没有寸头就设为null
            Dictionary <string, PositionsWithDetail> positionLast = (positions.Count == 0 ? null : positions[positions.Keys.Last()]);

            //对信号进行遍历
            foreach (var signal0 in signal.Values)
            {
                //整理成交信号,剔除不合理信号。
                //①信号触发时间必须在positionLast的记录时间之后,在当前时间now之前。
                //②信号必须有合理的交易数量。
                //③信号必须有对应的数据。
                if (signal0.time != now) //【???】不是说必须要在当前时间now之前么
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.status = "交易时间错误,无效信号";
                    tradingFeedback.Add(signal0.code, report0);
                    continue;
                }
                if (signal0.volume == 0 || signal0.price == 0)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.status = "交易数据错误,无效信号";
                    tradingFeedback.Add(signal0.code, report0);
                    continue;
                }
                if (data.ContainsKey(signal0.code) == false)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.status = "无法找到行情数据,无效信号";
                    tradingFeedback.Add(signal0.code, report0);
                    continue;
                }

                //根据K线来判断成交数量,有可能完全成交,也有可能部分成交
                //开多头时,如果价格大于最低价,完全成交,否者不成交
                //开空头时,如果价格小于最高价,完全成交,否者不成交

                //找出对应的K线
                KLine KLineData = data[signal0.code][nowIndex];
                //确定滑点
                double slip = Math.Max(slipPoint, signal0.bidAskSpread);

                //开多头时,如果价格大于最低价,完全成交,否者不成交
                if (signal0.volume > 0 && signal0.price >= KLineData.low)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.volume = signal0.volume;
                    report0.price  = signal0.price + slip;
                    report0.status = "完全成交";
                    tradingFeedback.Add(signal0.code, report0);
                }
                if (signal0.volume > 0 && signal0.price < KLineData.low)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.price  = signal0.price + slip;
                    report0.status = "无法成交";
                    tradingFeedback.Add(signal0.code, report0);
                    continue;
                }

                //开空头时,如果价格小于最高价,完全成交,否者不成交
                if (signal0.volume < 0 && signal0.price <= KLineData.high)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.price  = signal0.price - slip;
                    report0.volume = signal0.volume;
                    report0.status = "完全成交";
                    tradingFeedback.Add(signal0.code, report0);
                }
                if (signal0.volume < 0 && signal0.price > KLineData.high)
                {
                    ExecutionReport report0 = new ExecutionReport();
                    report0.code   = signal0.code;
                    report0.time   = signal0.time;
                    report0.price  = signal0.price - slip;
                    report0.status = "无法成交";
                    tradingFeedback.Add(signal0.code, report0);
                    continue;
                }

                ///【解释:以下部分 position,positionShot和position0的关系】
                /// position:是传入的参数,
                ///

                //接下来处理能够成交的signal0,信号下单的时间只能是lastTime或者now。
                PositionsWithDetail position0 = new PositionsWithDetail();
                //查询当前持仓数量
                double nowHoldingVolume;
                //当前证券已有持仓
                if (positionShot.Count > 0 && positionShot.ContainsKey(signal0.code))
                {
                    //将当前证券持仓情况赋值给临时持仓变量
                    position0        = positionShot[signal0.code];
                    nowHoldingVolume = position0.volume;
                }
                else //若历史无持仓
                {
                    //当前信号证券代码
                    position0.code = signal0.code;
                    //多空头寸初始化
                    position0.LongPosition  = new PositionDetail();
                    position0.ShortPosition = new PositionDetail();
                    position0.record        = new List <TransactionRecord>();
                    nowHoldingVolume        = 0;
                    positionShot.Add(position0.code, position0);
                }
                //持仓和开仓方向一致
                if (nowHoldingVolume * signal0.volume >= 0)
                {
                    //开多仓
                    if (signal0.volume > 0)
                    {
                        //重新计算仓位和价格
                        double volume       = signal0.volume + position0.LongPosition.volume;
                        double cost         = (signal0.price + slip) * signal0.volume + position0.LongPosition.volume * position0.LongPosition.averagePrice;
                        double averagePrice = cost / volume;
                        position0.LongPosition = new PositionDetail {
                            volume = volume, totalCost = cost, averagePrice = averagePrice
                        };
                    }
                    else //开空仓
                    {
                        //重新计算仓位和价格
                        double volume       = signal0.volume + position0.ShortPosition.volume;
                        double cost         = (signal0.price - slip) * signal0.volume + position0.ShortPosition.volume * position0.ShortPosition.averagePrice;
                        double averagePrice = cost / volume;
                        position0.ShortPosition = new PositionDetail {
                            volume = volume, totalCost = cost, averagePrice = averagePrice
                        };
                    }
                }
                else //持仓和开仓方向不一致
                {
                    if (nowHoldingVolume > 0) //原先持有多头头寸,现开空仓
                    {
                        //计算总头寸,分类讨论
                        double volume = signal0.volume + position0.LongPosition.volume;
                        if (volume > 0)
                        {
                            position0.LongPosition.volume    = volume;
                            position0.LongPosition.totalCost = position0.LongPosition.volume * position0.LongPosition.averagePrice;
                        }
                        else if (volume < 0)
                        {
                            position0.ShortPosition.volume       = volume;
                            position0.ShortPosition.totalCost    = volume * (signal0.price - slip);
                            position0.ShortPosition.averagePrice = (signal0.price - slip);
                            position0.LongPosition = new PositionDetail();
                        }
                        else
                        {
                            position0.LongPosition = new PositionDetail();
                        }
                    }
                    else //原先持有空头头寸,现开多仓
                    {
                        //计算总头寸,分类讨论
                        double volume = signal0.volume + position0.ShortPosition.volume;
                        if (volume < 0)
                        {
                            position0.ShortPosition.volume    = volume;
                            position0.ShortPosition.totalCost = position0.ShortPosition.volume * position0.ShortPosition.averagePrice;
                        }
                        else if (volume > 0)
                        {
                            position0.LongPosition.volume       = volume;
                            position0.LongPosition.averagePrice = (signal0.price + slip);
                            position0.LongPosition.totalCost    = (signal0.price - slip) * volume;
                            position0.ShortPosition             = new PositionDetail();
                        }
                        else
                        {
                            position0.ShortPosition = new PositionDetail();
                        }
                    }
                }
                //更新其他信息
                position0.record.Add(new TransactionRecord
                {
                    time   = now,
                    volume = signal0.volume,
                    price  = signal0.price + slip * (signal0.volume > 0?1:-1),
                    code   = position0.code
                });
                position0.totalCashFlow    = position0.totalCashFlow - (signal0.price + (signal0.volume > 0 ? 1 : -1) * slip) * signal0.volume;
                position0.transactionCost  = position0.transactionCost + Math.Abs(slip * signal0.volume);
                position0.volume           = position0.volume + signal0.volume;
                position0.time             = now;
                position0.code             = signal0.code;
                position0.currentPrice     = KLineData.close;
                position0.tradingVarieties = signal0.tradingVarieties;
                //总资产=权益现值+现金盈亏
                position0.totalAmt = position0.totalCashFlow + position0.volume * position0.currentPrice;
            }
            if (now > lastTime)
            {
                positions.Add(now, positionShot);
            }
            //更新持仓的头寸信息
            if (positions.Count != 0)
            {
                AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, now, nowIndex, data);
            }
            return(tradingFeedback);
        }