摘要
數據是量化交易的源頭,如何高效地管理大量數據是很是關鍵的環節,數據庫是最佳的解決方案之一,現現在數據庫的應用已是各種日內交易、高頻交易等策略的量化標準配置。本篇咱們來研究一下發明者量化(FMZ.COM)內置的數據庫,內容包括:如何建立數據表、存儲數據、修改數據、刪除數據、引用數據以及如何應用於實戰。node
如何選擇數據庫
熟悉發明者量化平臺的應該知道,在這以前要想把數據保存到本地複用,只能用_G()函數,每次中止策略時,_G()函數會自動保存須要的信息。但若是想要保存更多更復雜的格式化數據,_G()函數顯然就不太適用了,因而不少人想到了自建數據庫來解決這個問題。數據庫
提到自建數據庫,想必你們能想到Oracle、MySQL、KDB、OneTick、NoSQL...這些都是很是優秀的企業級別的應用,不論是功能仍是性能都很是強大。但也面臨幾個問題:上手難度大,配置繁瑣維護起來麻煩,這對於量化交易散戶來講,有點用大炮打蒼蠅的感受,即便上手也只是用到不多一部分功能。函數
發明者量化內置數據庫
接下來讓咱們認識一下發明者量化內置的輕型數據庫,DBExec是發明者量化內置的一個關係型數據管理系統接口,基於SQLite開發,其自己是用C寫的,不但體積小巧,佔用資源低,並且處理速度快,很是適合用於金融量化分析愛好者在本地實現數據管理,由於能夠將不一樣的「對象」(例如交易所,數據源,價格)分紅不一樣的表,並在表之間定義關係。此外用戶無需單獨安裝和配置,只要調用DBExec()函數就能夠直接使用!性能
另外,SQLite語言的學習成本很低,在數據庫上執行的大部分工做都由SQLite語句完成。熟悉基本語法就能知足大部分需求,如下是SQLite的基礎語法。學習
基礎語法
SQLite的語法是不區分大小寫的,不過有一些命令對大小寫敏感,如GLOB和glob表明不一樣的含義。SQLite語句能夠以任何關鍵字開始,如SELECT、INSERT、UPDATE、DELETE、ALTER、DROP等,它們分別表示:提取數據、插入數據、更新數據、刪除數據、修改數據庫、刪除數據表。全部的語句以英文分號結束。下面是一個簡單的數據庫建立、增、刪、改、查等操做:url
function main() { // 建立:若是「users」表不存在就建立一個,「id」是整數且自動增長,「name」是文本形式且不爲空 Log(DBExec('CREATE TABLE IF NOT EXISTS "users" (id INTEGER PRIMARY KEY AUTOINCREMENT, name text not NULL);')); // 增長: Log(DBExec("INSERT INTO users(name) values('張三')")); Log(DBExec("INSERT INTO users(name) values('李四')")); // 刪除: Log(DBExec("DELETE FROM users WHERE id=1;")); // 修改 Log(DBExec("UPDATE users SET name='王五' WHERE id=2")); // 查詢 Log(DBExec('select 2, ?, ?, ?, ?', 'ok', true,9.8,null)); Log(DBExec('select * from kvdb')); Log(DBExec('select * from cfg')); Log(DBExec('select * from log')); Log(DBExec('select * from profit')); Log(DBExec('select * from chart')); Log(DBExec("selEct * from users")); }
一個數據庫一般包含一個或多個表,每一個表都有一個名字標識,須要注意的是系統保留表分別: kvdb, cfg, log, profit, chart。也就是說在建立表時,應該避開系統保留的名字。讓咱們運行上面的代碼,輸出如下內容:
spa
策略實例
瞭解了SQLite的基礎語法,咱們趁熱打鐵使用發明者量化內置的數據庫,建立一個收集和使用Tick數據的實例。.net
第一步:更新託管者
首先確保您使用的是最新版本的託管者,若是以前下載使用過託管者,須要先刪除,並在 https://www.fmz.com/m/add-node 頁面從新下載和部署。3d
第二步:建立策略日誌
function main() { // 訂閱合約 _C(exchange.SetContractType, 'swap'); // 建立數據表 DBExec('CREATE TABLE IF NOT EXISTS "tick" (id INTEGER PRIMARY KEY AUTOINCREMENT,'.concat( 'High FLOAT not NULL,', 'Low FLOAT not NULL,', 'Sell FLOAT not NULL,', 'Buy FLOAT not NULL,', 'Last FLOAT not NULL,', 'Volume INTEGER not NULL,', 'Time INTEGER not NULL);' )); // 獲取10個tick數據 while (true) { let tick = exchange.GetTicker(); // 在tick表中增長數據 DBExec(`INSERT INTO tick(High, Low, Sell, Buy, Last, Volume, Time) values(${tick.High}, ${tick.Low}, ${tick.Sell}, ${tick.Buy}, ${tick.Last}, ${tick.Volume}, ${tick.Time})`); // 查詢全部數據 let allDate = DBExec('select * from tick'); if (allDate.values.length > 10) { break; } Sleep(1000); } // 查詢全部數據 Log(DBExec('select * from tick')); // 查詢第一個數據 Log(DBExec('select * from tick limit 1')); // 查詢前兩個數據 Log(DBExec('select * from tick limit 0,2')); // 刪除第一個數據 Log(DBExec('DELETE FROM tick WHERE id=1;')); // 修改第二個數據 Log(DBExec('UPDATE tick SET High=10000 WHERE id=2')); // 查詢全部數據 let allDate = DBExec('select * from tick') Log(allDate); }
第三步:運行策略
以Windows爲例,運行策略以後,會在託管者目錄的「\logs\storage」目錄中生成一個以機器人編號命名的文件夾,打開該文件夾,裏面有一個以「.db3」做爲後綴的文件,這個文件就是發明者量化內置數據庫的文件。如下圖所示:
上面的代碼首先建立了一個以「tick」命名的數據表,而後給該表添加tick數據字段,接着在循環中從交易所獲取tick數據,並把這些數據插入到「tick」數據表中,同時判斷該數據表中的數據量超過10個就跳出循環。最後分別用5個SQLite命令查詢、刪除、修改數據表中的數據。並在日誌中打印出來,如下圖所示:
第四步:建立狀態欄
最後咱們增長一些代碼,經過獲取發明者量化數據庫中的數據,給策略建立一個狀態欄,把數據更直觀的展現出來,新增代碼以下:
// 建立狀態欄 let table = { type: 'table', title: '幣安Tick數據', cols: allDate.columns, rows: allDate.values } LogStatus('`' + JSON.stringify(table) + '`');
上面的代碼經過數據庫中的數據,建立了一個「幣安Tick數據」表。其中數據庫中的「columns」字段表明狀態欄中的「行」,「values」字段表明狀態欄中的「列」。以下圖所示:
完整策略代碼
/*backtest start: 2020-07-19 00:00:00 end: 2020-08-17 23:59:00 period: 15m basePeriod: 15m exchanges: [{"eid":"Binance","currency":"LTC_USDT"}] */ function main() { Log(DBExec('DROP TABLE tick;')); // 訂閱合約 _C(exchange.SetContractType, 'swap'); // 建立數據表 DBExec('CREATE TABLE IF NOT EXISTS "tick" (id INTEGER PRIMARY KEY AUTOINCREMENT,'.concat( 'High FLOAT not NULL,', 'Low FLOAT not NULL,', 'Sell FLOAT not NULL,', 'Buy FLOAT not NULL,', 'Last FLOAT not NULL,', 'Volume INTEGER not NULL,', 'Time INTEGER not NULL);' )); // 獲取10個tick數據 while (true) { let tick = exchange.GetTicker(); // 在tick表中增長數據 DBExec(`INSERT INTO tick(High, Low, Sell, Buy, Last, Volume, Time) values(${tick.High}, ${tick.Low}, ${tick.Sell}, ${tick.Buy}, ${tick.Last}, ${tick.Volume}, ${tick.Time})`); // 查詢全部數據 let allDate = DBExec('select * from tick'); if (allDate.values.length > 10) { break; } Sleep(1000); } // 查詢全部數據 Log(DBExec('select * from tick')); // 查詢第一個數據 Log(DBExec('select * from tick limit 1')); // 查詢前兩個數據 Log(DBExec('select * from tick limit 0,2')); // 刪除第一個數據 Log(DBExec('DELETE FROM tick WHERE id=1;')); // 修改第二個數據 Log(DBExec('UPDATE tick SET High=10000 WHERE id=2')); // 查詢全部數據 let allDate = DBExec('select * from tick') Log(allDate); // 建立狀態欄 let table = { type: 'table', title: '幣安Tick數據', cols: allDate.columns, rows: allDate.values } LogStatus('`' + JSON.stringify(table) + '`'); }
點擊該連接 https://www.fmz.com/strategy/265906 便可複製完整策略代碼。
內存數據庫
若是操做的數據不但願永久保存到磁盤,能夠在SQL語句前加上:
符號就能夠在內存數據庫裏操做, 機器人重啓後數據重置
DBExec(":select 1,2,3");
總結
數據庫不只能夠承載海量數據,更能承載衆多量化交易愛好者的寬客夢想。對於數據庫的使用絕非僅限於本文的例子,更多使用方法能夠參考SQLite教程,以及發明者量化後續推出的系列文章。