本期文章,帶來兩個移植的經典政策:冰山委托(買入/賣出)。政策移植自發明者量化交易平台經典政策JavaScript版冰山委托,政策位址:https://www.fmz.com/square/s:%E5%86%B0%E5%B1%B1%E5%A7%94%E6%89%98/1 。
引用JavaScript版本政策介紹:
冰山委托指的是投資者在進行大額交易時,為避免對市場造成過大沖擊,将大單委托自動拆為多筆委托,根據目前的最新買一/賣一價格和客戶設定的價格政策自動進行小單委托,在上一筆委托被全部成交或最新價格明顯偏離目前委托價時,自動重新進行委托。
很多交易所的交易頁面都自帶冰山委托工具,有着豐富的功能,不過如果想根據自身需求,定制一些功能或者改動一些功能,就需要更加靈活的工具了。發明者量化交易平台很好的解決了這個問題。政策廣場上Python政策不多,一些希望使用Python語言編寫交易工具、政策的交易者需要參考範例。是以,就把經典的冰山委托政策移植為Python版。
Python版冰山委托 - 買入 政策代碼及注釋
import random # 導入随機數庫
def CancelPendingOrders(): # CancelPendingOrders 函數作用是取消目前交易對所有挂單。
while True: # 循環檢測,調用GetOrders 函數,檢測目前挂單,如果orders 為空數組,即len(orders) 等于0,說明訂單全部取消了,可以退出函數,調用return 退出。
orders = _C(exchange.GetOrders)
if len(orders) == 0 :
return
for j in range(len(orders)): # 周遊目前挂單數組,調用取消訂單的函數CancelOrder,逐個取消挂單。
exchange.CancelOrder(orders[j]["Id"])
if j < len(orders) - 1: # 除了最後一個訂單,每次都執行Sleep 讓程式等待一會兒,避免撤單過于頻繁。
Sleep(Interval)
LastBuyPrice = 0 # 設定一個全局變量,記錄最近一次買入的價格。
InitAccount = None # 設定一個全局變量,記錄初始賬戶資産資訊。
def dispatch(): # 冰山委托邏輯的主要函數
global InitAccount, LastBuyPrice # 引用全局變量
account = None # 聲明一個變量,記錄實時擷取的賬戶資訊,用于對比計算。
ticker = _C(exchange.GetTicker) # 聲明一個變量,記錄最近行情。
LogStatus(_D(), "ticker:", ticker) # 在狀态欄輸出時間,最新行情
if LastBuyPrice > 0: # 當LastBuyPrice大于0時,即已經委托開始時,執行if條件内代碼。
if len(_C(exchange.GetOrders)) > 0: # 調用exchange.GetOrders 函數擷取目前所有挂單,判斷有挂單,執行if條件内代碼。
if ticker["Last"] > LastBuyPrice and ((ticker["Last"] - LastBuyPrice) / LastBuyPrice) > (2 * (EntrustDepth / 100)): # 檢測偏離程度,如果觸發該條件,執行if内代碼,撤單。
Log("偏離過多, 最新成交價:", ticker["Last"], "委托價", LastBuyPrice)
CancelPendingOrders()
else :
return True
else : # 如果沒有挂單,證明訂單完全成交了。
account = _C(exchange.GetAccount) # 擷取目前賬戶資産資訊。
Log("買單完成, 累計花費:", _N(InitAccount["Balance"] - account["Balance"]), "平均買入價:", _N((InitAccount["Balance"] - account["Balance"]) / (account["Stocks"] - InitAccount["Stocks"]))) # 列印交易資訊。
LastBuyPrice = 0 # 重置 LastBuyPrice為0
BuyPrice = _N(ticker["Buy"] * (1 - EntrustDepth / 100)) # 通過目前行情和參數,計算挂單價格。
if BuyPrice > MaxBuyPrice: # 判斷是否超過參數設定的最大價格
return True
if not account: # 如果 account 為 null ,執行if 語句内代碼,重新擷取目前資産資訊,複制給account
account = _C(exchange.GetAccount)
if (InitAccount["Balance"] - account["Balance"]) >= TotalBuyNet: # 判斷買入所花費的總錢數,是不是超過參數設定。
return False
RandomAvgBuyOnce = (AvgBuyOnce * ((100.0 - FloatPoint) / 100.0)) + (((FloatPoint * 2) / 100.0) * AvgBuyOnce * random.random()) # 随機數 0~1
UsedMoney = min(account["Balance"], RandomAvgBuyOnce, TotalBuyNet - (InitAccount["Balance"] - account["Balance"]))
BuyAmount = _N(UsedMoney / BuyPrice) # 計算買入數量
if BuyAmount < MinStock: # 判斷買入數量是否小于 參數上最小買入量限制。
return False
LastBuyPrice = BuyPrice # 記錄本次下單價格,指派給LastBuyPrice
exchange.Buy(BuyPrice, BuyAmount, "花費:¥", _N(UsedMoney), "上次成交價", ticker["Last"]) # 下單
return True
def main():
global LoopInterval, InitAccount # 引用 LoopInterval, InitAccount 全局變量
CancelPendingOrders() # 開始運作時,取消所有挂單
InitAccount = _C(exchange.GetAccount) # 初始記錄 開始時的賬戶資産
Log(InitAccount) # 列印初始賬戶資訊
if InitAccount["Balance"] < TotalBuyNet: # 如果初始時資産不足,則抛出錯誤,停止程式
raise Exception("賬戶餘額不足")
LoopInterval = max(LoopInterval, 1) # 設定LoopInterval至少為1
while dispatch(): # 主要循環,不停調用 冰山委托邏輯函數 dispatch ,當dispatch函數 return false 時才停止循環。
Sleep(LoopInterval * 1000) # 每次循環都暫停一下,控制輪詢頻率。
Log("委托全部完成", _C(exchange.GetAccount)) # 當循環執行跳出時,列印目前賬戶資産資訊。
Python版冰山委托 - 賣出
可以嘗試,讀一下「Python版冰山委托 - 賣出」的代碼,政策邏輯和買入的是一樣的,隻有略微差别。
import random
def CancelPendingOrders():
while True:
orders = _C(exchange.GetOrders)
if len(orders) == 0:
return
for j in range(len(orders)):
exchange.CancelOrder(orders[j]["Id"])
if j < len(orders) - 1:
Sleep(Interval)
LastSellPrice = 0
InitAccount = None
def dispatch():
global LastSellPrice, InitAccount
account = None
ticker = _C(exchange.GetTicker)
LogStatus(_D(), "ticker:", ticker)
if LastSellPrice > 0:
if len(_C(exchange.GetOrders)) > 0:
if ticker["Last"] < LastSellPrice and ((LastSellPrice - ticker["Last"]) / ticker["Last"]) > (2 * (EntrustDepth / 100)):
Log("偏離過多,最新成交價:", ticker["Last"], "委托價", LastSellPrice)
CancelPendingOrders()
else :
return True
else :
account = _C(exchange.GetAccount)
Log("買單完成,累計賣出:", _N(InitAccount["Stocks"] - account["Stocks"]), "平均賣出價:", _N((account["Balance"] - InitAccount["Balance"]) / (InitAccount["Stocks"] - account["Stocks"])))
LastSellPrice = 0
SellPrice = _N(ticker["Sell"] * (1 + EntrustDepth / 100))
if SellPrice < MinSellPrice:
return True
if not account:
account = _C(exchange.GetAccount)
if (InitAccount["Stocks"] - account["Stocks"]) >= TotalSellStocks:
return False
RandomAvgSellOnce = (AvgSellOnce * ((100.0 - FloatPoint) / 100.0)) + (((FloatPoint * 2) / 100.0) * AvgSellOnce * random.random())
SellAmount = min(TotalSellStocks - (InitAccount["Stocks"] - account["Stocks"]), RandomAvgSellOnce)
if SellAmount < MinStock:
return False
LastSellPrice = SellPrice
exchange.Sell(SellPrice, SellAmount, "上次成交價", ticker["Last"])
return True
def main():
global InitAccount, LoopInterval
CancelPendingOrders()
InitAccount = _C(exchange.GetAccount)
Log(InitAccount)
if InitAccount["Stocks"] < TotalSellStocks:
raise Exception("賬戶币數不足")
LoopInterval = max(LoopInterval, 1)
while dispatch():
Sleep(LoopInterval)
Log("委托全部完成", _C(exchange.GetAccount))
政策運作
使用wexApp模拟交易所測試:
買入
賣出
政策邏輯并不複雜,政策執行時,根據政策參數、當時行情價格,動态的挂單、撤單。當交易金額/币數達到、接近參數設定數量時,政策停止。政策代碼非常簡單,适合初學。有興趣的同學可以加以改造,設計成适合自己交易方式的政策。
政策為教學性質,實盤慎用。