HQChart实战教程67-worker批量计算股票指标
- 什么是Worker
- 批量指标计算
- 示例地址
- 步骤
- 1. 创建一个后台工作线程类
- 2. 发送指标计算任务
- 3. 接收计算结果
- 数据对接
- 完整源码
- demo_workerthread_sina.html
- hqchart_worker_sina.js
- HQChart插件源码地址
什么是Worker
Worker 接口是 Web Workers API 的一部分,指的是一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。
批量指标计算
通过把指标计算迁移到后台线程中,可以提高效率(可以开N个后台线程,平行计算),也不会卡主线程。HQChart通达信指标计算引擎是一个独立的计算模块,通过在后台线程中独立调用计算模块就可以达到批量计算指标
示例地址
https://jones2000.github.io/HQChart/webhqchart.demo/samples/demo_workerthread_sina.html
注意:K线数据是网上找的,不支持跨域,调试的时候需要给浏览器安装一个跨域插件,就可以用
可以参看教程解决Chrome本地调试跨域.
步骤
1. 创建一个后台工作线程类
hqchart内置一个后台指标计算的工作线程类HQChartScriptWorker, 可以直接派生这个类, 然后把数据对接上就可以了。 其他的基类都封装好了。
HQChartScriptWorker源码地址:https://github.com/jones2000/HQChart/blob/master/webhqchart/umychart.worker.js
2. 发送指标计算任务
通过系统api接口postMessage可以发送数据给工作线程
function RunIndexScript(symbol, index, jobID)
{
var message=
{
ID:JSCHART_WORKER_MESSAGE_ID.EXECUTE_SCRIPT, //任务类型(固定值)
AryIndex: //需要执行的指标名称或指标脚本
[
{Index:index},
//{Index:"MACD"},
//{Index:"TEST", Name:"测试脚本", Script:'T:MA((L+H)/2,10)'}
],
Symbol:symbol, //股票代码
Name:symbol, //股票名称
Period:5, //5分钟K线
Right:0, //复权
HQDataType:HQ_DATA_TYPE.KLINE_ID, //K线指标(固定值)
Guid:jobID,
IsApiPeriod:true, //后台计算周期
};
g_HQWorker.postMessage(message);
}
3. 接收计算结果
绑定onmessage的回调,就可以
var g_HQWorker=new Worker("hqchart_worker_sina.js");
g_HQWorker.onmessage=(e)=>{ OnRecvWorkerMessage(e); }
function OnRecvWorkerMessage(e)
{
if (e.data.ID==JSCHART_WORKER_MESSAGE_ID.FINISH_EXECUTE_SCRIPT) //指标计算完成
{
console.log("[OnRecvWorkerMessage] data=", e.data);
}
}
数据结构
{
Data:指标数据 { Date:日期, Time:时间, Out:指标输出变量, Stock:股票信息, Index:指标名称 }
IndexInfo: 指标信息
JobInfo: 发送指标命令的内容
}
注意: 返回的指标结果数据是一个数组
数据对接
和前端图形一样,通过NetworkFilter对接数据
function JSSampleScriptWorker()
{
this.newMethod=HQChartScriptWorker; //派生
this.newMethod();
delete this.newMethod;
this.NetworkFilter=function(data, callback, indexInfo, message)
{
//数据对接
console.log(`[JSSampleScriptWorker::NetworkFilter] [${data.Name}][${data.Explain}] data=`, data);
if (data.Name=="JSSymbolData::GetSymbolData")
{
}
............
}
完整源码
demo_workerthread_sina.html
<!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>工作线程计算</title>
</head>
<body>
<div id="range_title">5分钟周期指标计算</div>
<br/>
<div>
<div class="table_row">
<span class="item_symbol">600000.sh</span>
<span class="item_indexname">MA</span>
<div id="item_result_1">计算结果</div>
</div>
<div class="table_row">
<span class="item_symbol">605166.sh</span>
<span class="item_indexname">RSI</span>
<div id="item_result_2">计算结果</div>
</div>
<div class="table_row">
<span class="item_symbol">601169.sh</span>
<span class="item_indexname">MACD</span>
<div id="item_result_3">计算结果</div>
</div>
</div>
<br/>
<div>
<span id="button_1" class="btn-style">开始计算</span>
</div>
<script src="../jscommon/umychart.resource/js/jquery.min.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>
var g_HQWorker=new Worker("hqchart_worker_sina.js");
g_HQWorker.onmessage=(e)=>{ OnRecvWorkerMessage(e); }
var jobCount=1;
var job_list=
[
{ Symbol:"sh600000.sh", Index:"MA", OutDomID:"item_result_1" },
{ Symbol:"sh605166.sh", Index:"RSI", OutDomID:"item_result_2" },
{ Symbol:"sh601169.sh", Index:"MACD", OutDomID:"item_result_3" },
];
function OnRecvWorkerMessage(e)
{
if (e.data.ID==JSCHART_WORKER_MESSAGE_ID.FINISH_EXECUTE_SCRIPT)
{
console.log("[OnRecvWorkerMessage] data=", e.data);
var jobID=e.data.JobInfo.Guid;
var aryDate=e.data.Data.Date;
var aryTime=e.data.Data.Time;
var index=aryTime.length-10; //输出最后条记录
var aryResult=[];
var outText="";
for(var i=index;i<aryTime.length;++i)
{
var date=aryDate[i];
var time=aryTime[i];
var text=`Date=${date}, Time:${time} `;
for(var j=0;j<e.data.Data.Out.length;++j)
{
var name=e.data.Data.Out[j].Name;
var value=e.data.Data.Out[j].Data[i];
text+=`${name}=${value.toFixed(4)}, `;
}
aryResult.push(text);
outText+=text;
outText+='<br>';
}
var domID=null;
for(var i=0;i<job_list.length;++i)
{
var item=job_list[i];
if (item.JobID==jobID)
{
domID=item.OutDomID;
break;
}
}
if (!domID) return;
$("#"+domID)[0].innerHTML=outText;
}
}
function RunIndexScript(symbol, index, jobID)
{
var message=
{
ID:JSCHART_WORKER_MESSAGE_ID.EXECUTE_SCRIPT,
AryIndex:
[
{Index:index},
//{Index:"MACD"},
//{Index:"TEST", Name:"测试脚本", Script:'T:MA((L+H)/2,10)'}
],
Symbol:symbol,
Name:symbol,
Period:5, //5分钟K线
Right:0,
HQDataType:HQ_DATA_TYPE.KLINE_ID,
Guid:jobID,
IsApiPeriod:true,
};
g_HQWorker.postMessage(message);
}
function RunAll()
{
for(var i=0;i<job_list.length;++i)
{
var item=job_list[i];
item.JobID=++jobCount;
RunIndexScript(item.Symbol, item.Index, item.JobID);
}
}
$(function ()
{
$("#button_1").click(function() { RunAll(); } );
})
</script>
</body>
</html>
<style>
.btn-style
{
padding: 3px 8px;
border: 1px solid #ececec;
border-radius: 5px;
background-color: #f5f5f5;
cursor: pointer;
}
.item_symbol
{
display:block;
line-height: 50px;
width:100px;
align-items: center;
}
.item_indexname
{
display:block;
line-height: 50px;
width:100px;
align-items: center;
}
.table_row
{
display: flex;
height: 200px;
border-style:solid;
border-width:1px;
border-color:#BEBEBE;
}
</style>
hqchart_worker_sina.js
/
// 工作线程
//
//
importScripts("../jscommon/umychart.complier.js","../jscommon/umychart.js", "../jscommon/umychart.index.data.js","../jscommon/umychart.worker.js");
JSConsole.Complier.Log=()=>{ }
function JSSampleScriptWorker()
{
this.newMethod=HQChartScriptWorker; //派生
this.newMethod();
delete this.newMethod;
this.NetworkFilter=function(data, callback, indexInfo, message)
{
//数据对接
console.log(`[JSSampleScriptWorker::NetworkFilter] [${data.Name}][${data.Explain}] data=`, data);
if (data.Name=="JSSymbolData::GetSymbolData")
{
var requestData=data.Request.Data;
if (requestData.period==5) //5分钟K线
{
var symbol=requestData.symbol;
symbol=symbol.replace(".sh","");
//http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=sz000001&scale=5&ma=5&datalen=1023
var url=`https://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=${symbol}&scale=5&ma=5&datalen=1023`;
console.log(`[JSSampleScriptWorker::NetworkFilter] url=${url}`);
var response=this.HttpRequest(url);
if (response.response)
{
var recv=JSON.parse(response.response);
this.RecvHistoryMinuteData(recv, callback, data);
}
}
}
}
this.RecvHistoryMinuteData=function(recv, callback, data)
{
var hqChartData={code:0, data:[] };
hqChartData.symbol=hqChartData.name=data.Request.Data.symbol;
var yClose=null;
for(var i=0;i<recv.length;++i)
{
var item=recv[i];
var dateTime=new Date(item.day);
var date=dateTime.getFullYear()*10000+(dateTime.getMonth()+1)*100+dateTime.getDate();
var time=dateTime.getHours()*100+dateTime.getMinutes();
var close=parseFloat(item.close);
var high=parseFloat(item.high);
var low=parseFloat(item.low);
var open=parseFloat(item.open);
var vol=parseFloat(item.volume);
var amount=null;
if (close==null) continue;
var newItem=[ date, yClose, open, high, low, close, vol, amount, time];
hqChartData.data.push(newItem);
yClose=close;
}
console.log("[JSSampleScriptWorker::RecvHistoryMinuteData] hqchartData ", hqChartData)
callback(hqChartData);
}
this.HttpRequest=function(url)
{
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.onerror=(e)=>{
console.log(e);
}
req.send();
return req;
}
}
var g_ScriptWorker=new JSSampleScriptWorker();
g_ScriptWorker.Create();
HQChart插件源码地址
https://github.com/jones2000/HQChart