如何使用 Python 來自動交易加密貨幣
然而,不像紐約證券交易所這樣的傳統證券交易所一樣,有一段固定的交易時間。對於加密貨幣而言,則是 7×24 小時交易,這使得任何人都無法獨自盯著市場。
在以前,我經常思考與加密貨幣交易相關的問題:
- 一夜之間發生了什麼?
- 為什麼沒有日誌記錄?
- 為什麼下單?
- 為什麼不下單?
通常的解決手段是使用加密交易機器人,當在你做其他事情時,例如睡覺、與家人在一起或享受空閑時光,代替你下單。雖然有很多商業解決方案可用,但是我選擇開源的解決方案,因此我編寫了加密交易機器人 Pythonic。 正如去年 我寫過的文章 一樣,「Pythonic 是一種圖形化編程工具,它讓用戶可以輕鬆使用現成的函數模塊來創建 Python 應用程序。」 最初它是作為加密貨幣機器人使用,並具有可擴展的日誌記錄引擎以及經過精心測試的可重用部件,例如調度器和計時器。
開始
本教程將教你如何開始使用 Pythonic 進行自動交易。我選擇 幣安 交易所的 波場 與 比特幣 交易對為例。我之所以選擇這個加密貨幣對,是因為它們彼此之間的波動性大,而不是出於個人喜好。
機器人將根據 指數移動平均 (EMA)來做出決策。
![TRX/BTC 1-hour candle chart](/data/attachment/album/202103/28/094050ln73sss1s9st3ssf.png "TRX/BTC 1-hour candle chart")
TRX/BTC 1 小時 K 線圖
EMA 指標通常是一個加權的移動平均線,可以對近期價格數據賦予更多權重。儘管移動平均線可能只是一個簡單的指標,但我對它很有經驗。
上圖中的紫色線顯示了 EMA-25 指標(這表示要考慮最近的 25 個值)。
機器人監視當前的 EMA-25 值(t0)和前一個 EMA-25 值(t-1)之間的差距。如果差值超過某個值,則表示價格上漲,機器人將下達購買訂單。如果差值低於某個值,則機器人將下達賣單。
差值將是做出交易決策的主要指標。在本教程中,它稱為交易參數。
工具鏈
將在本教程使用如下工具:
- 幣安專業交易視圖(已經有其他人做了數據可視化,所以不需要重複造輪子)
- Jupyter 筆記本:用於數據科學任務
- Pythonic:作為整體框架
- PythonicDaemon :作為終端運行(僅適用於控制台和 Linux)
數據挖掘
為了使加密貨幣交易機器人儘可能做出正確的決定,以可靠的方式獲取資產的 美國線 (OHLC)數據是至關重要。你可以使用 Pythonic 的內置元素,還可以根據自己邏輯來對其進行擴展。
一般的工作流程:
- 與幣安時間同步
- 下載 OHLC 數據
- 從文件中把 OHLC 數據載入到內存
- 比較數據集並擴展更新數據集
這個工作流程可能有點誇張,但是它能使得程序更加健壯,甚至在停機和斷開連接時,也能平穩運行。
一開始,你需要 幣安 OHLC 查詢 元素和一個 基礎操作 元素來執行你的代碼。
![Data-mining workflow](/data/attachment/album/202103/28/094051qdkqluhu7sqkus73.png "Data-mining workflow")
數據挖掘工作流程
OHLC 查詢設置為每隔一小時查詢一次 TRXBTC 資產對(波場/比特幣)。
![Configuration of the OHLC query element](/data/attachment/album/202103/28/094051lwhwz6qyhg8zspg6.png "Configuration of the OHLC query element")
配置 OHLC 查詢元素
其中輸出的元素是 Pandas DataFrame。你可以在 基礎操作 元素中使用 輸入 變數來訪問 DataFrame。其中,將 Vim 設置為 基礎操作 元素的默認代碼編輯器。
![Basic Operation element set up to use Vim](/data/attachment/album/202103/28/094052xn23s2x4xoj24d6f.png "Basic Operation element set up to use Vim")
使用 Vim 編輯基礎操作元素
具體代碼如下:
import pickle, pathlib, os
import pandas as pd
outout = None
if isinstance(input, pd.DataFrame):
file_name = 'TRXBTC_1h.bin'
home_path = str(pathlib.Path.home())
data_path = os.path.join(home_path, file_name)
try:
df = pickle.load(open(data_path, 'rb'))
n_row_cnt = df.shape[0]
df = pd.concat([df,input], ignore_index=True).drop_duplicates(['close_time'])
df.reset_index(drop=True, inplace=True)
n_new_rows = df.shape[0] - n_row_cnt
log_txt = '{}: {} new rows written'.format(file_name, n_new_rows)
except:
log_txt = 'File error - writing new one: {}'.format(e)
df = input
pickle.dump(df, open(data_path, "wb" ))
output = df
首先,檢查輸入是否為 DataFrame 元素。然後在用戶的家目錄(~/
)中查找名為 TRXBTC_1h.bin
的文件。如果存在,則將其打開,執行新代碼段(try
部分中的代碼),並刪除重複項。如果文件不存在,則觸發異常並執行 except
部分中的代碼,創建一個新文件。
只要啟用了複選框 日誌輸出 ,你就可以使用命令行工具 tail
查看日誌記錄:
$ tail -f ~/Pythonic_2020/Feb/log_2020_02_19.txt
出於開發目的,現在跳過與幣安時間的同步和計劃執行,這將在下面實現。
準備數據
下一步是在單獨的 網格 中處理評估邏輯。因此,你必須藉助 返回元素 將 DataFrame 從網格 1 傳遞到網格 2 的第一個元素。
在網格 2 中,通過使 DataFrame 通過 基礎技術分析 元素,將 DataFrame 擴展包含 EMA 值的一列。
![Technical analysis workflow in Grid 2](/data/attachment/album/202103/28/094052xq7vemsqm9evasx9.png "Technical analysis workflow in Grid 2")
在網格 2 中技術分析工作流程
配置技術分析元素以計算 25 個值的 EMA。
![Configuration of the technical analysis element](/data/attachment/album/202103/28/094053x0wecdexbcc7idhy.png "Configuration of the technical analysis element")
配置技術分析元素
當你運行整個程序並開啟 技術分析 元素的調試輸出時,你將發現 EMA-25 列的值似乎都相同。
![Missing decimal places in output](/data/attachment/album/202103/28/094053c6poxlr4exxe4foc.png "Missing decimal places in output")
輸出中精度不夠
這是因為調試輸出中的 EMA-25 值僅包含六位小數,即使輸出保留了 8 個位元組完整精度的浮點值。
為了能進行進一步處理,請添加 基礎操作 元素:
![Workflow in Grid 2](/data/attachment/album/202103/28/094053w4u44j6wqz6fr3kh.png "Workflow in Grid 2")
網格 2 中的工作流程
使用 基礎操作 元素,將 DataFrame 與添加的 EMA-25 列一起轉儲,以便可以將其載入到 Jupyter 筆記本中;
![Dump extended DataFrame to file](/data/attachment/album/202103/28/094054kc1y44yvcvgycnye.png "Dump extended DataFrame to file")
將擴展後的 DataFrame 存儲到文件中
評估策略
在 Juypter 筆記本中開發評估策略,讓你可以更直接地訪問代碼。要載入 DataFrame,你需要使用如下代碼:
![Representation with all decimal places](/data/attachment/album/202103/28/094054s7ozuzh8ebzpeb9o.png "Representation with all decimal places")
用全部小數位表示
你可以使用 iloc 和列名來訪問最新的 EMA-25 值,並且會保留所有小數位。
你已經知道如何來獲得最新的數據。上面示例的最後一行僅顯示該值。為了能將該值拷貝到不同的變數中,你必須使用如下圖所示的 .at
方法方能成功。
你也可以直接計算出你下一步所需的交易參數。
![Buy/sell decision](/data/attachment/album/202103/28/094054jnb1dc3caipaksf9.png "Buy/sell decision")
買賣決策
確定交易參數
如上面代碼所示,我選擇 0.009 作為交易參數。但是我怎麼知道 0.009 是決定交易的一個好參數呢? 實際上,這個參數確實很糟糕,因此,你可以直接計算出表現最佳的交易參數。
假設你將根據收盤價進行買賣。
![Validation function](/data/attachment/album/202103/28/094055jx5yz5ctp8qctej9.png "Validation function")
回測功能
在此示例中,buy_factor
和 sell_factor
是預先定義好的。因此,發散思維用直接計算出表現最佳的參數。
![Nested for loops for determining the buy and sell factor](/data/attachment/album/202103/28/094055b6i0t6cqv5ffhbwq.png "Nested for loops for determining the buy and sell factor")
嵌套的 for 循環,用於確定購買和出售的參數
這要跑 81 個循環(9x9),在我的機器(Core i7 267QM)上花費了幾分鐘。
![System utilization while brute forcing](/data/attachment/album/202103/28/094055bquv9iyqi0rbtqth.png "System utilization while brute forcing")
在暴力運算時系統的利用率
在每個循環之後,它將 buy_factor
、sell_factor
元組和生成的 profit
元組追加到 trading_factors
列表中。按利潤降序對列表進行排序。
![Sort profit with related trading factors in descending order](/data/attachment/album/202103/28/094056ze0gzqu9vgg22v2w.png "Sort profit with related trading factors in descending order")
將利潤與相關的交易參數按降序排序
當你列印出列表時,你會看到 0.002 是最好的參數。
![Sorted list of trading factors and profit](/data/attachment/album/202103/28/094056oxlihvaiixwg84jw.png "Sorted list of trading factors and profit")
交易要素和收益的有序列表
當我在 2020 年 3 月寫下這篇文章時,價格的波動還不足以呈現出更理想的結果。我在 2 月份得到了更好的結果,但即使在那個時候,表現最好的交易參數也在 0.002 左右。
分割執行路徑
現在開始新建一個網格以保持邏輯清晰。使用 返回 元素將帶有 EMA-25 列的 DataFrame 從網格 2 傳遞到網格 3 的 0A 元素。
在網格 3 中,添加 基礎操作 元素以執行評估邏輯。這是該元素中的代碼:
![Implemented evaluation logic](/data/attachment/album/202103/28/094056ofn26ububue22fuz.png "Implemented evaluation logic")
實現評估策略
如果輸出 1
表示你應該購買,如果輸出 2
則表示你應該賣出。 輸出 0
表示現在無需操作。使用 分支 元素來控制執行路徑。
![Branch element: Grid 3 Position 2A](/data/attachment/album/202103/28/094056q3tp37wwlz0b0on1.png "Branch element: Grid 3 Position 2A")
分支元素:網格 3,2A 位置
因為 0
和 -1
的處理流程一樣,所以你需要在最右邊添加一個分支元素來判斷你是否應該賣出。
![Branch element: Grid 3 Position 3B](/data/attachment/album/202103/28/094057stk7jj4nlwnk2puw.png "Branch element: Grid 3 Position 3B")
分支元素:網格 3,3B 位置
網格 3 應該現在如下圖所示:
![Workflow on Grid 3](/data/attachment/album/202103/28/094057fe1wnzar6x664rnz.png "Workflow on Grid 3")
網格 3 的工作流程
下單
由於無需在一個周期中購買兩次,因此必須在周期之間保留一個持久變數,以指示你是否已經購買。
你可以利用 棧 元素來實現。顧名思義,棧元素表示可以用任何 Python 數據類型來放入的基於文件的棧。
你需要定義棧僅包含一個布爾類型,該布爾類型決定是否購買了(True
)或(False
)。因此,你必須使用 False
來初始化棧。例如,你可以在網格 4 中簡單地通過將 False
傳遞給棧來進行設置。
![Forward a False-variable to the subsequent Stack element](/data/attachment/album/202103/28/094057yiszipdxx9dxizfw.png "Forward a False-variable to the subsequent Stack element")
將 False 變數傳輸到後續的棧元素中
在分支樹後的棧實例可以進行如下配置:
![Configuration of the Stack element](/data/attachment/album/202103/28/094058xquob8epsfqzopif.png "Configuration of the Stack element")
設置棧元素
在棧元素設置中,將 對輸入的操作 設置成 無 。否則,布爾值將被 1
或 0
覆蓋。
該設置確保僅將一個值保存於棧中(True
或 False
),並且只能讀取一個值(為了清楚起見)。
在棧元素之後,你需要另外一個 分支 元素來判斷棧的值,然後再放置 幣安訂單 元素。
![Evaluate the variable from the stack](/data/attachment/album/202103/28/094058vwu6v7ii6kvwwx9a.png "Evaluate the variable from the stack")
判斷棧中的變數
將幣安訂單元素添加到分支元素的 True
路徑。網格 3 上的工作流現在應如下所示:
![Workflow on Grid 3](/data/attachment/album/202103/28/094059oreu9efclcs5glo9.png "Workflow on Grid 3")
網格 3 的工作流程
幣安訂單元素應如下配置:
![Configuration of the Binance Order element](/data/attachment/album/202103/28/094059cc5ntnvbsqriu43w.png "Configuration of the Binance Order element")
編輯幣安訂單元素
你可以在幣安網站上的帳戶設置中生成 API 和密鑰。
![Creating an API key in Binance](/data/attachment/album/202103/28/094059dlcgcx40e8eeb4bu.png "Creating an API key in Binance")
在幣安賬戶設置中創建一個 API 密鑰
在本文中,每筆交易都是作為市價交易執行的,交易量為 10,000 TRX(2020 年 3 月約為 150 美元)(出於教學的目的,我通過使用市價下單來演示整個過程。因此,我建議至少使用限價下單。)
如果未正確執行下單(例如,網路問題、資金不足或貨幣對不正確),則不會觸發後續元素。因此,你可以假定如果觸發了後續元素,則表示該訂單已下達。
這是一個成功的 XMRBTC 賣單的輸出示例:
![Output of a successfully placed sell order](/data/attachment/album/202103/28/094059o026ikbaqx3ak6e4.png "Output of a successfully placed sell order")
成功賣單的輸出
該行為使後續步驟更加簡單:你可以始終假設只要成功輸出,就表示訂單成功。因此,你可以添加一個 基礎操作 元素,該元素將簡單地輸出 True 並將此值放入棧中以表示是否下單。
如果出現錯誤的話,你可以在日誌信息中查看具體細節(如果啟用日誌功能)。
![Logging output of Binance Order element](/data/attachment/album/202103/28/094100lksn1anu9npa99aw.png "Logging output of Binance Order element")
幣安訂單元素中的輸出日誌信息
調度和同步
對於日程調度和同步,請在網格 1 中將整個工作流程置於 幣安調度器 元素的前面。
![Binance Scheduler at Grid 1, Position 1A](/data/attachment/album/202103/28/094100vg5oseqx5mmongua.png "Binance Scheduler at Grid 1, Position 1A")
在網格 1,1A 位置的幣安調度器
由於幣安調度器元素只執行一次,因此請在網格 1 的末尾拆分執行路徑,並通過將輸出傳遞迴幣安調度器來強制讓其重新同步。
![Grid 1: Split execution path](/data/attachment/album/202103/28/094100wq624z79q2732w6b.png "Grid 1: Split execution path")
網格 1:拆分執行路徑
5A 元素指向 網格 2 的 1A 元素,並且 5B 元素指向網格 1 的 1A 元素(幣安調度器)。
部署
你可以在本地計算機上全天候 7×24 小時運行整個程序,也可以將其完全託管在廉價的雲系統上。例如,你可以使用 Linux/FreeBSD 雲系統,每月約 5 美元,但通常不提供圖形化界面。如果你想利用這些低成本的雲,可以使用 PythonicDaemon,它能在終端中完全運行。
![PythonicDaemon console interface](/data/attachment/album/202103/28/094101rmm0m11k2so4r1yp.png "PythonicDaemon console interface")
PythonicDaemon 控制台
PythonicDaemon 是基礎程序的一部分。要使用它,請保存完整的工作流程,將其傳輸到遠程運行的系統中(例如,通過 安全拷貝協議 SCP),然後把工作流程文件作為參數來啟動 PythonicDaemon:
$ PythonicDaemon trading_bot_one
為了能在系統啟動時自啟 PythonicDaemon,可以將一個條目添加到 crontab 中:
# crontab -e
![Crontab on Ubuntu Server](/data/attachment/album/202103/28/094101gpj5vpvp3vps33ut.png "Crontab on Ubuntu Server")
在 Ubuntu 伺服器上的 Crontab
下一步
正如我在一開始時所說的,本教程只是自動交易的入門。對交易機器人進行編程大約需要 10% 的編程和 90% 的測試。當涉及到讓你的機器人用金錢交易時,你肯定會對編寫的代碼再三思考。因此,我建議你編碼時要儘可能簡單和易於理解。
如果你想自己繼續開發交易機器人,接下來所需要做的事:
- 收益自動計算(希望你有正收益!)
- 計算你想買的價格
- 比較你的預訂單(例如,訂單是否填寫完整?)
你可以從 GitHub 上獲取完整代碼。
via: https://opensource.com/article/20/4/python-crypto-trading-bot
作者:Stephan Avenwedde 選題:lujun9972 譯者:wyxplus 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive