Ejemplo n.º 1
0
        /// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static TradeRecords Load(String filename)
        {
            String[] lines = System.IO.File.ReadAllLines(filename);
            if (lines == null || lines.Length <= 0)
            {
                return(new TradeRecords());
            }

            TradeRecords records = new TradeRecords();

            foreach (String line in lines)
            {
                TradeBout bout = TradeBout.Parse(line);
                if (bout != null)
                {
                    records.bouts.Add(bout);
                }
            }
            return(records);
        }
Ejemplo n.º 2
0
 /// <summary>
 /// 判断指定的回合是否要在d这天卖出
 /// </summary>
 /// <param name="code"></param>
 /// <param name="bout"></param>
 /// <param name="d"></param>
 /// <param name="strategyParam"></param>
 /// <param name="backtestParam"></param>
 /// <returns>是否进入择机卖出</returns>
 public abstract bool DoSell(String code, TradeBout bout, DateTime d, Properties strategyParam, BacktestParameter backtestParam, out String reason);
Ejemplo n.º 3
0
        /// <summary>
        /// 执行卖出操作
        /// </summary>
        /// <param name="tradeRecord"></param>
        /// <param name="strategyParam"></param>
        /// <param name="backtestParam"></param>
        public virtual void Execute(TradeRecords tradeRecord, Properties strategyParam, BacktestParameter backtestParam)
        {
            #region 1初始化行情库
            if (tradeRecord == null)
            {
                return;
            }

            IndicatorRepository repository = (IndicatorRepository)backtestParam.Get <Object>("repository");
            if (repository == null)
            {
                return;
            }

            #endregion

            #region 2 取得策略参数

            int    p_maxbuynum      = strategyParam.Get <int>("maxbuynum", 0);
            double p_maxprofilt     = strategyParam.Get <double>("maxprofilt");
            int    p_maxholddays    = strategyParam.Get <int>("maxholddays");
            double p_stoploss       = strategyParam.Get <double>("stoploss");
            int    p_choosedays     = strategyParam.Get <int>("choosedays");
            double p_chooseprofilt  = strategyParam.Get <double>("chooseprofilt");
            double p_addholdprofilt = strategyParam.Get <double>("addholdprofilt");
            double p_addholdamount  = strategyParam.Get <double>("addholdamount");
            #endregion


            String           code  = tradeRecord.Code;
            List <TradeBout> bouts = tradeRecord.Bouts;

            #region 3 遍历每一个买入回合
            for (int i = 0; i < bouts.Count; i++)
            {
                #region 3.1 取得该回合的行情数据
                TradeBout          bout = bouts[i];
                TimeSerialsDataSet ds   = repository[bout.Code];
                if (ds == null)
                {
                    continue;
                }

                if (bout.Completed)
                {
                    continue;                //跳过已完成的
                }
                KLine kline = ds.DayKLine;
                if (kline == null)
                {
                    continue;
                }

                bool chooseToSell = false;//择机卖出状态,是指持仓价值较低

                int bIndex = kline.IndexOf(bout.BuyInfo.TradeDate);
                if (bIndex < 0)
                {
                    continue;
                }
                KLineItem klineItemDay = kline[bIndex];
                DateTime  d            = klineItemDay.Date;


                #endregion

                #region 3.2 如果超过了最大持仓数限制,该回合跳过
                if (p_maxbuynum > 0)
                {
                    //计算当前回合的买入日期这天有多少持仓
                    int count = 0;
                    bouts.ForEach(x => { if (x.Completed && bout.BuyInfo.TradeDate.Date >= x.BuyInfo.TradeDate.Date && bout.BuyInfo.TradeDate.Date < x.SellInfo.TradeDate.Date)
                                         {
                                             count++;
                                         }
                                  });

                    if (count > p_maxbuynum)
                    {
                        continue;
                    }
                }
                #endregion


                #region 3.3 寻找卖点
                String reason = "";
                for (int index = bIndex + 1; index < kline.Count; index++)
                {
                    klineItemDay = kline[index];
                    d            = klineItemDay.Date;


                    #region A 计算以当日最高价和收盘价卖出的盈利
                    double diff        = klineItemDay.HIGH - bout.BuyInfo.TradePrice;
                    double percentHigh = diff / bout.BuyInfo.TradePrice;
                    diff = klineItemDay.CLOSE - bout.BuyInfo.TradePrice;
                    double percentClose = diff / bout.BuyInfo.TradePrice;
                    #endregion

                    #region B 盈利超过预定
                    if (p_maxprofilt > 0 && percentHigh >= p_maxprofilt) //盈利超过预定
                    {
                        double price  = bout.BuyInfo.TradePrice * (1 + p_maxprofilt);
                        int    amount = bout.BuyInfo.Amount;
                        bout.RecordTrade(2, klineItemDay.Date, TradeDirection.Sell, price, amount, backtestParam.Volumecommission, backtestParam.Stampduty, "盈利>=" + p_maxprofilt.ToString("F2"));
                        break;
                    }
                    #endregion

                    #region C 择机卖出状态
                    if (chooseToSell)
                    {
                        for (int t = 0; t < p_choosedays; t++)
                        {
                            index += t;
                            if (index >= kline.Count)
                            {
                                break;
                            }
                            double percent = (kline[index].HIGH - bout.BuyInfo.TradePrice) / bout.BuyInfo.TradePrice;
                            if (percent >= p_chooseprofilt)
                            {
                                double price  = bout.BuyInfo.TradePrice * (1 + p_chooseprofilt);
                                int    amount = bout.BuyInfo.Amount;
                                bout.RecordTrade(2, kline[index].Date, TradeDirection.Sell, price, amount, backtestParam.Volumecommission, backtestParam.Stampduty, (reason == "" ? "" : reason + ",并") + "在第" + (t + 1).ToString() + "天择机卖出");
                                break;
                            }
                        }
                        if (!bout.Completed)
                        {
                            if (index >= kline.Count)
                            {
                                return;
                            }
                            double price  = kline[index].CLOSE;
                            int    amount = bout.BuyInfo.Amount;
                            bout.RecordTrade(2, kline[index].Date, TradeDirection.Sell, price, amount, backtestParam.Volumecommission, backtestParam.Stampduty, (reason == "" ? "" : reason + ",并") + "择机强制卖出");
                        }
                        break;
                    }
                    #endregion

                    #region D 持仓超过n天进入到择机卖出状态
                    if (p_maxholddays != 0 && CalendarUtils.WorkDayCount(bout.BuyInfo.TradeDate, klineItemDay.Date) >= p_maxholddays)
                    {
                        reason       = "持仓超过" + p_maxholddays.ToString() + "天";
                        chooseToSell = true;
                        continue;
                    }
                    #endregion


                    #region E 达到止损线,进入到择机卖出状态
                    double loss = (klineItemDay.LOW - bout.BuyInfo.TradePrice) / bout.BuyInfo.TradePrice;
                    if (p_stoploss > 0 && loss < 0 && loss < -1 * p_stoploss)
                    {
                        reason = "达到止损" + p_stoploss.ToString("F2");
                        bout.RecordTrade(2, d, TradeDirection.Sell, bout.BuyInfo.TradePrice * (1 - p_stoploss), bout.BuyInfo.Amount, backtestParam.Volumecommission, backtestParam.Stampduty, reason);
                        break;
                    }
                    #endregion

                    #region F调用子类的算法来寻找卖点
                    if (!bout.Completed)
                    {
                        chooseToSell = DoSell(code, bout, d, strategyParam, backtestParam, out reason);
                    }
                    if (bout.Completed)
                    {
                        break;
                    }
                    #endregion

                    #region 判断是否加仓或者减仓
                    if (p_addholdprofilt > 0 && p_addholdamount > 0)
                    {
                        if (percentClose > p_addholdprofilt)
                        {
                            int       addamount = (int)(bout.BuyInfo.Amount * p_addholdamount);
                            TradeInfo tradeInfo = new TradeInfo();
                            tradeInfo.Code       = bout.Code;
                            tradeInfo.Amount     = addamount;
                            tradeInfo.Direction  = TradeDirection.Buy;
                            tradeInfo.Reason     = "加仓";
                            tradeInfo.Fee        = backtestParam.Volumecommission;
                            tradeInfo.Stamps     = backtestParam.Stampduty;
                            tradeInfo.TradeDate  = klineItemDay.Date;
                            tradeInfo.TradePrice = klineItemDay.CLOSE;

                            bout.BuyInfo.TradePrice = (bout.BuyInfo.Amount * bout.BuyInfo.TradePrice + addamount * klineItemDay.CLOSE) / (bout.BuyInfo.Amount + addamount);
                            bout.BuyInfo.Amount    += addamount;
                        }
                    }
                    #endregion
                }

                #endregion //寻找卖点结束
            }

            #endregion //遍历每一个买入回合结束
        }
Ejemplo n.º 4
0
        /// <summary>
        /// 执行回测
        /// </summary>
        /// <param name="props"></param>
        /// <returns></returns>
        public virtual TotalStat doTestByDate(List <TradeRecords> tradeRecoreds)
        {
            List <TradeBout> bouts = new List <TradeBout>();

            tradeRecoreds.ForEach(x => bouts.AddRange(x.Bouts));

            double   marketValueMin         = backtestParam.Initfunds; //日最低市值
            double   marketValueMax         = backtestParam.Initfunds; //日最高市值
            double   lastmarketValueMax     = backtestParam.Initfunds; //上一个日最高市值
            DateTime lastmarketValueMaxDate = backtestParam.BeginDate;
            double   curFund = backtestParam.Initfunds;                //当前资金

            TotalStat stat = new TotalStat();
            List <DateDetailRecord> records    = new List <DateDetailRecord>(); //日详细记录
            List <TradeBout>        holdTrades = new List <TradeBout>();        //日持仓回合
            List <int>    holdDays             = new List <int>();              //持仓日期
            List <String> codes     = new List <string>();                      //交易的股票代码
            List <int>    buyCounts = new List <int>();                         //每天买入的回合数

            IndicatorRepository repository = (IndicatorRepository)backtestParam.Get <Object>("repository");
            String reason = "";

            //遍历每一天
            for (DateTime d = backtestParam.BeginDate; d <= backtestParam.EndDate; d = d.AddDays(1))
            {
                //跳过非工作日
                //if (!CalendarUtils.IsWorkDay(d))
                //     continue;


                //生成空的当日记录
                DateDetailRecord record = new DateDetailRecord();
                record.date = d;
                //找到当日的买入回合、卖出回合
                bouts.ForEach(y =>
                {
                    if (y.BuyInfo.TradeDate.Date == d.Date)
                    {
                        record.buyBouts.Add(y);
                    }
                    else if (y.SellInfo.TradeDate.Date == d.Date)
                    {
                        record.sellBouts.Add(y);
                    }
                });

                //当日没有发生买卖操作,也没有持仓,跳过
                if (record.buyBouts.Count <= 0 && record.sellBouts.Count <= 0 && holdTrades.Count <= 0)
                {
                    continue;
                }



                //将buyTrades按照优先规则排序,待实现

                //计算当日买入的花销,如果超过了资金允许买入的量,则删除一部分
                record.willBuyCount = record.buyBouts.Count;
                for (int i = 0; i < record.buyBouts.Count; i++)
                {
                    if (record.buyBouts[i].BuyInfo.TradeCost > curFund)//资金不够
                    {
                        bouts.Remove(record.buyBouts[i]);
                        record.buyBouts.RemoveAt(i--);
                    }
                    else if (isForbidBuy(d, record.buyBouts[i].Code, out reason))//如果策略实现禁止买入
                    {
                        bouts.Remove(record.buyBouts[i]);
                        record.buyBouts.RemoveAt(i--);
                    }
                    else
                    {
                        curFund -= record.buyBouts[i].BuyInfo.TradeCost; //买入
                        holdTrades.Add(record.buyBouts[i]);              //买入后变成持仓
                    }
                }
                record.buyCount = record.buyBouts.Count;
                if (stat.MaxTradeCountPerDay < record.buyBouts.Count)
                {
                    stat.MaxTradeCountPerDay = record.buyBouts.Count;
                }
                buyCounts.Add(record.buyBouts.Count);

                //判断持仓中的股票是否被禁止持仓
                for (int i = 0; i < holdTrades.Count; i++)
                {
                    TradeBout info = holdTrades[i];
                    if (!isForbidHold(d, info.Code, out reason))
                    {
                        continue;
                    }
                    info.SellInfo.Reason = reason + "(" + info.SellInfo.Reason + ")";
                    record.sellBouts.Add(info);
                    holdTrades.RemoveAt(i--);
                }


                //卖出收入放回资金
                for (int i = 0; i < record.sellBouts.Count; i++)
                {
                    if (!codes.Contains(record.sellBouts[i].Code))//记录交易的股票
                    {
                        codes.Add(record.sellBouts[i].Code);
                    }
                    stat.BoutNum += 1;           //回合数加1
                    if (record.sellBouts[i].Win) //胜数加1
                    {
                        stat.WinNum += 1;
                    }
                    holdDays.Add(record.sellBouts[i].PositionDays);    //记录持仓日期

                    curFund += record.sellBouts[i].SellInfo.TradeCost; //回收资金
                    holdTrades.Remove(record.sellBouts[i]);            //从持仓中拿掉
                }
                record.SellCount = record.sellBouts.Count;

                //计算市值
                record.holdCount = holdTrades.Count;
                if (holdTrades.Count <= 0)//如果没有持仓,市值就是资金量
                {
                    marketValueMax = marketValueMin = curFund;
                    records.Add(record);
                }
                else//如果有持仓,则计算市值=资金量+持仓当日市值
                {
                    double min = 0, max = 0;
                    foreach (TradeBout info in holdTrades)
                    {
                        TimeSerialsDataSet ds = repository[info.Code];
                        KLine     kline       = ds.DayKLine;
                        KLineItem klineitem   = kline.GetNearest(d, true, -1);
                        if (klineitem == null)//有一个回合找不到当日K线数据,则当日市值不再计算
                        {
                            min = max = 0;
                            this.log.Warn("日期" + d.ToString("yyyyMMdd") + "中有回合缺少当日和历史K线:" + info.Code);
                            break;
                        }
                        min += info.BuyInfo.Amount * klineitem.LOW;
                        max += info.BuyInfo.Amount * klineitem.HIGH;
                        record.holdBouts.Add(info.Code + "," + info.BuyInfo.Amount + "," + info.BuyInfo.TradePrice.ToString("F2") + "," + klineitem.CLOSE);
                    }
                    if (min != 0)
                    {
                        marketValueMin = curFund + min;
                    }
                    if (max != 0)
                    {
                        marketValueMax = curFund + max;
                    }
                    if (min != 0 && max != 0)
                    {
                        records.Add(record);
                    }
                }
                //记录资金和市值数据
                record.curFund        = curFund;
                record.marketValueMin = marketValueMin;
                record.marketValueMax = marketValueMax;
                if (marketValueMin < backtestParam.Initfunds)
                {
                    record.retracement = (backtestParam.Initfunds - marketValueMin) / backtestParam.Initfunds;
                }
                if (stat.MaxInitRetracementRate < record.retracement)
                {
                    stat.MaxInitRetracementRate = record.retracement;
                    stat.MaxInitRetracementDate = d;
                }
                if (marketValueMax > lastmarketValueMax)
                {
                    lastmarketValueMax     = marketValueMax;
                    lastmarketValueMaxDate = d;
                    record.retracement     = 0;
                }
                else
                {
                    record.retracement      = (lastmarketValueMax - marketValueMax) / lastmarketValueMax;
                    stat.MaxRetracementRate = record.retracement;
                    stat.MaxRetracementDate = d;
                }
            }

            //清除没有卖出的回合
            for (int i = 0; i < holdTrades.Count; i++)
            {
                curFund += holdTrades[i].BuyInfo.TradeCost;
                bouts.Remove(holdTrades[i]);
            }
            holdTrades.Clear();
            marketValueMin = marketValueMax = curFund;
            if (records.Count > 0)
            {
                DateDetailRecord extends = new DateDetailRecord();
                extends.date           = records[records.Count - 1].date.AddDays(1);
                extends.curFund        = curFund;
                extends.marketValueMax = marketValueMax;
                extends.marketValueMin = marketValueMin;
                records.Add(extends);
            }

            //结果统计
            stat.Records = records;

            stat.AverageTradeCountPerDay = buyCounts.Count <= 0?0:buyCounts.Average();
            stat.AvgHoldDays             = holdDays.Count <= 0?0:(int)holdDays.Average();
            stat.Count        = codes.Count;
            stat.MaxHoldDays  = holdDays.Count <= 0 ? 0 : holdDays.Max();
            stat.TotalFund    = curFund;
            stat.TotalProfilt = (curFund - backtestParam.Initfunds) / backtestParam.Initfunds;
            stat.WinRate      = stat.WinNum * 1.0 / stat.BoutNum;

            return(stat);
        }