UNPKG

hqchart

Version:

HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接

374 lines (306 loc) 15.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _map = require('babel-runtime/core-js/map'); var _map2 = _interopRequireDefault(_map); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* 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 _map2.default(); //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 _map2.default(); }; this.GetBSData = function (startDate) //BS点配对 { B:[{Data:K线数据, Count:天数, NextOpen:下一天的开盘价 }], S:{Data:K线数据}} { var index = null; for (var i = 0; i < this.HistoryData.length; ++i) { var item = this.HistoryData[i]; if (item.Date >= 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; i < this.HistoryData.length; ++i) { var buyItem = this.BuyData[i]; var sellItem = this.SellData[i]; var kLineItem = this.HistoryData[i]; if (bsItem === null) { if (buyItem > 0) { var bItem = { Data: kLineItem, Count: 0 }; if (i + 1 < this.HistoryData.length) bItem.NextOpen = this.HistoryData[i + 1].Open; //获取下一天的开盘价 bsItem = { B: [bItem], S: null }; //B可以多个,S一个 } } else { for (var j in bsItem.B) { ++bsItem.B[j].Count; } //天数+1 if (buyItem > 0) { var bItem = { Data: kLineItem, Count: 0 }; if (i + 1 < this.HistoryData.length) bItem.NextOpen = this.HistoryData[i + 1].Open; //获取下一天的开盘价 bsItem.B.push(bItem); } else if (sellItem > 0) { 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.Max < bItem.Count) day.Max = bItem.Count; if (day.Min === null) day.Min = bItem.Count;else if (day.Min > bItem.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; i < this.HistoryData.length; ++i) { var buyItem = this.BuyData[i]; var sellItem = this.SellData[i]; var kLineItem = this.HistoryData[i]; var dayItem = { Date: kLineItem.Date, Position: lastDayItem.Position, Cache: lastDayItem.Cache, Open: kLineItem.Open, Close: kLineItem.Close }; dayItem.MarketValue = dayItem.Position * dayItem.Close + dayItem.Cache; //市值 股票+现金 if (bsItem === null) { if (buyItem > 0) //买 { bsItem = { B: { Data: kLineItem }, S: null }; if (this.NetCalculateModel === 1 && i + 1 < this.HistoryData.length && this.HistoryData[i + 1].Open > 0) buyPrice = this.HistoryData[i + 1].Open; //使用B点下一天的开盘价买 else buyPrice = dayItem.Open; var position = parseInt(dayItem.Cache / buyPrice); //开盘价买 var 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; var 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; i < aryDay.length; ++i) { var item = aryDay[i]; var dataItem = { Net: item.Net, Date: item.Date, Close: item.Close }; if (item.IndexClose) dataItem.IndexClose = item.IndexClose; netValue.push(dataItem); } //console.log('[RegressionTest::CaclulateNetValue] netValue',netValue); return netValue; }; this.CaclulateMaxDropdown = function (data) //最大回撤 { var maxNet = data[0].Net; //最大净值 var maxValue = 0; var maxDay; for (var i = 1; i < data.length; ++i) { var item = data[i]; var value = 1 - item.Net / maxNet; //1-策略当日净值 / 当日之前策略最大净值 if (maxValue < value) { maxValue = value; maxDay = item; } if (maxNet < item.Net) maxNet = item.Net; //取当前最大的净值 } console.log('[RegressionTest::CaclulateMaxDropdown] maxDay', maxDay); return maxValue; }; this.CaclulateBeta = function (data) //β(Beta)系数,参数是净值数组NetValue { var lastItem = data[0]; //上一天的数据 var indexProfit = []; //大盘涨幅 var bsProfit = []; //策略涨幅 var indexProfitTotal = 0, bsProfitTotal = 0; for (var i = 1; i < data.length; ++i) { indexProfit[i - 1] = 0; bsProfit[i - 1] = 0; var item = data[i]; if (item.IndexClose > 0 && 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 < indexProfit.length; ++i) { betaCOV += (bsProfit[i] - averageBSProfit) * (indexProfit[i] - averageIndexProfit); } var betaVAR = 0; //标准差:方差的开2次方 for (var i = 0; i < indexProfit.length; ++i) { betaVAR += (indexProfit[i] - averageIndexProfit) * (indexProfit[i] - averageIndexProfit); } return betaCOV / betaVAR; }; this.Execute = function (obj) //开始计算[ { Name:名字 , Date:起始日期 格式(20180101) }, ....] { for (var i in obj) { var item = obj[i]; if (this.Data.has(item.Name)) //已经计算过了不计算 { console.log('[RegressionTest::Execute] id=' + i + ' Name=' + item.Name + ' Date:' + item.Date + ' is exsit.'); continue; } console.log('[RegressionTest::Execute] id=' + i + ' Name=' + item.Name + ' Date:' + item.Date); var data = this.GetBSData(item.Date); var result = this.Calculate(data); this.Data.set(item.Name, result); } }; } //计算叠加数据 (日期必须匹配) RegressionTest.CaclulateOverlayData = function (obj) //{Main:主数据, Sub:[]//多组叠加数据} { if (!obj.Main || !obj.Sub) return null; var count = obj.Main.length; for (var i in obj.Sub) { var item = obj.Sub[i]; if (item.length != count) { console.log('[RegressionTest::OverlayData] id=' + i + ' data count not match. MainData count=' + count); return null; } } var result = []; //[0]:主数据 , [1...]:叠加数据 var firstData = { Sub: [] }; firstData.Main = obj.Main[0]; result.push([]); for (var i = 0; i < obj.Sub.length; ++i) { var subData = obj.Sub[i]; firstData.Sub[i] = subData[0]; result.push([]); } for (var i = 0; i < obj.Main.length; ++i) { var value = obj.Main[i]; var valuePer = (value - firstData.Main) / firstData.Main; result[0][i] = valuePer; for (var j = 0; j < obj.Sub.length; ++j) { var subData = obj.Sub[j]; var subValue = subData[i]; var subValuePer = (subValue - firstData.Sub[j]) / firstData.Sub[j]; result[j + 1][i] = subValuePer; } } return result; }; RegressionTest.GetPolicyData = function (data) //获取指标数据里面需要计算回测的数据 { var policyData = { KLineData: null, IndexClose: null, BuyData: null, SellData: null }; policyData.KLineData = data.HistoryData.Data; //个股K线数据, for (var i in data.OutVar) { var item = data.OutVar[i]; if (item.Name == 'INDEXCLOSE') { policyData.IndexClose = item.Data; //绑定大盘收盘数据 } else if (item.Name == 'DRAWICON') //买卖盘BS函数 { if (item.Draw && item.Draw.Icon) { if (item.Draw.Icon.ID === 13) policyData.BuyData = item.Draw.DrawData; //买 else if (item.Draw.Icon.ID === 14) policyData.SellData = item.Draw.DrawData; //卖 } } } if (policyData.KLineData && policyData.BuyData && policyData.SellData) return policyData; return null; }; /*暴露外部用的方法*/ exports.default = { RegressionTest: RegressionTest //个股单策略回测 };