/// <summary> /// 克隆 /// </summary> /// <returns></returns> public DateDetailRecord Clone() { DateDetailRecord r = new DateDetailRecord(); r.buyBouts.AddRange(this.buyBouts); r.buyCount = this.buyCount; r.curFund = this.curFund; r.date = this.date; r.holdBouts.AddRange(this.holdBouts); r.marketValueMax = marketValueMax; r.marketValueMin = marketValueMin; r.retracement = retracement; r.sellBouts.AddRange(this.sellBouts); r.SellCount = SellCount; r.willBuyCount = willBuyCount; return(r); }
/// <summary> /// 写详细记录 /// </summary> /// <param name="recordFileName">每天的交易摘要文件</param> /// <param name="boutFileName">每天的交易明细文件</param> public void WriteRecord(String recordFileName, String boutFileName) { List <String> recordLines = new List <string>(); List <String> boutLines = new List <string>(); recordLines.Add(DateDetailRecord.GetTitle()); for (int i = 0; i < this.records.Count; i++) { recordLines.Add(records[i].ToString()); boutLines.AddRange(records[i].ToDetailString()); } if (recordLines.Count > 0 && recordFileName != null && recordFileName != "") { System.IO.File.WriteAllLines(recordFileName, recordLines.ToArray()); } if (boutLines.Count > 0 && boutFileName != null && boutFileName != "") { System.IO.File.WriteAllLines(boutFileName, boutLines.ToArray()); } }
/// <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); }