最近,FMZ量化交易平臺支持了數據庫接口,因此使用JavaScript語言也能夠很方便的實現一個K線行情數據收集器了。
有了需求,立刻行動~數據庫
構建JavaScript版本行情收集器以前,咱們先來熟悉FMZ的數據庫接口DBExec
。函數
DBExec 函數
先熟悉如下幾種操做:測試
- 建立表
- 向表中寫入數據
- 查詢表中數據
function main() { /* 建立表 var strSql = [ "CREATE TABLE RECORDS_MIN1(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") // DBExec函數返回:{"rowsAffected":1,"lastInsertId":18} */ /* 表寫入數據 // INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) // VALUES (1, 'Paul', 32, 'California', 20000.00 ); var strSql = [ "INSERT INTO RECORDS_MIN1 (TS, HIGH, OPEN, LOW, CLOSE, VOLUME)", "VALUES (111111, 111.11, 111.12, 111.13, 111.14, 111.16);" ].join("") // DBExec函數返回:{"rowsAffected":1,"lastInsertId":1} */ /* 查詢表中數據 // SELECT * FROM RECORDS_MIN1; var strSql = ["SELECT * FROM RECORDS_MIN1;"].join("") // DBExec函數返回:{"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[111111,111.11,111.12,111.13,111.14,111.16]]} */ var ret = DBExec(strSql) // 執行SQL語句 Log(ret) }
設計行情收集器
利用FMZ的數據庫接口DBExec
能夠實現收集交易所的K線數據。
例若有些策略基於很長的K線數據計算指標,很不容易收集數據讓K線數據長度足夠計算指標,可是遇到策略程序設計不完善實盤異常中止、臨時調整代碼、臨時調整策略參數等須要重啓實盤的場景。此時實盤一旦重啓收集的數據就沒有了(程序變量中保存)。因此使用數據庫接口,保存收集的行情數據是一個很是好的解決辦法。this
咱們的需求也十分簡單:url
- 程序輪詢獲取行情
- 判斷BAR更新,將完成的BAR數據寫入數據庫表保存。
- 查詢數據庫中表的數據
- 本例子爲了展現數據,增長了畫圖(使用畫線類庫),經過策略交互按鈕,更新K線圖表。
- 刪除數據庫表
- 初始化,從新建立數據庫表,寫入最新的數據。
簡單的數據收集器源碼:spa
var collecter = {} collecter.init = function(tableName) { this.preBarTS = 0 this.tableName = tableName this.tableAvaliable = false // 檢測tableName表 if (typeof(tableName) == "undefined" || typeof(tableName) != "string") { Log(tableName) throw "tableName error!" } // SELECT * FROM RECORDS_MIN1 LIMIT 1 var strSql = "SELECT * FROM " + tableName + " LIMIT 1" var ret = DBExec(strSql) if (!ret) { // 表不存在,建立表 Log("嘗試讀取表", this.tableName, "的數據,讀取失敗,開始建立表", this.tableName) var strSql = [ "CREATE TABLE " + tableName + "(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") ret = DBExec(strSql) if (!ret) { throw "建立" + tableName + "表失敗!" } Log("建立", tableName, "表", ret) } Log(this.tableName, ret) this.tableAvaliable = true } collecter.run = function(records) { if (!this.tableAvaliable) { return } var len = records.length var lastBar = records[len - 1] var beginBar = records[0] var ret = null if (this.preBarTS == 0) { // 初始 /* DELETE FROM table_name WHERE [condition]; */ var strSql = "DELETE FROM " + this.tableName + " WHERE TS >= " + beginBar.Time + ";" ret = DBExec(strSql) Log("刪除與當前記錄重複部分", ret) // 寫入 ret = DBExec("BEGIN") Log("BEGIN:", ret) for (var i = 0 ; i < len - 1 ; i++) { var strSql = [ "INSERT INTO " + this.tableName + " (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) ", `VALUES (${records[i].Time}, ${records[i].High}, ${records[i].Open}, ${records[i].Low}, ${records[i].Close}, ${records[i].Volume});` ].join("") DBExec(strSql) } ret = DBExec("COMMIT") Log("COMMIT:", ret) this.preBarTS = lastBar.Time } else if(this.preBarTS != lastBar.Time) { // 更新 var strSql = [ "INSERT INTO " + this.tableName + " (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) ", `VALUES (${records[len-2].Time}, ${records[len-2].High}, ${records[len-2].Open}, ${records[len-2].Low}, ${records[len-2].Close}, ${records[len-2].Volume});` ].join("") ret = DBExec(strSql) Log("INSERT:", ret) this.preBarTS = lastBar.Time } } collecter.getRecords = function() { // 讀取數據庫 var strSql = "SELECT * FROM " + this.tableName + ";" var ret = DBExec(strSql) // Log("SELECT * FROM .. :", ret) // SELECT * FROM .. : {"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[1616110200000,58085.9,57716.2,57664.3,57757.6,24.216], ...]} var arr = ret.values var r = [] for (var i = 0 ; i < arr.length ; i++) { r.push({ Time : arr[i][0], High : arr[i][1], Open : arr[i][2], Low : arr[i][3], Close : arr[i][4], Volume : arr[i][5] }) } return r } collecter.deleteTable = function() { // DROP TABLE database_name.table_name; var strSql = "DROP TABLE " + this.tableName var ret = DBExec(strSql) if (!ret) { Log("刪除表", this.tableName, "失敗:", ret) } else { Log("刪除表", this.tableName, " DROP TABLE:", ret) this.tableAvaliable = false } } function main() { collecter.init(tableName) while(true) { var r = _C(exchange.GetRecords) // records tbl var rTbl = { type : "table", title : "數據", cols : ["strTime", "Time", "High", "Open", "Low", "Close", "Volume"], rows : [] } var arrR = [] if (collecter.tableAvaliable) { arrR = collecter.getRecords() } for (var i = arrR.length - 1; (i > arrR.length - 1 - 9) && (i >= 0); i--) { var bar = arrR[i] rTbl.rows.push([_D(bar.Time), bar.Time, bar.High, bar.Open, bar.Low, bar.Close, bar.Volume]) } LogStatus(_D(), "獲取的K線數據長度:", arrR.length, ", collecter.tableAvaliable:", collecter.tableAvaliable, "\n", "`" + JSON.stringify(rTbl) + "`") collecter.run(r) // 交互測試 var cmd = GetCommand() if(cmd) { // 處理交互 Log("交互命令:", cmd) var arr = cmd.split(":") // 從數據庫中讀取K線數據,刷新圖表 if(arr[0] == "refreshRecords") { if (collecter.tableAvaliable) { var records = collecter.getRecords() $.PlotRecords(records, collecter.tableName) // 使用畫線類庫畫圖 } else { Log("對應的數據庫表不存在 collecter.tableAvaliable:", collecter.tableAvaliable) } } else if (arr[0] == "deleteBDTable") { // 刪除數據庫表 collecter.deleteTable() } else if (arr[0] == "initCollecter") { // 初始化收集器 Log("初始化收集器") collecter.init(tableName) } } Sleep(5000) } }
測試
對比數據.net