Beispiel #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>();
            //标记当日跨式组合多空信号。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));
        }
Beispiel #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>();
            //持仓量
            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
                        });
                    }
                }
            }
Beispiel #3
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;

            //第一层循环:所有交易日循环一遍...
            for (int i = 0; i < tradeDays.Count(); i++)
            {
                DateTime today = tradeDays[i];
                //找到当日参数
                FiveParameterPairs pair = parameters[today];
                frequency   = pair.frequency;
                numbers     = pair.numbers;
                longER      = pair.longER;
                shortER     = pair.shortER;
                lossPercent = pair.lossPercent;

                //从wind或本地CSV获取相应交易日的数据list
                //这里不能直接去调用FreqTransferUtils.minuteToNMinutes(data, frequency),因为data.count可能为0
                var dataOnlyToday = getData(today, underlying);                                //一个交易日里有多条分钟线数据
                var data          = getData(DateUtils.PreviousTradeDay(today, 3), underlying); //前3交易日的分钟线频率数据list
                data.AddRange(getData(DateUtils.PreviousTradeDay(today, 2), underlying));
                data.AddRange(getData(DateUtils.PreviousTradeDay(today, 1), underlying));

                int indexStart = data.Count();
                if (indexStart == 0) //前一天没数据
                {
                    indexStart = numbers;
                }
                //将当天的数据add到前一天的数据之后
                data.AddRange(dataOnlyToday);

                #region 20170118更新
                //将data转换成FuturesMinute分钟线频率
                //data= FreqTransferUtils.minuteToNMinutes(data, frequency);
                #endregion

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

                //第二层循环:只循环某当天的数据(开始的索引值为前一天数据的List.count)
                #region 第二层循环

                //这里减1:最后一个周期只平仓,不开仓
                for (int j = indexStart; j < data.Count() - 1; j++)
                {
                    DateTime now    = data[j].time;
                    double[] prices = new double[numbers];
                    for (int k = j - numbers; k < j; k++)
                    {
                        //导入收盘价
                        prices[k - (j - numbers)] = data[k].close;
                    }
                    //计算出ER值
                    double ER = computeER(prices);

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

                    //追踪止损判断 触发止损平仓
                    if (positionVolume != 0) //头寸量不为0,额外要做的操作
                    {
                        //计算开盘价和头寸当前价的差价
                        double incomeNow = individualIncome(positions.Last().Value[underlying], data[j].open);
                        //若当前收入大于最大收入值,则更新最大收入值
                        if (incomeNow > maxIncome)
                        {
                            maxIncome = incomeNow;
                        }
                        //若盈利回吐大于5个点 或者 最大收入大于45,则进行平仓
                        //&& ((positionVolume>0 && ER<longLevel) || (positionVolume<0 && ER>shortLevel))

                        else if ((maxIncome - incomeNow) > lossPercent * Math.Abs(data[j].open) || incomeNow < -lossPercent * Math.Abs(data[j].open)) //从最高点跌下来3%,就止损

                        {
                            positionVolume = 0;
                            Console.WriteLine("追踪止损!平仓价格: {0}", data[j].open);
                            MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                            maxIncome = 0;
                        }
                        //if (positionVolume>0 && ER<0 && incomeNow<-10)
                        //{
                        //    positionVolume = 0;
                        //    Console.WriteLine("信号止损!平仓价格: {0}", data[j].open);
                        //    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        //    maxIncome = 0;
                        //}
                        //else if (positionVolume < 0 && ER >0 && incomeNow < -10)
                        //{
                        //    positionVolume = 0;
                        //    Console.WriteLine("信号止损!平仓价格: {0}", data[j].open);
                        //    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, now, j, slipPoint);
                        //    maxIncome = 0;
                        //}
                    }

                    #endregion

                    if (ER >= longER && positionVolume == 0) //多头信号,无头寸,则开多仓

                    {
                        double volume = 1;
                        //长头寸信号
                        MinuteSignal longSignal = new MinuteSignal()
                        {
                            code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = data[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}", data[j].open);
                        //头寸量叠加
                        positionVolume += volume;
                        //单笔最大收益重置
                        maxIncome = 0;
                    }
                    else if (ER <= shortER && positionVolume == 0) //空头信号,无头寸,则开空仓
                    {
                        double volume = -1;
                        maxIncome = 0;
                        MinuteSignal shortSignal = new MinuteSignal()
                        {
                            code = underlying, volume = volume, time = now, tradingVarieties = "futures", price = data[j].open, minuteIndex = j
                        };
                        Console.WriteLine("做空期货!空头开仓价格: {0}", data[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);
                    }
                }

                #endregion

                int closeIndex = data.Count() - 1;

                if (positionVolume != 0)
                {
                    positionVolume = 0;
                    maxIncome      = 0;
                    MinuteCloseAllWithBar.CloseAllPosition(dataToday, ref positions, ref myAccount, data[closeIndex].time, closeIndex, slipPoint);
                    Console.WriteLine("{2}   每日收盘前强制平仓,平仓价格:{0},账户价值:{1}", data[closeIndex].open, myAccount.totalAssets, today);
                }
                if (data.Count > 0)
                {
                    //更新当日属性信息

                    AccountOperator.Minute.maoheng.AccountUpdatingWithMinuteBar.computeAccount(ref myAccount, positions, data.Last().time, data.Count() - 1, dataToday);

                    //记录历史仓位信息
                    accountHistory.Add(new BasicAccount(myAccount.time, myAccount.totalAssets, myAccount.freeCash, myAccount.positionValue, myAccount.margin, myAccount.initialAssets));
                    benchmark.Add(data.Last().close);
                    if (netValue.Count() == 0)
                    {
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = 0, benchmarkReturn = 0, netvalue = myAccount.totalAssets, benchmark = data.Last().close
                        });
                    }
                    else
                    {
                        var netValueLast = netValue.Last();
                        netValue.Add(new NetValue {
                            time = today, netvalueReturn = myAccount.totalAssets / netValueLast.netvalue - 1, benchmarkReturn = data.Last().close / netValueLast.benchmark - 1, netvalue = myAccount.totalAssets, benchmark = data.Last().close
                        });
                    }
                }
            }