HQChart使用教程60-新版k线训练使用教程
样例页面
手机端页面 https://jones2000.github.io/HQChart/webhqchart.demo/samples/demo_ktrain.html
手机端横屏页面 https://jones2000.github.io/HQChart/webhqchart.demo/samples/demo_ktrain2.html
K线训练重构
老的K训练缺陷:
- 算法比较单一, 只能买一卖一, 无法一条数据上多次卖买。
- K线回放只是简单的在显示的时候限制数据个数,而不是数据一个一个的增加.导致复权和有些指标计算会不正确.
- 不支持数据拖拽,手机端手势操作
基于这些缺陷, 决定重构新版本的K线训练.
1. 支持一条K线上多次买卖,这样就可以训练T+0的模拟
2. 支持移动端手势和PC鼠标对数据的操作,缩放,移动等
3. 模拟K线回放,数据是一个一个的增加.
创建K线图
通过setoption来创建K线训练图, option参数和K线图参数一致。type使用K线训练
Type
- 动态复权模式:
每次移动K线都会重新计算复权价格和指标
‘K线训练’ 横屏使用 “K线训练横屏”. - 简单模式
复权价格和指标只计算1次,把所有的数据都算好。移动K线的不会重新计算复权价格和指标
“简单K线训练”, 横屏使用 “简单K线训练横屏”
Train
K线训练配置
- DataCount: 训练数据个数
- StartDate: 数据训练的起始位置 { Date:日期, Time:时间(分钟K线训练使用) }
下面是一个创建K线训练的配置, 使用600000股票训练,从20180201 开始训练,训练200个数据
this.Option =
{
Type: 'K线训练',
Windows: //窗口指标
[
//{Index:"EMPTY"},
{Index:"MA", Modify:false,Change:false},
{Index:"VOL", Modify:false,Change:false}
],
Symbol: "600000.sh",
IsAutoUpate: false, //是自动更新数据
IsShowRightMenu: false, //右键菜单
CorssCursorTouchEnd:true,
CorssCursorInfo:{ Left:0, Right:2, Bottom:1, IsShowCorss:true },
KLine:
{
Right: 0, //复权 0 不复权 1 前复权 2 后复权
Period: 0, //周期 0 日线 1 周线 2 月线 3 年线
MaxReqeustDataCount: 2000, //数据个数
PageSize: 50, //一屏显示多少数据
IsShowTooltip: false, //是否显示K线提示信息
RightSpaceCount:2,
},
KLineTitle: //标题设置
{
IsShowName: false, //不显示股票名称
IsShowSettingInfo: false, //不显示周期/复权
IsShowDateTime:true
},
Border: //边框
{
Top:1,
Left: 1, //左边间距
Right: 1 //右边间距
},
Frame: //子框架设置
[
{ SplitCount: 3, IsShowLeftText:false },
{ SplitCount: 2, IsShowLeftText:false }
],
Train:
{
DataCount:200, //训练数据
StartDate:{ Date:20180201 }
},
ExtendChart: //扩展图形
[
{ Name:'KLineTooltip' }, //手机端tooltip
{ Name:'背景图', FrameID:0 }
],
}
操作接口
K线训练接口没有对外开放,可以通过内部图形实例来操作
this.Chart.JSChartContainer JSChartContainer就是hqchart实例的内部图形实例变量, 所有K线训练操作都通过调用它的成员函数来实现。
下一个K线
MoveNextKLineData(option)
移动一个K线
option可选, k不填默认移动一个K线
{
PageSize: 移动以后一屏显示多少个数据(可选) 默认是当前的数据个数一致
Step:移动一个K线,(可选), 默认移动1个K线
}
this.MoveNextKLine=function()
{
this.Chart.JSChartContainer.MoveNextKLineData();
}
自动/停止自动移动K线
Run(option)/Stop()
Run(option)
option可选, 不填每次移动1个K线
option和 MoveNextKLineData()里的option设置一致
this.Run=function()
{
this.Chart.JSChartContainer.Run();
}
this.Stop=function()
{
this.Chart.JSChartContainer.Stop();
}
买卖股票
BuyOrSell=function(data, isDraw);
data 买卖股票的数据 { Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 }
isDraw 是否更新图形
可以多次买卖无限制
this.Sell=function()
{
.....
++this.OrderID;
//以最新价格卖出
var data={ Price:this.LatestKData.Close, Vol:10 ,Op:1 , ID:this.OrderID }; //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 }
......
this.Chart.JSChartContainer.BuyOrSell(data, false);
this.Chart.JSChartContainer.MoveNextKLineData(); //卖了股票移动到下一个K线上
}
this.Buy=function()
{
........
++this.OrderID;
//以最新价格买入
var data={ Price:this.LatestKData.Close, Vol:10 ,Op:0 , ID:this.OrderID }; //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 }
........
this.Chart.JSChartContainer.BuyOrSell(data, false);
this.Chart.JSChartContainer.MoveNextKLineData(); //买了股票移动到下一个K线上
}
K线移动监听事件
事件ID JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP
回调函数格式 function(event,data,obj)
event 注册的事件信息
data k训练信息
- TrainDataCount: 剩余训练数据个数
- BuySellData 所有的买卖数据
- LastData 最新一条K线原始数据(为复权或计算周期的)
- LastShowData 最新一条K线显示数据 (已复权或已计算周期的)
- KLine K线信息 { Start 训练起始位置, End 当前最后一个数据位置 }
obj k线训练内部实例
this.Create=function() //创建图形
{
var self=this;
$(window).resize(function() { self.OnSize(); }); //绑定窗口大小变化事件
var blackStyle=HQChartStyle.GetStyleConfig(STYLE_TYPE_ID.BLACK_ID); //读取黑色风格配置
//JSChart.SetStyle(blackStyle);
//this.DivKLine.style.backgroundColor=blackStyle.BGColor; //设置最外面的div背景
this.OnSize(); //让K线全屏
this.Chart.SetOption(this.Option); //设置K线配置
//注册K线移动监听事件
this.Chart.AddEventCallback({
event:JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP,
callback:(event,data,obj)=> { this.OnKLineMove(event,data,obj); }
});
}
//K线移动事件回调, 把最后的K线信息显示到元素上。
this.OnKLineMove=function(event,data,obj)
{
console.log("[KLineChart::OnKLineMove] data", data);
this.LatestKData=data.LastShowData;
var dateTime=`日期:${this.LatestKData.Date}`;
if (this.LatestKData.Time) dateTime=`日期:${this.LatestKData.Date} 时间:${this.LatestKData.Time}`;
//更新界面显示信息
var message=`状态:${data.Description} ${dateTime} 最新价格: ${this.LatestKData.Close.toFixed(2)} 剩余数据:${data.TrainDataCount}`;
$("#traininfo")[0].innerText=message;
if (data.TrainDataCount<=0) this.LatestKData=null;
}
重新开始训练
RestartTrain=function(option)
option:
{
Symbol:训练的股票
Train:{ DataCount:训练数据格式, DateTime{ Date:训练起始日期, Time:训练起始时间(分钟K线才有)}
Period: 周期
Right: 复权
}
//重新开始训练
this.RestartTrain=function()
{
//外部清空上一次训练数据
.......
var option=
{
Symbol:"000001.sz",
Train:{ DataCount:30, DateTime: { Date:20190301 } } ,
Period:0, //周期
Right:1, //复权
}
this.Chart.JSChartContainer.RestartTrain(option);
}
}
买卖数据计算
由于训练成绩的规则会根据业务需求调整,所有k线训练内部不计算成绩, 有外部自行计算收益和成绩,你可以通过卖入价和卖出价简单的统计胜率, 或使用初始资金,买卖股票,最后通过持仓计算最后资金来计算复杂收益
K线背景色设置功能
为了在UI上直观的体现用户在训练的时候那次操作时赚钱的,那次操作时赔钱的。我增加了K线背景色的设置,可以设置一个时间区间段的颜色。效果如下图
- 创建
在setoption里面(ExtendChart)创建背景色的画法并绑定到第1个窗口
Name:固定背景图
FrameID: 绑定窗口的索引
ExtendChart: //扩展图形
[
{ Name:'KLineTooltip' }, //手机端tooltip
{ Name:'背景图', FrameID:0 }
],
- 获取背景色画法实例
遍历所有的扩展画法,找到背景色画法实例
this.GetBGChartPaint=function()
{
for(var i in this.Chart.JSChartContainer.ExtendChartPaint)
{
var item=this.Chart.JSChartContainer.ExtendChartPaint[i];
if (item.ClassName=='BackgroundPaint') return item;
}
return null;
}
- 设置背景色
data:[ ] 背景色数组
单个背景数据结构
{ Start: 起始位置
{Date:日期, Time:时间(分钟K线才有)},
End: 结束位置
{ Date:日期, Time:时间(分钟K线才有) } ,
Color:背景颜色 'rgba(200,200,200,0.5)
};
//计算成绩
this.CalulateScore=function()
{
var count=0, success=0;
var aryBGData=[]; //背景颜色
.... 生成背景色数据
var bgPaint=this.GetBGChartPaint();
if (bgPaint) bgPaint.Data=aryBGData; //设置到背景色画法里面
}
买卖图标修改
通过获取全局的资源配置修改K线训练的配置就可以。
HQChart使用教程4- 如何自定义K线图颜色风格
图标使用SVG, Family 为iconfont字体名字 Text:为竖屏图标字符 HScreenText:为横屏字符。 横屏和竖屏使用2个不同的图标字符。
下面是h5用的。
this.KLineTrain =
{
IconFont:
{
Family:'iconfont',
Buy:{ Text:'\ue64a', HScreenText:'\ue68a' ,Color:'rgb(255,140,0)' }, //买
Sell:{ Text:'\ue64b', HScreenText:'\ue68a' ,Color:'rgb(6,79,18)' }, //卖
Last:{ Text:'\ue681', HScreenText:'\ue68a' ,Color:'rgb(55,0,255)' }, //最后一个K线
}
};
小程序和uniapp画布都不支持SVG. 只能是文字输出作为买卖图标,比SVG效果要差好多(平台不行我也没办法)
读取全局资源配置,修改下面的配置就可以
this.KLineTrain = {
Font:'bold 14px arial',
LastDataIcon: {Color:'rgb(0,0,205)',Text:'⬇'},
BuyIcon: {Color:'rgb(0,205,102 )',Text:'B'},
SellIcon: {Color:'rgb(255,127,36 )',Text:'S'},
}
完整代码
就贴一个竖屏的吧,横屏的都一样,就UI布局改下,具体看demo页面源码就可以了,我就不贴出来了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>K线图训练(手机)</title>
<!-- 加载资源 -->
<link rel="stylesheet" href="../jscommon/umychart.resource/css/tools.css" />
<link rel="stylesheet" href="../jscommon/umychart.resource/font/iconfont.css" />
<style>
.line-box{
height: 25px;
}
.btn-style{
padding: 3px 8px;
border: 1px solid #ececec;
border-radius: 5px;
background-color: #f5f5f5;
cursor: pointer;
}
</style>
</head>
<body>
<div id="kline" style="width: 900px;height:400px;position: relative;"></div>
<div class="line-box">
<span class="btn-style" id='nextkline'>下一K线</span>
<label>
<input type="checkbox" id='autorun' /> K线自动前进
</label>
<span class="btn-style" id='reset'>重新开始</span>
</div>
<div class="line-box">
<span class="btn-style" id='buy'>[ 买 ]</span>
<span class="btn-style" id='sell'>[ 卖 ]</span>
</div>
<div>
<span id='traininfo'>加载中......</span>
</div>
<div>
<span id='traininfo2'>成绩:</span>
</div>
<script src="content/js/jquery.min.js"></script>
<script src="content/js/webfont.js"></script>
<script src='../jscommon/umychart.console.js'></script> <!-- 日志输出 -->
<script src="../jscommon/umychart.network.js"></script> <!-- 网络请求分装 -->
<script src="../jscommon/umychart.js"></script> <!-- K线图形 -->
<script src="../jscommon/umychart.complier.js"></script> <!-- 麦语言解析执行器 -->
<script src="../jscommon/umychart.index.data.js"></script> <!-- 基础指标库 -->
<script src="../jscommon/umychart.style.js"></script> <!-- 白色风格和黑色风格配置信息 -->
<script>
//JSConsole.Chart.Log=() =>{}
//JSConsole.Complier.Log=()=>{}
//简单的把K线控件封装下
function KLineChart(divKLine)
{
this.DivKLine=divKLine;
this.Chart=JSChart.Init(divKLine); //把K线图绑定到一个Div上
this.LatestKData; //最新的K线数据
this.LatestTrade; //最新一条交易记录
this.AryTrade=[]; //交易记录 {Buy:, Sell:} 成对
this.ClearDivDOM=function()
{
var childList = this.DivKLine.childNodes;
for(var i in childList)
{
this.DivKLine.removeChild(childList[i]);
}
}
//K线配置信息
this.Option = {
Type: 'K线训练',
Windows: //窗口指标
[
//{Index:"EMPTY"},
{Index:"MA", Modify:false,Change:false},
{Index:"VOL", Modify:false,Change:false}
],
Symbol: "600000.sh",
IsAutoUpate: false, //是自动更新数据
IsShowRightMenu: false, //右键菜单
CorssCursorTouchEnd:true,
CorssCursorInfo:{ Left:0, Right:2, Bottom:1, IsShowCorss:true },
KLine:
{
Right: 0, //复权 0 不复权 1 前复权 2 后复权
Period: 0, //周期 0 日线 1 周线 2 月线 3 年线
MaxReqeustDataCount: 2000, //数据个数
PageSize: 50, //一屏显示多少数据
IsShowTooltip: false, //是否显示K线提示信息
RightSpaceCount:2,
},
KLineTitle: //标题设置
{
IsShowName: false, //不显示股票名称
IsShowSettingInfo: false, //不显示周期/复权
IsShowDateTime:true
},
Border: //边框
{
Top:1,
Left: 1, //左边间距
Right: 1 //右边间距
},
Frame: //子框架设置
[
{ SplitCount: 3, IsShowLeftText:false },
{ SplitCount: 2, IsShowLeftText:false }
],
Train:
{
DataCount:200, //训练数据
//StartDate:{ Date:20180201 }
},
ExtendChart: //扩展图形
[
{ Name:'KLineTooltip' }, //手机端tooltip
{ Name:'背景图', FrameID:0 }
],
}
this.Create=function() //创建图形
{
var self=this;
$(window).resize(function() { self.OnSize(); }); //绑定窗口大小变化事件
var blackStyle=HQChartStyle.GetStyleConfig(STYLE_TYPE_ID.BLACK_ID); //读取黑色风格配置
//JSChart.SetStyle(blackStyle);
//this.DivKLine.style.backgroundColor=blackStyle.BGColor; //设置最外面的div背景
this.OnSize(); //让K线全屏
this.Chart.SetOption(this.Option); //设置K线配置
this.Chart.AddEventCallback({
event:JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP,
callback:(event,data,obj)=> { this.OnKLineMove(event,data,obj); }
});
}
this.OnSize=function() //自适应大小调整
{
var height= $(window).height()-90;
var width = $(window).width();
this.DivKLine.style.top='px';
this.DivKLine.style.left='px';
this.DivKLine.style.width=width+'px';
this.DivKLine.style.height=height+'px';
this.Chart.OnSize();
}
this.MoveNextKLine=function()
{
this.Chart.JSChartContainer.MoveNextKLineData();
}
this.Run=function()
{
this.Chart.JSChartContainer.Run();
}
this.Stop=function()
{
this.Chart.JSChartContainer.Stop();
}
this.OnKLineMove=function(event,data,obj)
{
console.log("[KLineChart::OnKLineMove] data", data);
this.LatestKData=data.LastShowData;
var dateTime=`日期:${this.LatestKData.Date}`;
if (this.LatestKData.Time) dateTime=`日期:${this.LatestKData.Date} 时间:${this.LatestKData.Time}`;
//更新界面显示信息
var message=`状态:${data.Description} ${dateTime} 最新价格: ${this.LatestKData.Close.toFixed(2)} 剩余数据:${data.TrainDataCount}`;
$("#traininfo")[0].innerText=message;
if (data.TrainDataCount<=0) this.LatestKData=null;
}
this.OrderID=10; //模拟单号
this.Sell=function()
{
if (!this.LatestKData) return;
++this.OrderID;
//以最新价格卖出
var data={ Price:this.LatestKData.Close, Vol:10 ,Op:1 , ID:this.OrderID }; //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 }
this.LatestTrade=data;
var tradeItem=this.AryTrade[this.AryTrade.length-1];
tradeItem.Sell={ Price:data.Price, ID:data.ID , Date:this.LatestKData.Date, Time:this.LatestKData.Time };
this.CalulateScore();
$("#sell").hide();
$("#buy").show();
this.Chart.JSChartContainer.BuyOrSell(data, false);
this.Chart.JSChartContainer.MoveNextKLineData();
}
this.Buy=function()
{
if (!this.LatestKData) return;
++this.OrderID;
//以最新价格买入
var data={ Price:this.LatestKData.Close, Vol:10 ,Op:0 , ID:this.OrderID }; //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 }
this.LatestTrade=data;
var tradeItem={ Buy:{Price:data.Price, ID:data.ID, Date:this.LatestKData.Date, Time:this.LatestKData.Time } };
this.AryTrade.push(tradeItem);
$("#buy").hide();
$("#sell").show();
this.Chart.JSChartContainer.BuyOrSell(data, false);
this.Chart.JSChartContainer.MoveNextKLineData();
this.CalulateScore();
}
//计算成绩
this.CalulateScore=function()
{
var count=0, success=0;
var aryBGData=[]; //背景颜色
for(var i in this.AryTrade)
{
var item=this.AryTrade[i];
if (!item.Buy || !item.Sell) continue;
var bgItem={ Start:{Date:item.Buy.Date, Time:item.Buy.Time}, End:{ Date:item.Sell.Date, Time:item.Sell.Time } };
if (item.Buy.Price<item.Sell.Price)
{
++success;
bgItem.Color='rgba(250,128,114,0.5)';
}
else
{
bgItem.Color='rgba(152,251,152,0.5)';
}
aryBGData.push(bgItem);
++count;
}
var result=0;
if (count>0) result=success/count;
var message=`成绩:交易次数${count} 成功次数${success} 胜率:${result.toFixed(2)}`;
$("#traininfo2")[0].innerText=message;
var bgPaint=this.GetBGChartPaint();
if (bgPaint) bgPaint.Data=aryBGData;
}
this.GetBGChartPaint=function()
{
for(var i in this.Chart.JSChartContainer.ExtendChartPaint)
{
var item=this.Chart.JSChartContainer.ExtendChartPaint[i];
if (item.ClassName=='BackgroundPaint') return item;
}
return null;
}
//重新开始训练
this.RestartTrain=function()
{
this.AryTrade=[];
this.LatestKData=null;
this.LatestTrade=null;
$("#traininfo2")[0].innerText='成绩:';
$("#traininfo")[0].innerText='加载中......';
$("#buy").show();
$("#sell").hide();
var bgPaint=this.GetBGChartPaint();
if (bgPaint) bgPaint.Data=null;
var option=
{
Symbol:"000001.sz",
Train:{ DataCount:30, DateTime: { Date:20190301 } } ,
Period:0, //周期
Right:1, //复权
}
this.Chart.JSChartContainer.RestartTrain(option);
}
}
$(function ()
{
WebFont.load({ custom: { families: ['iconfont'] } }); //预加载下iconfont资源
var klineControl=new KLineChart(document.getElementById('kline'));
klineControl.Create();
$("#nextkline").click(function() { klineControl.MoveNextKLine()} );
$("#autorun").click(function()
{
if (this.checked) klineControl.Run();
else klineControl.Stop();
});
$("#buy").click(function() { klineControl.Buy()} );
$("#sell").click(function() { klineControl.Sell({ Price:8.2, Vol:14 , Op:1 })} );
$("#reset").click(function() { klineControl.RestartTrain()} );
$("#buy").show();
$("#sell").hide();
})
</script>
</body>
</html>