策略廣場上的Python策略不多,這裏編寫了一個Python版本的網格策略。策略原理十分簡單,在一個價格區間內固定價格距離產生一系列的網格節點,當行情變化時,價格到達一個網格節點價格位置,就掛一個買入訂單。當這個訂單成交時,即按照掛單的價格加上利潤差價,掛出平倉的賣單訂單。捕捉在設置的價格區間內的波動。
網格策略的風險不用多說,任何網格類型的策略都是屬於賭價格在某個區間震盪,一旦價格衝出網格範圍,可能造成嚴重浮虧。所以寫該策略的目的在於,提供Python策略編寫思路上或者程序設計上的參考。該策略僅僅用於學習,實盤可能有很大風險。
策略思路講解直接寫在了策略代碼註釋上。
策略代碼
'''backtest
start: 2019-07-01 00:00:00
end: 2020-01-03 00:00:00
period: 1m
exchanges: [{"eid":"OKEX","currency":"BTC_USDT"}]
'''
import json
# 參數
beginPrice = 5000 # 網格區間開始價格
endPrice = 8000 # 網格區間結束價格
distance = 20 # 每個網格節點的價格距離
pointProfit = 50 # 每個網格節點的利潤差價
amount = 0.01 # 每個網格節點的掛單量
minBalance = 300 # 賬戶最小資金餘額(買入時)
# 全局變量
arrNet = []
arrMsg = []
acc = None
def findOrder (orderId, NumOfTimes, ordersList = []) :
for j in range(NumOfTimes) :
orders = None
if len(ordersList) == 0:
orders = _C(exchange.GetOrders)
else :
orders = ordersList
for i in range(len(orders)):
if orderId == orders[i]["Id"]:
return True
Sleep(1000)
return False
def cancelOrder (price, orderType) :
orders = _C(exchange.GetOrders)
for i in range(len(orders)) :
if price == orders[i]["Price"] and orderType == orders[i]["Type"]:
exchange.CancelOrder(orders[i]["Id"])
Sleep(500)
def checkOpenOrders (orders, ticker) :
global arrNet, arrMsg
for i in range(len(arrNet)) :
if not findOrder(arrNet[i]["id"], 1, orders) and arrNet[i]["state"] == "pending" :
orderId = exchange.Sell(arrNet[i]["coverPrice"], arrNet[i]["amount"], arrNet[i], ticker)
if orderId :
arrNet[i]["state"] = "cover"
arrNet[i]["id"] = orderId
else :
# 撤銷
cancelOrder(arrNet[i]["coverPrice"], ORDER_TYPE_SELL)
arrMsg.append("掛單失敗!" + json.dumps(arrNet[i]) + ", time:" + _D())
def checkCoverOrders (orders, ticker) :
global arrNet, arrMsg
for i in range(len(arrNet)) :
if not findOrder(arrNet[i]["id"], 1, orders) and arrNet[i]["state"] == "cover" :
arrNet[i]["id"] = -1
arrNet[i]["state"] = "idle"
Log(arrNet[i], "節點平倉,重置爲空閒狀態。", "#FF0000")
def onTick () :
global arrNet, arrMsg, acc
ticker = _C(exchange.GetTicker) # 每次獲取當前最新的行情
for i in range(len(arrNet)): # 遍歷所有網格節點,根據當前行情,找出需要掛單的位置,掛買單。
if i != len(arrNet) - 1 and arrNet[i]["state"] == "idle" and ticker.Sell > arrNet[i]["price"] and ticker.Sell < arrNet[i + 1]["price"]:
acc = _C(exchange.GetAccount)
if acc.Balance < minBalance : # 如果錢不夠了,只能跳出,什麼都不做了。
arrMsg.append("資金不足" + json.dumps(acc) + "!" + ", time:" + _D())
break
orderId = exchange.Buy(arrNet[i]["price"], arrNet[i]["amount"], arrNet[i], ticker) # 掛買單
if orderId :
arrNet[i]["state"] = "pending" # 如果買單掛單成功,更新網格節點狀態等信息
arrNet[i]["id"] = orderId
else :
# 撤單
cancelOrder(arrNet[i]["price"], ORDER_TYPE_BUY) # 使用撤單函數撤單
arrMsg.append("掛單失敗!" + json.dumps(arrNet[i]) + ", time:" + _D())
Sleep(1000)
orders = _C(exchange.GetOrders)
checkOpenOrders(orders, ticker) # 檢測所有買單的狀態,根據變化做出處理。
Sleep(1000)
orders = _C(exchange.GetOrders)
checkCoverOrders(orders, ticker) # 檢測所有賣單的狀態,根據變化做出處理。
# 以下爲構造狀態欄信息,可以查看FMZ API 文檔。
tbl = {
"type" : "table",
"title" : "網格狀態",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
for i in range(len(arrNet)) :
tbl["rows"].append([i, json.dumps(arrNet[i])])
errTbl = {
"type" : "table",
"title" : "記錄",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
orderTbl = {
"type" : "table",
"title" : "orders",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
while len(arrMsg) > 20 :
arrMsg.pop(0)
for i in range(len(arrMsg)) :
errTbl["rows"].append([i, json.dumps(arrMsg[i])])
for i in range(len(orders)) :
orderTbl["rows"].append([i, json.dumps(orders[i])])
LogStatus(_D(), "\n", acc, "\n", "arrMsg length:", len(arrMsg), "\n", "`" + json.dumps([tbl, errTbl, orderTbl]) + "`")
def main (): # 策略執行從這裏開始
global arrNet
for i in range(int((endPrice - beginPrice) / distance)): # for 這個循環根據參數構造了網格的數據結構,是一個列表,儲存每個網格節點,每個網格節點的信息如下:
arrNet.append({
"price" : beginPrice + i * distance, # 該節點的價格
"amount" : amount, # 訂單數量
"state" : "idle", # pending / cover / idle # 節點狀態
"coverPrice" : beginPrice + i * distance + pointProfit, # 節點平倉價格
"id" : -1, # 節點當前相關的訂單的ID
})
while True: # 構造好網格數據結構後,進入策略主要循環
onTick() # 主循環上的處理函數,主要處理邏輯
Sleep(500) # 控制輪詢頻率
策略主要設計思路是,根據自己維護的這個網格數據結構,對比GetOrders
接口返回的當前掛單列表。分析掛出的訂單變化(成交與否),更新網格數據結構,做出後續操作。並且掛出的訂單不會撤銷,直到成交,即使價格偏離也不撤銷,因爲數字貨幣市場經常有插針的情況,這些掛單也有可能接到插針的單子(如果交易所有掛單數量限制,那就要調整了)。
策略數據可視化,使用了LogStatus
函數把數據實時顯示在狀態欄上。
tbl = {
"type" : "table",
"title" : "網格狀態",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
for i in range(len(arrNet)) :
tbl["rows"].append([i, json.dumps(arrNet[i])])
errTbl = {
"type" : "table",
"title" : "記錄",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
orderTbl = {
"type" : "table",
"title" : "orders",
"cols" : ["節點索引", "詳細信息"],
"rows" : [],
}
構造了三個表格,第一個表格顯示當前網格數據結構中每個節點的信息,第二個表格顯示異常信息,第三個表格顯示交易所實際掛單信息。
回測測試
策略地址
策略僅供參考學習,回測測試,有興趣可以優化升級。