/* Copyright (c) 2018 jones http://www.apache.org/licenses/LICENSE-2.0 开源项目 https://github.com/jones2000/HQChart jones_2000@163.com 个股指标回测 */ /* 指标回测 计算: Trade: {Count 交易次数 Days:交易天数 Success:成功交易次数 Fail:失败交易次数} Day: {Count:总运行 Max:最长运行 Min:最短运行 Average:平均运行} Profit: 总收益 StockProfit:个股收益 Excess:超额收益 MaxDropdown:最大回撤 Beta:β(Beta)系数(指标里面需要又大盘数据) NetValue: [ {Date:日期, Net:净值, Close:股票收盘价, IndexClose:大盘的收盘价}, ] */ function RegressionTest() { //只读数据不能修改 this.HistoryData; //K线数据 this.BuyData; //策略买数据 this.SellData; //策略卖数据 this.IndexClose; //大盘收盘价 this.NetCalculateModel=0; //净值及收益计算模型 0=使用B点开盘价计算 1=使用B点下一天的开盘价计算 this.InitialCapital=10000; //初始资金1W //计算结果数据 this.Data=new Map(); //key:DATA_NAME value:数据 this.SetPolicyData=function(obj) //设置策略结果的数据 {KLineData:个股K线数据, BuyData:策略买数据, SellData:策略卖数据, IndexClose:大盘收盘价} { this.HistoryData=obj.KLineData; //K线数据 this.BuyData=obj.BuyData; //策略买数据 this.SellData=obj.SellData; //策略卖数据 if (obj.IndexClose) this.IndexClose=obj.IndexClose; //大盘收盘价 如果没有大盘数据 就不计算β(Beta)系数 和指数涨幅数据 } this.ClearData=function() //清空所有的结果数据 { this.Data=new Map() } this.GetBSData=function(startDate) //BS点配对 { B:[{Data:K线数据, Count:天数, NextOpen:下一天的开盘价 }], S:{Data:K线数据}} { var index=null; for(var i=0;i=startDate) { index=i; break; } } if (index===null) return null; console.log(`[RegressionTest::GetBSData] startDate=${startDate} index=${index}`); var aryBS=[]; var bsItem=null; for(var i=index;i0) { var bItem={Data:kLineItem, Count:0 }; if (i+10) { var bItem={Data:kLineItem, Count:0}; if (i+10) { bsItem.S={Data:kLineItem}; aryBS.push(bsItem); bsItem=null; } } } var data={StartDate:this.HistoryData[index].Date, StartIndex:index, Count:this.HistoryData.length-index, BSData:aryBS }; console.log('[RegressionTest::GetBSData] data',data); return data; } this.Calculate=function(data) { var day={ Count:data.Count, Max:null, Min:null, Average:null }; //Count:总运行 Max:最长运行 Min:最短运行 Average:平均运行 var trade={Count:0, Days:0, Success:0 , Fail:0, SuccessRate:0}; //Count 交易次数 Days:交易天数 Success:成功交易次数 Fail:失败交易次数 for(var i in data.BSData) { var item=data.BSData[i]; for(var j in item.B) { var bItem=item.B[j]; if (day.Max===null) day.Max=bItem.Count; else if (day.MaxbItem.Count) day.Min=bItem.Count; ++trade.Count; trade.Days+=bItem.Count; if (item.S.Data.Close>bItem.Data.Open) ++trade.Success; else ++trade.Fail; } } if (trade.Count>0) { day.Average=trade.Days/trade.Count; trade.SuccessRate=trade.Success/trade.Count; } //计算收益(总收益) var profit=1,buyPrice; for(var i in data.BSData) { var item=data.BSData[i]; if (this.NetCalculateModel===1 && item.B[0].NextOpen>0 ) buyPrice=item.B[0].NextOpen; else buyPrice=item.B[0].Data.Open; var sellPrice=item.S.Data.Close; var value=(sellPrice-buyPrice)/buyPrice+1; profit*=value; } profit-=1; //公式:[(1+收益1)*(1+收益2)*(1+收益3)……(1+收益n)-1] x 100% //标的证券收益 var yClose=this.HistoryData[data.StartIndex].Close; //使用前收盘 var close=this.HistoryData[this.HistoryData.length-1].Close; //最后一个大盘收盘价 var stockProfit=(close-yClose)/yClose; console.log(`[RegressionTest::Calculate] stock profit first[${this.HistoryData[data.StartIndex].Date}, YClose=${this.HistoryData[data.StartIndex].YClose}] end[${this.HistoryData[this.HistoryData.length-1].Date}, Close=${this.HistoryData[this.HistoryData.length-1].Close}]`); var netValue=this.CaclulateNetValue(data); var maxDropdown=null, beta=null; if (netValue && netValue.length>0) { maxDropdown=this.CaclulateMaxDropdown(netValue); if (this.IndexClose) beta=this.CaclulateBeta(netValue); } //Profit:收益 StockProfit:标的证券收益 Excess:超额收益(加上BS配对的数据) var result={ Day:day, Trade:trade, Profit:profit, StockProfit:stockProfit, Excess:profit-stockProfit, NetValue:netValue, MaxDropdown:maxDropdown, Beta:beta,BSDataPair:data.BSData}; console.log('[RegressionTest::Calculate] NetCalculateModel, result ',this.NetCalculateModel, result); return result; } this.CaclulateNetValue=function(data) //计算净值 { var index=data.StartIndex; var aryDay=[]; //{Close:收盘 , Open:开盘, Position:持仓数量, Cache:现金 , MarketValue:总市值} var lastDayItem={Position:0, Cache:this.InitialCapital }; var bsItem=null, buyPrice; for(var i=index;i0) //买 { bsItem={ B:{Data:kLineItem}, S:null}; if (this.NetCalculateModel===1 && i+10) buyPrice=this.HistoryData[i+1].Open; //使用B点下一天的开盘价买 else buyPrice=dayItem.Open; let position=parseInt(dayItem.Cache/buyPrice); //开盘价买 let cache=dayItem.Cache-buyPrice*position; //剩余的现金 dayItem.Position=position; dayItem.Cache=cache; dayItem.MarketValue=dayItem.Position*dayItem.Close+dayItem.Cache; //市值 股票+现金 } } else { if (sellItem>0) //卖 { bsItem.S={Data:kLineItem}; bsItem=null; let stockValue=dayItem.Position*dayItem.Close; //卖掉的股票钱 dayItem.Position=0; dayItem.Cache+=stockValue; //卖掉的钱放到现金里面 dayItem.MarketValue=dayItem.Position*dayItem.Close+dayItem.Cache; //市值 股票+现金 } } //缓存上一天的数据 lastDayItem.Position=dayItem.Position; lastDayItem.Cache=dayItem.Cache; dayItem.Net=dayItem.MarketValue/this.InitialCapital; //净值 if (this.IndexClose) dayItem.IndexClose=this.IndexClose[i]; //指数收盘价 aryDay.push(dayItem); } //console.log('[RegressionTest::CaclulateNetValue] aryDay',aryDay); if (aryDay.length<=0) return []; var netValue=[]; //净值 {Date:日期, Net:净值, Close:股票收盘价, IndexClose:大盘的收盘价} for(var i=0;i0 && lastItem.IndexClose>0) indexProfit[i-1]=(item.IndexClose-lastItem.IndexClose)/lastItem.IndexClose; if (item.Net>0 && lastItem.Net>0) bsProfit[i-1]=(item.Net-lastItem.Net)/lastItem.Net; //if (item.Close>0 && lastItem.Close>0) bsProfit[i-1]=(item.Close-lastItem.Close)/lastItem.Close; indexProfitTotal+=indexProfit[i-1]; bsProfitTotal+=bsProfit[i-1]; lastItem=item; } var averageIndexProfit=indexProfitTotal/indexProfit.length; var averageBSProfit=bsProfitTotal/bsProfit.length; var betaCOV=0; //协方差 for(var i=0;i