/// <summary> /// 对代码集合进行回测 /// </summary> /// <param name="codes"></param> /// <returns></returns> protected virtual List <TradeRecords> doTestByCodes(List <String> codes) { if (codes == null || codes.Count <= 0) { return(new List <TradeRecords>()); } IndicatorRepository repository = (IndicatorRepository)backtestParam.Get <Object>("repository"); if (repository == null) { return(null); } if (buyer == null || Seller == null) { throw new Exception("买卖策略对象无效"); } List <TradeRecords> allTrades = new List <TradeRecords>(); foreach (String code in codes) { TimeSerialsDataSet ds = repository[code]; if (ds == null) { continue; } TradeRecords tr = buyer.Execute(code, props, backtestParam); if (tr == null || tr.Bouts.Count <= 0) { continue; } seller.Execute(tr, props, backtestParam); //最后删除未完成的回合 tr.RemoveUnCompeletedBouts(); if (tr.Bouts.Count > 0) { allTrades.Add(tr); tr.Print(log, true); //tr.Print(log,backtestParam.Get<bool>("printdetailed",false)); log.Info("总回合数=" + allTrades.Sum(x => x.Bouts.Count).ToString() + "," + "总胜率=" + (allTrades.Sum(x => x.WinCount) * 1.0 / allTrades.Sum(x => x.Bouts.Count)).ToString("F3")); } } return(allTrades); }
/// <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); }
/// <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 //遍历每一个买入回合结束 }