目錄
- 一、前言
- 問題的引出
- MMQT模型的優勢
- 二、MMQT簡介
- 1.介面模塊
- 2.風控模塊
- 3.策略模塊
- 4.反饋模塊
- 三、MMQT的代碼實作
- 1.定義中間模塊(類)
- 1.初始化
- 2.獲取賬戶資訊、交易對資訊、訂單資訊
- 3.資料更新
- 4.創建訂單
- 5.獲取訂單狀態
- 6.撤銷訂單
- 7.獲取k線資訊
- 2.定義風控模塊(類)
- 3.定義策略模塊(類)
- 1.策略模塊初始化
- 2.技術分析及交易下單
- 3.反饋模塊
- 4.相關類實體化及程式運行
- 1.ccxt實體化
- 2.中間類、風控類、策略類實體化
- 3.調控程式
- 四、回測的代碼實作
- 1.獲取資料
- 2.資料清洗
- 3.模擬賬戶初始化
- 4.回測程式
- 五、總結與展望
- 1.MMQT的不足之處
- 2.致謝與參考資料
- 六、附錄:完整代碼
- 1.MMQT
- 2.回測
著作權宣告:如需對此文章代碼進行轉載請注明出處,若用于商業用途、論文寫作請私信或聯系作者郵箱940648114@qq.com
一、前言
問題的引出
量化交易是指以先進的數學模型替代人為的主觀判斷,利用計算機技術從龐大的歷史資料中海選能帶來超額收益的多種“大概率”事件以制定策略,極大地減少了投資者情緒波動的影響,避免在市場極度狂熱或悲觀的情況下作出非理性的投資決策,
數字貨幣是一種基于節點網路和數字加密演算法的虛擬貨幣價值的數字化表示,可以認為不由央行或當局發行,也不與法幣掛鉤,但由于被公眾所接受,所以可作為支付手段,也可以電子形式轉移、存盤或交易,
隨著美國的無限量化寬松政策以及疫情的持續爆發,美元和黃金市場持續低迷,導致了大量的避險資金涌入幣圈,造成了今年從四月份以來主流幣兇猛的漲勢,同時,伴隨著國際上對于位元幣等主流幣的認同聲逐漸增加,許多國外的金融機構都對于數字貨幣市場躍躍欲試,其中具有代表性是灰度信托,近幾年它一直在不可逆性持續增倉,并且在二級市場發售數字貨幣的基金信托,這更是催生了更多的大資金買家入場,其中不乏中國的很多基金公司,
由于數字貨幣24*7小時不間斷的交易市場的連續性,并且量化交易可以達到高頻交易的效果,從數字貨幣市場入手顯然是做量化的很好的起步點,目前數字貨幣市場仍然是不成熟的,平臺交易系統的宕機,k線插針依然是會偶爾出現,對于量化交易也是一種風險所在,不過對于數字貨幣進行量化交易總體來看依然是利大于弊,因為通過模型的回測訓練和時間序列的回測分析,我們可以在最短時間能嘗試到數百種模型中最合適的方式,甚至在本文介紹的Multi Module Quantitative Transaction(MMQT)框架下,我們可以把機器學習放在對時間序列的處理中,或許能達到傳統計量模型所難達到的效果,以下,我將依照模型框架構建順序介紹如何實作數字貨幣量化交易,
MMQT模型的優勢
- 通過python撰寫,可修改性強,兼容性強
- 多模塊的設計可以根據自身的需要進行模塊的替換
- 對運算要求低,在高頻交易中可以節省交易時間
- 對初學者上手快,較易學習
二、MMQT簡介
類似遺傳演算法一樣,在MMQT模型中,我把交易者在交易程序中的每個步驟轉化成模型的各個模塊,我先將整體框架展示一下:

通俗的來講,整個量化交易的程序實際上就是對交易人員的代替,通過穩定的客觀判斷決策來避免主觀判斷所造成的損失,而這個功能的實作主要由數個小模塊構成,利用這樣的模塊化量化系統可以很好的隨時進行拓展,也能很容易進行Debug,
1.介面模塊
通過這個模塊,我們可以獲取一切可以獲取的相關資訊,并且把從交易所得到的所有資訊轉化為我們交易系統中通用的語言,我們在這個模塊可以實作賬戶資訊、交易對資訊、訂單資訊、買賣交易、獲取實時行情對等等,通過這個模塊獲得的資訊和進行的操作,基本上能夠滿足我們的整個量化交易程序,
2.風控模塊
通過這個模塊,我們可以實作在每一次交易前進行對整體的風險把控并終止某些交易,我們在這個板塊可以實作的功能有倉位控制、余額報警、發送郵件微信等等,
3.策略模塊
通過這個板塊,我們可以進行資料的清洗并轉化為talib熟悉的時間序列,通過我們決定的策略進行交易,例如BBANDS布林線指標、DEMA雙移動平均線、EMA指數平均數等等,
4.反饋模塊
通過這個板塊,我們對已經開的交易訂單進行查詢并更新,更新自身的訂單串列,實作監控,本文中反饋模塊并沒有單獨定義成一個類,而是寫在了策略中,大家可以根據自己的需要自行定義類的內容和相應方法,
三、MMQT的代碼實作
本文用的賬號是火幣交易所,如果沒有賬號的可以先進行注冊或者通過ccxt實體化到你自己一直用的交易所,
ccxt是一個封裝了諸多數字貨幣交易平臺的api的開源庫,支持python、php、javascrit三種語言,github上可以下載原始碼,ccxt結構明確,易于使用,所有api被封裝成統一格式的介面,回傳資料被封裝成統一格式的字典,基本省去了api開發時間,MMQT模型就是避開了對于火幣平臺進行直接對接,減少了很大的開發代價,同時也使得模型適用于其他交易所,大家可以通過pip或brew進行安裝,資源還是很多的,
1.定義中間模塊(類)
這一步,我們會建立我們第一個類,在這個類,我們將實作MMQT框架中的介面模塊,同樣的,里面很多的資料都是通過實體化后的ccxt傳入的字典進行賦值的,并不需要太多麻煩的步驟,在這里,我們至少要寫上后面其他模塊需要資訊資料,也要定義一些最基本的開單撤單的方法,以下將進行逐步講解,
1.初始化
將實體化后的ccxt傳入我們的中間類進行中間類的實體化,并且定義一些比較重要的后面需要的引數,包括了交易幣種,交易精度,
class MidClass():
#初始化
def __init__(self,ThisExchange):
self.Exchange=ThisExchange
self.Symbol=ThisExchange.symbol
self.AmountPrecision=ThisExchange.AmountPrecision
self.PricePrecision=ThisExchange.PricePrecision
2.獲取賬戶資訊、交易對資訊、訂單資訊
因為方法都類似,就把獲取資訊的方法放在一起進行統一的說明,思路就是通過ccxt匯入我們所需要的資訊,
#獲得交易對行情資訊
def GetTicker(self):
self.High='___'
self.Low='___'
self.Buy='___'
self.Sell='___'
self.Last='___'
try:
self.Ticker=self.Exchange.fetchTicker(self.Symbol)
self.High=self.Ticker['high']
self.Low=self.Ticker['low']
self.Buy=self.Ticker['bid']
self.Sell=self.Ticker['ask']
self.Last=self.Ticker['last']
return True#只要有一個成功就回傳True
except:
return False#如果全都獲取不了回傳False
#獲得賬戶對于該交易對資訊
def GetAccount(self):
self.Account='___'
self.Balance='___'
self.FrozenBalance='___'
self.Stocks='___'
self.FrozenStocks='___'
self.SymbolStocksName=self.Symbol.split('/')[0]
self.SymbolBalanceName=self.Symbol.split('/')[1]
try:
self.Account=self.Exchange.fetchBalance()
self.Balance=self.Account[self.SymbolBalanceName]['free']
self.FrozenBalance=self.Account[self.SymbolBalanceName]['used']
self.Stocks=self.Account[self.SymbolStocksName]['free']
self.FrozenStocks=self.Account[self.SymbolStocksName]['used']
return True
except:
return False
感興趣的同學可以去ccxt中文手冊上看一下相關回傳的值,這里主要通過字典索引方式將我們基本上用得到的資料提取了出來,大家也可以根據自己需要再去讀取一些其他資料,比如市場深度等等,在文中的上述代碼中我們實作了以下功能:獲取交易對最高價最低價,買盤賣盤最優價以及最新價格,在提取賬戶資訊中,我們獲取賬戶余額,凍結余額,指定幣對的余額以及凍結余額,通過try的簡單函式可以告訴我們是否成功賦值,如果因為資料缺失或者沒有連上網都會進行報錯提示監控人員,
3.資料更新
我們寫好了如果調取資料的方法,那么我們就可以再寫一個方法進行統一的重繪交易幣對行情資料和賬戶資料,通過判斷陳述句可以方便的統一告訴我們是否成功重繪,
#確認是否獲取到賬戶和交易對資訊
def RefreshData(self):
if not self.GetAccount():
return 'false get account'
if not self.GetTicker():
return 'false get ticker'
return'refresh data finish!'
4.創建訂單
由于taker和maker手續費不同,我們選擇了低手續費的掛單方法,在定義創建訂單函式時,我們要指定交易型別以及價格數量,在開單結束后我們還需要重新重繪Account資訊,
#創建訂單
def CreateOrder(self,OrderType,Price,Amount):
if OrderType=='buy':
#執行買單
OrderId=self.Exchange.createLimitBuyOrder(self.Symbol,round(Amount,self.AmountPrecision),round(Price,self.PricePrecision))['id']
elif OrderType=='sell':
#執行賣單
OrderId=self.Exchange.createLimitSellOrder(self.Symbol,round(Amount,self.AmountPrecision),round(Price,self.PricePrecision))['id']
else:
pass
#訂單每次執行結束后,等待一點時間,讓訂單執行完,再重繪資料,再回傳訂單
time.sleep(1)
self.GetAccount()
return OrderId
5.獲取訂單狀態
我們創建了訂單后,需要對訂單進行實時的檢查,因為掛單不一定在短時間會成交,通過定義查詢訂單狀態的方法,可以方便我們可以在后續的模塊中進行一些未成交訂單的處理,避免風險以及資金的占用浪費,
#獲取訂單狀態
def GetOrder(self,Idd):
self.OrderId='___'
self.OrderPrice='___'
self.OrderNum='___'
self.OrderDealNum='___'
self.OrderAvgPrice='___'
self.OrderStatus='___'
try:
self.Order=self.Exchange.fetchOrder(Idd,self.Symbol)
self.OrderId=self.Order['id']
self.OrderPrice=self.Order['price']
self.OrderNum=self.Order['amount']
self.OrderDealNum=self.Order['filled']
self.OrderAvgPrice=self.Order['average']
self.OrderStatus=self.Order['status']
return True
except:
return False
通過上述方法,我們可以提取出以下資訊:訂單id、下單價格、下單數量、成交均價,訂單狀態等等,
6.撤銷訂單
往往由于各種原因,我們的訂單沒有成交,其中一種方式就是撤單后改變我們的訂單價格再重新下單,這樣能夠更快的成交不錯過行情,
#取消訂單
def CancelOrder(self,Idd):
self.CancelResult= '___'
try:
self.CancelResult = self.Exchange.cancelOrder(Idd,self.Symbol)
return True
except:
return False
7.獲取k線資訊
在之前我們只是獲取了交易對的基本資訊,對于資訊處理遠遠不夠,利用調取k線的方法獲取一定的時間序列可以方便的直接對接talib包進行策略分析,在這里我選擇獲取1分鐘的k線,大家可以根據自己策略的需求呼叫5m、30m、甚至1d都可以,具體有多少選擇可以查看交易所官網的k線圖上的選項,
#獲取k線資料
def GetRecords(self,Timeframe='1m'):
self.Records='___'
try:
self.Records=self.Exchange.fetchOHLCV(self.Symbol,Timeframe)
return True
except:
return False
2.定義風控模塊(類)
風控模塊最基礎的就是保證我們余額足夠交易,在此就做一個監測余額的方法作為演示,同學們可以自己寫一些其他的方法放在這個模塊里,
class RiskClass():
#風控模塊初始化,傳入實體化后的中間類
def __init__(self,ThisMyMid):
self.MyMid=ThisMyMid
def CheckRisk(self,Price,Amount):
self.MyMid.RefreshData()
if self.MyMid.Balance>=Price*Amount:
return True
else:
print('余額不足,買單未執行')
return False
3.定義策略模塊(類)
通過這個模塊,我們可以將自己選擇的策略模型添加到其中,我并不建議大家自己去寫一些策略方法,通過talib我們可以很輕松的得到我們所需要的策略信號,talib集成了市面上幾乎所有你能看到的時間序列分析的方法,由于長時間的沉淀,talib幾乎已經完美,在這個類,我用雙均線模型,即快慢線交易策略來做演示,我還將反饋模型集合在了策略類中,用來監控通過這個策略我們所開的所有的單,并且進行相應的風控處理,
1.策略模塊初始化
因為我們選用的是雙均線策略,即MA均線,我們需要傳入幾個引數:實體化的中間類、實體化的風控類、每次下單的數量、talib所需要的兩個時間視窗
class DoubleMa():
#雙均線策略初始化,傳入傳入實體化后的中間類以及雙均線需要的視窗引數
def __init__(self,ThisMyMid,ThisMyRisk,BuySellAmount,MyFastWindow,MySlowWindow):
self.MyMid=ThisMyMid
self.MyRisk=ThisMyRisk
self.RemainStocks=self.MyMid.Stocks
self.BuySellAmount=BuySellAmount
self.FastWindow=MyFastWindow
self.SlowWindow=MySlowWindow
self.SentOrders=[]#創建一個訂單串列,可以記錄目前的委托訂單狀態
通過簡單的賦值,我們可以得到了接下來所有策略所需要的引數資訊,那么接下來就到了最重要的環節了,值得一提的是SentOrders這個變數,它將會記載每次策略執行的訂單情況,作為了反饋模塊的核心,在觸發下單后,交易數量會通過初始化得到的BuySellAmount確定,而交易價格可以根據自己的情況進行調整,通常而言,限價下單時,下買單通常低于市場價格,賣單通常高于市場價格,大家可以自行替換,我在策略中設定的買賣價比例分別是0.99和1.01,
2.技術分析及交易下單
#資料清洗并作出分析
def BeginTrade(self):
self.MyMid.GetRecords()
self.CloseArrar=np.zeros(1000)#初始化收盤價陣列,一共1000根k線有1000個資料
t=0
for i in self.MyMid.Records:
self.CloseArrar[t]=i[4]
t+=1
# print(self.CloseArrar)
self.FastMaArrar=talib.SMA(self.CloseArrar,self.FastWindow)#快速均線陣列
self.SlowMaArrar=talib.SMA(self.CloseArrar,self.SlowWindow)#慢速均線陣列
#得到最新的Ma值,包括最近一個和上一個
self.fast_ma0 = self.FastMaArrar[-1]
self.fast_ma1 = self.FastMaArrar[-2]
self.slow_ma0 = self.SlowMaArrar[-1]
self.slow_ma1 = self.SlowMaArrar[-2]
#金叉和死叉的判斷
CrossOver = self.fast_ma0 > self.slow_ma0 and self.fast_ma1 < self.slow_ma1#金叉
CrossBelow = self.fast_ma0 < self.slow_ma0 and self.fast_ma1 > self.slow_ma1#死叉
#通過判斷進行交易
if CrossOver: #如果金叉買入
if MyRisk.CheckRisk(0.23,self.BuySellAmount):#風控
self.OrderId=self.MyMid.CreateOrder("buy",self.CloseArrar[-1],self.BuySellAmount)#創建買單
self.SentOrders.append(self.OrderId)#添加這一個訂單id到訂單id串列
print('買入價',self.CloseArrar[-1]*0.99)
print('產生一個限價買單!!!!!!!!!!!!!!!!!')
if CrossBelow:#如果死叉賣出
if self.RemainStocks>self.BuySellAmount:
self.OrderId=self.MyMid.CreateOrder("sell",self.CloseArrar[-1],self.BuySellAmount)#創建賣單
self.SentOrders.append(self.OrderId)#添加這一個訂單id到訂單id串列
print('賣出價',self.CloseArrar[-1]*1.01)
print('產生一個限價賣單!!!!!!!!!!!!!!!!!')
else:
print('該幣種數量不足,賣單未執行')
對于每一步我都已經做出了解釋,還是在重復一遍,這個策略可以更換成其他策略,策略會因交易對,交易時間的不同而顯現不同的決策效果,需要交易者自己進行回測分析,對于回測方法我也會在后面跟大家分享,
3.反饋模塊
SentOrders的最大作用是方便我們對未成交的訂單做出一定的處理,通過以下的方法,我們可以針對訂單的不同狀態,即掛單中、已成交、已撤單進行相應操作,對SentOrders串列的如下操作將會大大減少我們后期查詢訂單狀態的麻煩,并且可以快速對于長期未成功交易的訂單進行相應處理,
#檢查策略完成后的資訊
def CheckAndReTrade(self):
for i in self.SentOrders:#在訂單id串列中遍歷
self.MyMid.GetOrder(i)#獲得目前的訂單id的訂單狀態
if self.MyMid.OrderStatus=='closed':#如果訂單完成
self.SentOrders.remove(i)#移除在訂單id串列的資訊
if self.MyMid.OrderStatus=='open':
pass#不進行操作,可以修改價格再放上去,因為長期放著會占用保證金
if self.MyMid.OrderStatus=='canceled':#如果撤單
self.SentOrders.remove(i)#移除在訂單id串列的資訊,或者可以修改價格再放上去
4.相關類實體化及程式運行
在本次的介紹中,用的交易對是剛上新的OXT/USDT,我使用的是火幣交易所,火幣交易所的api介面設定需要用電腦登陸網頁,一般默認給的權限是讀取和交易,不建議設定提幣,由于服務器在國外,大陸的用戶需要自己爬梯子或者添加代理,可以到網上查詢相關教程,這里就不展開了,如果不這么做程式基本上是一直報錯的,
1.ccxt實體化
通過這一步,我們進行ccxt的實體化,指定目標賬戶,交易幣對,交易所交易精度,一定要注意交易價格和數量的精度在不同平臺或不同幣對都不一樣,在選擇交易對的同時一定要查看精度,另一個需要注意的是平臺最低的單筆訂單交易額,不然容易造成交易損失或報錯,
apiKey='填上你自己的'
secret='填上你自己的'
exchange=ccxt.huobipro({
# #代理部分
# 'proxies':{
# 'http':'socks5h://127.0.0.1:7891',
# 'https':'socks5h://127.0.0.1:7891'
# },
#api登陸
'apiKey':apiKey,
'secret':secret
})
#幣種
exchange.symbol='OXT/USDT'
#交易所該幣種交易最小數量精度
exchange.AmountPrecision=4
#交易所該幣種價格最小精度
exchange.PricePrecision=4
2.中間類、風控類、策略類實體化
接下來就是要對接我們所有定義的類了,我們需要做的就是對于各個類進行實體化并且進行測驗,在這里我選擇了快慢線視窗分別為3和10,每次下單數量為30的策略,用來回測我在之前教程中選擇的策略引數,
#中間模塊實體化
MyMid=MidClass(exchange)
#資料更新
print(MyMid.RefreshData())
#風險模塊實體化
MyRisk=RiskClass(MyMid)
#策略模塊實體化
MyDoubleMa=DoubleMa(MyMid,MyRisk,BuySellAmount=30,MyFastWindow=3,MySlowWindow=10)#設定交易引數
#顯示相關資料
print(MyMid.Symbol,'最新價:',MyMid.Last)
print('該幣種可用額度為:',round(MyMid.Stocks,2),MyMid.SymbolStocksName)
print('該幣種凍結額度為:',round(MyMid.FrozenStocks,2),MyMid.SymbolStocksName)
print('賬戶可用額度為:',round(MyMid.Balance,2),'USD')
print('賬戶凍結額度為:',round(MyMid.FrozenBalance,2),'USD')
如果前面都沒有問題的話,大家就可以看到下面的效果:

如果沒有顯示以上資料或者報錯,說明哪里可能出了問題,如果正常顯示的也不一定代表別的沒問題,主要要看最后能不能正常回圈開單,
3.調控程式
在定義了所有我們需要的方法后,我們一切就緒,可以開始進行量化交易的編碼,在這里,由于我呼叫的是1m的k線資訊,因此我這里進行每60秒操作一次,每兩分鐘對未成交的掛單進行撤單處理,
主控程式的整體思想是實作以下步驟:
- 等待60秒
- 策略處理(開單或沒有任何操作)
- 反饋
- 列印目前訂單狀態
- 列印賬戶資訊和交易對最新資料
- 如果經過兩輪的掛單依然沒有成交,進行撤單處理
- 回到第一步
step=1
while True:
time.sleep(60)
MyDoubleMa.BeginTrade()
MyDoubleMa.CheckAndReTrade()
print('目前掛單情況',MyDoubleMa.SentOrders)
#資料更新
print(MyMid.RefreshData())
print(MyMid.Symbol,'最新價:',MyMid.Last)
print('該幣種可用額度為:',round(MyMid.Stocks,2),MyMid.SymbolStocksName)
print('該幣種凍結額度為:',round(MyMid.FrozenStocks,2),MyMid.SymbolStocksName)
print('賬戶可用額度為:',round(MyMid.Balance,2),'USD')
print('賬戶凍結額度為:',round(MyMid.FrozenBalance,2),'USD')
print('------------------------第',step,'輪嘗試,等待60秒----------------------------')
if step%2==0:
# print('排除掛單')
for i in MyDoubleMa.SentOrders:#在訂單id串列中遍歷
MyDoubleMa.MyMid.GetOrder(i)#獲得目前的訂單id的訂單狀態
if MyDoubleMa.MyMid.OrderStatus=='closed':#如果訂單完成
MyDoubleMa.SentOrders.remove(i)#移除在訂單id串列的資訊
if MyDoubleMa.MyMid.OrderStatus=='open':#如果訂單還沒交易
MyDoubleMa.MyMid.CancelOrder(i)#取消訂單
print('排除了一個未成交的掛單')
if MyDoubleMa.MyMid.OrderStatus=='canceled':#如果撤單
MyDoubleMa.SentOrders.remove(i)#移除在訂單id串列的資訊,或者可以修改價格再放上去
step=step+1
正常的迭代下去的效果應該是以下這樣,由于在雙均線策略下沒有出現買點或者賣點,所以一直沒有進行交易,

如果雙均線策略下出現了交易那么賬戶上的資訊也將會發生改變,以下截圖是在手機端看到的下單效果,

由于每一次的賣單都沒有成功觸發,都被撤掉了,可以看出整個程式是正常運行的,
四、回測的代碼實作
通常而言,想要檢測一個策略的可行性,如果直接用之前的MMQT實盤測驗,經濟成本和時間成本都太高,如何一次性設定一個可靠的策略引數成為了最大的問題,其中包括了策略模塊的選擇(talib的雙均線,海龜,MACD等等),策略模塊的引數(每次下單的數量,每次下單的價格,時間視窗的選擇等等),要知道這些引數的最佳狀態都因不同的交易對,不同的交易時間而異,
我們想要用最小的成本確定最佳的引數,就可以利用過去的資料進行回測,以下就簡單對雙均線策略進行回測,選擇的時間周期是1m,幣種依然是OXT,時間視窗分別為3和10,正好對于前面教程里的引數確定的交易策略進行回測,
1.獲取資料
我們獲取資料的方式與MMQT的方法一樣,通過ccxt實體化獲取,
apiKey='填上你自己的'
secret='填上你自己的'
exchange=ccxt.huobipro({
# #代理部分
# 'proxies':{
# 'http':'socks5h://127.0.0.1:7891',
# 'https':'socks5h://127.0.0.1:7891'
# },
#api登陸
'apiKey':apiKey,
'secret':secret
})
#幣種
exchange.symbol='OXT/USDT'
Records=exchange.fetchOHLCV('OXT/USDT','1m')#獲得1分鐘k線資料
2.資料清洗
我們需要得到最重要的收盤價,我們可以通過簡單的索引得到這些資料,
CloseArrar=np.zeros(len(Records))#初始化收盤價陣列,一共1000根k線有1000個資料
t=0
for i in Records:
CloseArrar[t]=i[4]
t+=1
3.模擬賬戶初始化
既然是模擬賬戶,我們就要自己確定一些引數,其中包括了資產,初始代幣數量,交易數量,交易手續費,這里的交易數量可以根據簡單的操作通過倉位來確定,
#賬戶初始化
StartBalance=226#初始資金
Balance=StartBalance#目前資產
RemainStocks=0#賬戶持倉
BuySellAmount=round(0.2*Balance/Records[-1][4],0)#每次交易的數量,倉位兩成
fee=0.01*0.2#交易所買賣的手續費
4.回測程式
接下來就是確定我們的策略方式了,大家可以根據自己的選擇調整相關引數以及策略模型來進行回測,以達到收益最大化,我選擇的引數還是和實盤里的一樣,用來檢驗在過去1000分鐘我利用這個方法可以最侄訓得的收益,回測的方法和實盤是類似的,唯一不同的是通過對原始的賬戶余額不斷的賦值來模擬真實賬戶資金的變化,由于是模擬盤,因此把限價掛單模擬成了市價下單,并且忽略了滑點或無人交易的情況,最終結果肯定會有一點微小的誤差,
#雙均線策略回測
FastMaArrar=talib.SMA(CloseArrar,3)#快速均線陣列
SlowMaArrar=talib.SMA(CloseArrar,10)#慢速均線陣列
m=10
sell=buy=0
for m in range(len(Records)):
LastPrice=Records[m][4]
CrossOver = FastMaArrar[m]> SlowMaArrar[m] and FastMaArrar[m-1] < SlowMaArrar[m-1]#金叉
CrossBelow = FastMaArrar[m] < SlowMaArrar[m]and FastMaArrar[m-1] > SlowMaArrar[m-1]#死叉
if CrossOver: #如果金叉買入
if Balance>=LastPrice*BuySellAmount:
Balance=Balance-LastPrice*BuySellAmount-LastPrice*BuySellAmount*fee
RemainStocks=RemainStocks+BuySellAmount
print('產生一個限價買單','資產當前資產',round(Balance+RemainStocks*Records[m][4],2),
'現金',round(Balance,2),'幣',round(RemainStocks,2))
buy+=1
else:
print('余額不足!!!!!!!!!!')
if CrossBelow:#如果死叉賣出
if RemainStocks>=BuySellAmount:
Balance=Balance+LastPrice*BuySellAmount-LastPrice*BuySellAmount*fee
RemainStocks=RemainStocks-BuySellAmount
print('產生一個限價賣單','當前資產',round(Balance+RemainStocks*Records[m][4],2),
'現金',round(Balance,2),'幣',round(RemainStocks,2))
sell+=1
else:
print('該幣余額不足!!!!!!!!!!!!')
print('XXXXXXXXXXXXXXXX交易結束','當前資產',Balance+RemainStocks*Records[-1][4],'收益率為',
round(((Balance+RemainStocks*Records[-1][4])/StartBalance-1)*100,2),'%','該幣漲跌幅為',
round(((Records[-1][4]/Records[0][4])-1)*100,2),'%'
)
print('買單次數',buy,'賣單次數',sell)
在每一次的交易后會立即更新當前賬戶的資產,還有一點不同的是,在回測結束后會,我增加了總的收益率和交易對的漲跌情況,

可以看到在1000分鐘后該交易對跌了近9個點,而通過雙均線策略在考慮一百次交易手續費的情況下,總體僅虧了5.8個點,如果不考慮手續費,應該收益在正的5個點左右,可以看出,該方法還是有一定收益和抗風險的,但最后還是交易所賺了╮(╯▽╰)╭
五、總結與展望
1.MMQT的不足之處
照例先進行自我批評,縱觀近十年,量化交易早已在金融行業的各個領域生根發芽,我們能接觸到的,打包好的模型肯定是最經典的,同時也意味著最原始和簡單的模型,例如本文利用的雙均線模型,也就所謂股民掛在嘴邊的均線,早已在20世紀中期被美國投資大佬提出,甚至連現在很多金融投行用的CVaR指標也是上世紀提出來的,總而言之,MMQT的核心就在于決策的選擇,而這個決策也是最需要與時俱進的,真正最新的,最好的策略幾乎都是不會在互聯網上找到,更不可能在哪里報個班,你學會了就能賺錢的,
量化交易的深度學習并不是靠敲敲代碼能實作的,量化的框架并不是很難搭建,最難的核心在于交易者對于市場規律的把控,而這種從不規律中獲取規律的直覺感也正是機器所不能代替的,因此從另一個角度來看,如果交易者能夠嚴格執行自己的策略,在瞬息萬變的市場下,機器量化交易遠遠比不上人的主觀操作,
MMQT模型還較為基礎,四大模塊還有帶擴充,市場上波動無常,行情規律也飄忽不定,讀者可以根據自己的交易喜好補充自己的風控模塊,本文闡述的只是個人對于量化交易板塊的理解,我定義了一個框架,里面的靈魂需要靠大家自己去發揮設計,
2.致謝與參考資料
- ccxt中文開發手冊
- 火幣網
六、附錄:完整代碼
1.MMQT
import requests
import json
import ccxt
import time
import numpy as np
import talib
apiKey='填上你自己的'
secret='填上你自己的'
exchange=ccxt.huobipro({
# #代理部分
# 'proxies':{
# 'http':'socks5h://127.0.0.1:7891',
# 'https':'socks5h://127.0.0.1:7891'
# },
#api登陸
'apiKey':apiKey,
'secret':secret
})
#幣種
exchange.symbol='OXT/USDT'
#交易所該幣種交易最小數量精度
exchange.AmountPrecision=4
#交易所該幣種價格最小精度
exchange.PricePrecision=4
class MidClass():
#初始化
def __init__(self,ThisExchange):
self.Exchange=ThisExchange
self.Symbol=ThisExchange.symbol
self.AmountPrecision=ThisExchange.AmountPrecision
self.PricePrecision=ThisExchange.PricePrecision
#獲得交易對行情資訊
def GetTicker(self):
self.High='___'
self.Low='___'
self.Buy='___'
self.Sell='___'
self.Last='___'
try:
self.Ticker=self.Exchange.fetchTicker(self.Symbol)
self.High=self.Ticker['high']
self.Low=self.Ticker['low']
self.Buy=self.Ticker['bid']
self.Sell=self.Ticker['ask']
self.Last=self.Ticker['last']
return True#只要有一個成功就回傳True
except:
return False#如果全都獲取不了回傳False
#獲得賬戶對于該交易對資訊
def GetAccount(self):
self.Account='___'
self.Balance='___'
self.FrozenBalance='___'
self.Stocks='___'
self.FrozenStocks='___'
self.SymbolStocksName=self.Symbol.split('/')[0]
self.SymbolBalanceName=self.Symbol.split('/')[1]
try:
self.Account=self.Exchange.fetchBalance()
self.Balance=self.Account[self.SymbolBalanceName]['free']
self.FrozenBalance=self.Account[self.SymbolBalanceName]['used']
self.Stocks=self.Account[self.SymbolStocksName]['free']
self.FrozenStocks=self.Account[self.SymbolStocksName]['used']
return True
except:
return False
#確認是否獲取到賬戶和交易對資訊
def RefreshData(self):
if not self.GetAccount():
return 'false get account'
if not self.GetTicker():
return 'false get ticker'
return'refresh data finish!'
#創建訂單
def CreateOrder(self,OrderType,Price,Amount):
if OrderType=='buy':
#執行買單
OrderId=self.Exchange.createLimitBuyOrder(self.Symbol,round(Amount,self.AmountPrecision),round(Price,self.PricePrecision))['id']
elif OrderType=='sell':
#執行賣單
OrderId=self.Exchange.createLimitSellOrder(self.Symbol,round(Amount,self.AmountPrecision),round(Price,self.PricePrecision))['id']
else:
pass
#訂單每次執行結束后,等待一點時間,讓訂單執行完,再重繪資料,再回傳訂單
time.sleep(1)
self.GetAccount()
return OrderId
#獲取訂單狀態
def GetOrder(self,Idd):
self.OrderId='___'
self.OrderPrice='___'
self.OrderNum='___'
self.OrderDealNum='___'
self.OrderAvgPrice='___'
self.OrderStatus='___'
try:
self.Order=self.Exchange.fetchOrder(Idd,self.Symbol)
self.OrderId=self.Order['id']
self.OrderPrice=self.Order['price']
self.OrderNum=self.Order['amount']
self.OrderDealNum=self.Order['filled']
self.OrderAvgPrice=self.Order['average']
self.OrderStatus=self.Order['status']
return True
except:
return False
#取消訂單
def CancelOrder(self,Idd):
self.CancelResult= '___'
try:
self.CancelResult = self.Exchange.cancelOrder(Idd,self.Symbol)
return True
except:
return False
#獲取k線資料
def GetRecords(self,Timeframe='1m'):
self.Records='___'
try:
self.Records=self.Exchange.fetchOHLCV(self.Symbol,Timeframe)
return True
except:
return False
class RiskClass():
#風控模塊初始化,傳入實體化后的中間類
def __init__(self,ThisMyMid):
self.MyMid=ThisMyMid
def CheckRisk(self,Price,Amount):
self.MyMid.RefreshData()
if self.MyMid.Balance>=Price*Amount:
return True
else:
print('余額不足,買單未執行')
return False
#策略類
class DoubleMa():
#雙均線策略初始化,傳入傳入實體化后的中間類以及雙均線需要的視窗引數
def __init__(self,ThisMyMid,ThisMyRisk,BuySellAmount,MyFastWindow,MySlowWindow):
self.MyMid=ThisMyMid
self.MyRisk=ThisMyRisk
self.RemainStocks=self.MyMid.Stocks
self.BuySellAmount=BuySellAmount
self.FastWindow=MyFastWindow
self.SlowWindow=MySlowWindow
self.SentOrders=[]#創建一個訂單串列,可以記錄目前的委托訂單狀態
#資料清洗并作出分析
def BeginTrade(self):
self.MyMid.GetRecords()
self.CloseArrar=np.zeros(1000)#初始化收盤價陣列,一共1000根k線有1000個資料
t=0
for i in self.MyMid.Records:
self.CloseArrar[t]=i[4]
t+=1
self.FastMaArrar=talib.SMA(self.CloseArrar,self.FastWindow)#快速均線陣列
self.SlowMaArrar=talib.SMA(self.CloseArrar,self.SlowWindow)#慢速均線陣列
#得到最新的Ma值,包括最近一個和上一個
self.fast_ma0 = self.FastMaArrar[-1]
self.fast_ma1 = self.FastMaArrar[-2]
self.slow_ma0 = self.SlowMaArrar[-1]
self.slow_ma1 = self.SlowMaArrar[-2]
#金叉和死叉的判斷
CrossOver = self.fast_ma0 > self.slow_ma0 and self.fast_ma1 < self.slow_ma1#金叉
CrossBelow = self.fast_ma0 < self.slow_ma0 and self.fast_ma1 > self.slow_ma1#死叉
#通過判斷進行交易
if CrossOver: #如果金叉買入
if MyRisk.CheckRisk(0.23,self.BuySellAmount):#風控
self.OrderId=self.MyMid.CreateOrder("buy",self.CloseArrar[-1],self.BuySellAmount)#創建買單
self.SentOrders.append(self.OrderId)#添加這一個訂單id到訂單id串列
print('買入價',self.CloseArrar[-1]*0.99)
print('產生一個限價買單!!!!!!!!!!!!!!!!!')
if CrossBelow:#如果死叉賣出
if self.RemainStocks>self.BuySellAmount:
self.OrderId=self.MyMid.CreateOrder("sell",self.CloseArrar[-1],self.BuySellAmount)#創建賣單
self.SentOrders.append(self.OrderId)#添加這一個訂單id到訂單id串列
print('賣出價',self.CloseArrar[-1]*1.01)
print('產生一個限價賣單!!!!!!!!!!!!!!!!!')
else:
print('該幣種數量不足,賣單未執行')
#檢查策略完成后的資訊
def CheckAndReTrade(self):
for i in self.SentOrders:#在訂單id串列中遍歷
self.MyMid.GetOrder(i)#獲得目前的訂單id的訂單狀態
if self.MyMid.OrderStatus=='closed':#如果訂單完成
self.SentOrders.remove(i)#移除在訂單id串列的資訊
if self.MyMid.OrderStatus=='open':
pass#不進行操作,可以修改價格再放上去,因為長期放著會占用保證金
if self.MyMid.OrderStatus=='canceled':#如果撤單
self.SentOrders.remove(i)#移除在訂單id串列的資訊,或者可以修改價格再放上去
#中間模塊實體化
MyMid=MidClass(exchange)
#資料更新
print(MyMid.RefreshData())
#風險模塊實體化
MyRisk=RiskClass(MyMid)
#策略模塊實體化
MyDoubleMa=DoubleMa(MyMid,MyRisk,BuySellAmount=30,MyFastWindow=3,MySlowWindow=10)#設定交易引數
#顯示相關資料
print(MyMid.Symbol,'最新價:',MyMid.Last)
print('該幣種可用額度為:',round(MyMid.Stocks,2),MyMid.SymbolStocksName)
print('該幣種凍結額度為:',round(MyMid.FrozenStocks,2),MyMid.SymbolStocksName)
print('賬戶可用額度為:',round(MyMid.Balance,2),'USD')
print('賬戶凍結額度為:',round(MyMid.FrozenBalance,2),'USD')
step=1
while True:
time.sleep(60)
MyDoubleMa.BeginTrade()
MyDoubleMa.CheckAndReTrade()
print('目前掛單情況',MyDoubleMa.SentOrders)
#資料更新
print(MyMid.RefreshData())
print(MyMid.Symbol,'最新價:',MyMid.Last)
print('該幣種可用額度為:',round(MyMid.Stocks,2),MyMid.SymbolStocksName)
print('該幣種凍結額度為:',round(MyMid.FrozenStocks,2),MyMid.SymbolStocksName)
print('賬戶可用額度為:',round(MyMid.Balance,2),'USD')
print('賬戶凍結額度為:',round(MyMid.FrozenBalance,2),'USD')
print('------------------------第',step,'輪嘗試,等待60秒----------------------------')
if step%2==0:
for i in MyDoubleMa.SentOrders:#在訂單id串列中遍歷
MyDoubleMa.MyMid.GetOrder(i)#獲得目前的訂單id的訂單狀態
if MyDoubleMa.MyMid.OrderStatus=='closed':#如果訂單完成
MyDoubleMa.SentOrders.remove(i)#移除在訂單id串列的資訊
if MyDoubleMa.MyMid.OrderStatus=='open':#如果訂單還沒交易
MyDoubleMa.MyMid.CancelOrder(i)#取消訂單
print('排除了一個未成交的掛單')
if MyDoubleMa.MyMid.OrderStatus=='canceled':#如果撤單
MyDoubleMa.SentOrders.remove(i)#移除在訂單id串列的資訊,或者可以修改價格再放上去
step=step+1
2.回測
import requests
import json
import ccxt
import time
import numpy as np
import talib
apiKey='填上你自己的'
secret='填上你自己的'
exchange=ccxt.huobipro({
# #代理部分
# 'proxies':{
# 'http':'socks5h://127.0.0.1:7891',
# 'https':'socks5h://127.0.0.1:7891'
# },
#api登陸
'apiKey':apiKey,
'secret':secret
})
#幣種
exchange.symbol='OXT/USDT'
Records=exchange.fetchOHLCV('OXT/USDT','1m')#獲得1分鐘k線資料
#資料清洗,獲得收盤價
CloseArrar=np.zeros(len(Records))#初始化收盤價陣列,一共1000根k線有1000個資料
t=0
for i in Records:
CloseArrar[t]=i[4]
t+=1
#賬戶初始化
StartBalance=226#初始資金
Balance=StartBalance#目前資產
RemainStocks=0#賬戶持倉
BuySellAmount=round(0.2*Balance/Records[-1][4],0)#每次交易的數量,倉位兩成
fee=0.01*0.2#交易所買賣的手續費
#雙均線策略回測
FastMaArrar=talib.SMA(CloseArrar,3)#快速均線陣列
SlowMaArrar=talib.SMA(CloseArrar,10)#慢速均線陣列
m=10
sell=buy=0
for m in range(len(Records)):
LastPrice=Records[m][4]
CrossOver = FastMaArrar[m]> SlowMaArrar[m] and FastMaArrar[m-1] < SlowMaArrar[m-1]#金叉
CrossBelow = FastMaArrar[m] < SlowMaArrar[m]and FastMaArrar[m-1] > SlowMaArrar[m-1]#死叉
if CrossOver: #如果金叉買入
if Balance>=LastPrice*BuySellAmount:
Balance=Balance-LastPrice*BuySellAmount-LastPrice*BuySellAmount*fee
RemainStocks=RemainStocks+BuySellAmount
print('產生一個限價買單','資產當前資產',round(Balance+RemainStocks*Records[m][4],2),
'現金',round(Balance,2),'幣',round(RemainStocks,2))
buy+=1
else:
print('余額不足!!!!!!!!!!')
if CrossBelow:#如果死叉賣出
if RemainStocks>=BuySellAmount:
Balance=Balance+LastPrice*BuySellAmount-LastPrice*BuySellAmount*fee
RemainStocks=RemainStocks-BuySellAmount
print('產生一個限價賣單','當前資產',round(Balance+RemainStocks*Records[m][4],2),
'現金',round(Balance,2),'幣',round(RemainStocks,2))
sell+=1
else:
print('該幣余額不足!!!!!!!!!!!!')
print('XXXXXXXXXXXXXXXX交易結束','當前資產',Balance+RemainStocks*Records[-1][4],'收益率為',
round(((Balance+RemainStocks*Records[-1][4])/StartBalance-1)*100,2),'%','該幣漲跌幅為',
round(((Records[-1][4]/Records[0][4])-1)*100,2),'%'
)
print('買單次數',buy,'賣單次數',sell)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/239568.html
標籤:區塊鏈
上一篇:公鏈分析報告 - Fabric
下一篇:玩位元幣合約有什么秘訣?
