/// <summary> /// 根据个股主力线高位卖出 /// </summary> /// <param name="bouts"></param> /// <param name="ds"></param> /// <param name="strategyParam"></param> /// <param name="backtestParam"></param> public void DoSell2(TradeRecords tradeRecords, TimeSerialsDataSet ds, Properties strategyParam, BacktestParameter backtestParam) { if (tradeRecords == null || tradeRecords.Bouts == null || tradeRecords.Bouts.Count <= 0) { return; } TimeSeries <ITimeSeriesItem <List <double> > > dayFunds = ds.DayFundTrend; KLine dayLine = ds.DayKLine; if (dayLine == null) { return; } foreach (TradeBout bout in tradeRecords.Bouts) { DateTime buyDate = bout.BuyInfo.TradeDate; //找20个工作日的收盘价最高值 KLineItem klineItem = dayLine.GetNearest(buyDate, false); if (klineItem == null) { continue; } int index = dayLine.IndexOf(klineItem); DateTime sellDate = buyDate; double sellPrice = 0; for (int i = index + 1; i < Math.Min(index + 41, dayLine.Count); i++) { if (dayLine[i].CLOSE > sellPrice) { sellPrice = dayLine[i].CLOSE; sellDate = dayLine[i].Date; } } bout.RecordTrade(2, sellDate, TradeDirection.Sell, sellPrice, bout.BuyInfo.Amount, 0, 0, ""); } }
public void Execute() { List <String> codes = new List <string>(); System.IO.File.ReadAllLines(FileUtils.GetDirectory() + "test.csv") .ToList().ForEach(x => codes.Add(x.Split(',')[1])); IndicatorRepository repository = new IndicatorRepository("d:\\repository\\"); repository.Initilization(); foreach (String code in codes) { //生成数据 TimeSerialsDataSet ds = repository[code]; KLine dayLine = ds.DayKLine; KLine weekLine = dayLine.CreateWeek(); ds.WeekKLine = weekLine; TimeSeries <ITimeSeriesItem <double> > dayClose = dayLine.Select <double>("close", 0, 0); TimeSeries <ITimeSeriesItem <double> > weekClose = weekLine.Select <double>("close", 0, 0); TradingLine dayTradeLine = ds.CubeCreateOrLoad(TimeUnit.day); TradingLine weekTradeLine = ds.CubeCreateOrLoad(TimeUnit.week); TimeSeries <ITimeSeriesItem <List <double> > > dayFunds = ds.FundTrendCreate(TimeUnit.day); TimeSeries <ITimeSeriesItem <List <double> > > weekFunds = ds.FundTrendCreate(TimeUnit.week); TimeSeries <ITimeSeriesItem <double> > dayCross = ds.FundTrendCrossCreateOrLoad(TimeUnit.day); TimeSeries <ITimeSeriesItem <double> > weedCross = ds.FundTrendCrossCreateOrLoad(TimeUnit.week); //测试买入 List <TradeBout> bouts = new List <TradeBout>(); TimeSeries <ITimeSeriesItem <char> > dayTradePt = dayTradeLine.buysellPoints; for (int i = 0; i < dayTradePt.Count; i++) { ITimeSeriesItem <char> item = dayTradePt[i]; if (item.Value == 'S') { continue; } if (item.Date < begin || item.Date > end) { continue; } DateTime buyPtDate = item.Date; int index = dayFunds.IndexOf(buyPtDate); while (index <= dayFunds.Count) { ITimeSeriesItem <List <double> > fundItem = dayFunds[index]; if (fundItem == null) { index += 1; continue; } if (fundItem.Value[0] <= fundItem.Value[1]) { index += 1; continue; } TradeBout bout = new TradeBout(code); KLineItem klineItem = dayLine.GetNearest(fundItem.Date, false); if (klineItem == null) { index += 1; continue; } bout.RecordTrade(1, klineItem.Date, TradeDirection.Buy, klineItem.CLOSE, (int)(funds / klineItem.CLOSE), 0, 0, "发出B点且主力=" + fundItem.Value[0].ToString("F3") + "大于散户" + fundItem.Value[1].ToString("F3") + ",日期=" + fundItem.Date.ToString("yyyyMMdd")); bouts.Add(bout); break; } } //测试卖出 for (int i = 0; i < bouts.Count; i++) { DateTime buyDate = bouts[i].BuyInfo.TradeDate; int buyIndex = dayLine.IndexOf(buyDate); int index = buyIndex + 1; while (index <= dayLine.Count - 1) { KLineItem item = dayLine[index]; if (index - buyIndex >= maxdays) { bouts[i].RecordTrade(2, item.Date, TradeDirection.Sell, item.CLOSE, bouts[i].BuyInfo.Amount, 0, 0, "大于" + maxdays.ToString() + "天卖出"); break; } else { double profile = (item.HIGH - bouts[i].BuyInfo.TradePrice) / bouts[i].BuyInfo.TradePrice; if (profile >= maxProfilt) { bouts[i].RecordTrade(2, item.Date, TradeDirection.Sell, (bouts[i].BuyInfo.TradePrice * (1 + maxProfilt)), bouts[i].BuyInfo.Amount, 0, 0, "利润大于" + maxdays.ToString() + "天卖出"); break; } } index += 1; } } //去掉未完成的 for (int i = 0; i < bouts.Count; i++) { if (!bouts[i].Completed) { bouts.RemoveAt(i--); } } TradeRecords tradeRecords = new TradeRecords(); tradeRecords.Bouts.AddRange(bouts); //打印结果 for (int i = 0; i < bouts.Count; i++) { Console.WriteLine(bouts[i].ToString()); } Console.WriteLine(tradeRecords.ToString()); } }
/// <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); }