Python 精簡多品種 MACD 趨勢策略框架(註釋版)
Python超級精簡的多品種MACD趨勢策略框架, 代碼超級精簡, 註釋超級詳細囉嗦。 >_<!
需要引用 python版CTP商品期貨交易類庫(支持2/3 測試版) 模板, 模板的代碼有JS語言版本的註釋(邏輯一致)。
- 代碼附上:
'''
/*backtest
start: 2016-01-30
end: 2016-12-30
period: 1440
periodBase: 60
mode: 0
*/
'''
# 以上 爲設置的回測默認參數
# ------------
# 作者: Zero
# ------------
class Trader: # 聲明一個 python 類
def __init__(self, q, symbol): # Trader 類的構造函數, 參數 self(代表類實例化以後的對象) , q(引用 商品期貨交易類庫 模板 構造的 交易處理對象), symbol (商品期貨合約代碼)
self.q = q # 給 構造函數 構造的對象添加屬性q ,並用 參數 q 賦值。
self.symbol = symbol # 同上, 給構造的對象添加symbol 屬性,並用 參數 symbol 賦值。
self.position = 0 # 添加 屬性 position 賦值 0 , 該屬性是用於 記錄倉位數量。
self.isPending = False # 添加 屬性 isPending 賦值 False , 該屬性用於標記 對象狀態,是否是掛起狀態。
def onOpen(self, task, ret): # 類成員函數 , 執行開倉完成後的 回調函數(即 在模擬多線程處理交易的對象q 完成當前任務後 回調該 onOpen 函數處理一些開倉後的工作。)
if ret: # 交易處理對象 q ,會在處理交易任務完成後 回調onOpen ,傳入2個參數 ,第一個 就是由形參task 接收,具體數據爲執行的交易任務數據, 第二個參數就是 交易完成的情況
# 如果ret 爲有效數據(交易未成功 ret 爲 None),則處理 if 塊內代碼
self.position = ret['position']['Amount'] * (1 if (ret['position']['Type'] == PD_LONG or ret['position']['Type'] == PD_LONG_YD) else -1)
# 對調用該函數的對象的屬性position 賦值, ret['position']['Amount'] 爲交易後的持倉數量,根據 ret['position']['Type'] 持倉類型 等於 PD_LONG (持多倉)
# 還是 PD_LONG_YD(持空倉)去選擇 ret['position']['Amount'] 乘 1 還是 -1 ,最後把持倉數量賦值 給 position ,(作用是通過 position 區分持多倉還是持空倉)
Log(task["desc"], "Position:", self.position, ret) # 打印多個項: q 執行的任務的數據 task 字典的 描述內容,賦值過的position , q 對象處理的交易的完成情況(當前持倉信息)
self.isPending = False # 給 isPending 賦值 False 即代表 當前的 品種 交易邏輯 處於 非掛起狀態,可以接受任務。
def onCover(self, task, ret): # 平倉任務完成後要執行的回調函數, 參數同 onOpen 一致
self.isPending = False # 設置 isPending 爲False 當前 品種的交易邏輯爲 非掛起狀態,可以接受任務。
self.position = 0 # 給記錄持倉的變量 position 賦值 爲0, 即沒有持倉。
Log(task["desc"], ret) # 打印 交易處理對象 q 本次處理的任務 描述(desc), 完成處理的結果(ret)
def onTick(self): # 主要交易邏輯 , MACD 策略核心。
if self.isPending: # 如果 Trader類構造的當前邏輯對象的 isPending 屬性 爲True 則代表 目前有 交易任務 正在 交易處理對象q 隊列中執行。
return # 交易邏輯處於掛起狀態,不做任何處理。
ct = exchange.SetContractType(self.symbol) # 根據構造函數 傳入的 symbol 賦值給 對象成員屬性symbol 傳入 API 函數 即:交易平臺對象exchange 的 SetContractType 函數, 用來設置操作的合約類型
if not ct: # SetContractType 函數切換 交易合約代碼(symbol) 成功後會返回 該合約的詳細信息, 如果返回 None , 即 not ct 爲真, 則立即返回,等待下一輪。
return
r = exchange.GetRecords() # 聲明 變量 r (用來儲存K線數據) , 調用 API 函數 GetRecords 獲取 設置後的該合約的 K線數據,賦值給 r 。
if not r or len(r) < 35: # 如果 r 爲 None 或者 r的長度小於35 (因爲要計算 MACD 指標,必須有足夠數量的K線bar,小於35 無法計算)
return # 立即返回 ,下一輪處理。
macd = TA.MACD(r) # 調用 API 指標函數 TA.MACD ,傳入實參 r ,計算MACD 指標數據 ,並賦值給 變量 macd 。(TA.MACD 成功返回的數據是一個 二維數組 [dif, dea, 量柱])
# 不明白 dif 、dea 的可以百度 MACD 指標
diff = macd[0][-2] - macd[1][-2] # 計算dif和dea的差值 (注意,此處計算使用的是計算出的倒數第二bar的dif、dea,因爲倒數第一bar 的K線是一直變動的,macd指標也是一直在變動,準確不變的是倒數第二bar)
if abs(diff) > 0 and self.position == 0: # 開倉,如果此刻 指標 (dif - dea)的絕對值 大於0 並且 沒有持倉(即: position 等於 0)
self.isPending = True # 改變狀態 設置爲 掛起狀態, isPending = True
self.q.pushTask(exchange, self.symbol, ("buy" if diff > 0 else "sell"), 1, self.onOpen) # 調用交易處理對象q的成員函數pushTask發出開倉交易任務,參數:exchange交易平臺對象(傳入即可)
# self.symbol 合約代碼(構造時傳入), 根據diff 大於0 還是小於0 去設置 "buy" 或者 "sell", 1 這個參數指的是下單量1手, self.onOpen 傳入回調函數的引用
if abs(diff) > 0 and ((diff > 0 and self.position < 0) or (diff < 0 and self.position > 0)): # 平倉,如果此刻指標(dif - dea)的絕對值 大於0 並且 and後的 條件任意一個成立執行if 塊內代碼,
# diff 大於0 並且 持空倉 或者 diff小於0 並且 持多倉 ,均爲 平倉條件。
self.isPending = True # 設置 爲掛起狀態。
self.q.pushTask(exchange, self.symbol, ("closebuy" if self.position > 0 else "closesell"), 1, self.onCover) # 發送平倉交易任務, 參數 同上發送開倉任務。
def main(): # 入口函數
q = ext.NewTaskQueue() # 調用 python版商品期貨交易類庫 模板 的導出函數(即接口), ext.NewTaskQueue 返回一個 構造的 交易處理對象。引用 給 變量 q
Log(_C(exchange.GetAccount)) # 啓動 調用 _C 容錯函數 ,傳入要容錯處理的 API : GetAccount 函數, 返回賬戶信息 由 Log 函數 輸出到日誌 , 顯示。
tasks = [] # 聲明一個 空數組 tasks 。
for symbol in ["MA701", "rb1701"]: # 遍歷 數組 ["MA701", "rb1701"] 中的元素 , 每次循環 把其中的元素symbol 和 交易處理對象q 作爲 參數 傳遞給 Trader 類的構造函數 去構造交易邏輯對象。
tasks.append(Trader(q, symbol)) # 構造好的 交易邏輯對象 壓入 tasks 數組。用以 循環遍歷執行處理。
while True: # 設置一個 while 死循環
if exchange.IO("status"): # 每次循環 調用 API 函數 IO ,傳入參數 "status" 去檢測 與 期貨公司 前置服務器 的連接狀態(CTP協議), 返回 True 即連接 交易服務器 和 行情服務器
for t in tasks: # 遍歷 tasks 數組, 調用 構造 的 Trader 類的 對象 t的成員函數 onTick ,不斷檢測行情, 擇時開倉 、平倉。
t.onTick() # 見 Trader 類 中的 onTick 函數
q.poll() # 調用 交易處理對象 q 的成員函數 poll 去處理 q 對象內的隊列中的 交易任務。
Sleep(1000) # 程序每次 while 循環 暫停一段時間 Sleep(1000) 即: 暫停1秒 (1000毫秒),以免訪問 API 過於 頻繁。
歡迎提出BUG ,建議。