量化交易策略系列(一):Market Making
Market Making策略
Market Making (MM) 策略的核心思路:不持倉的情況下保持每筆交易盈利。特點是,在交易盤口上爭奪成交機會和利潤,高頻交易,薄利多銷。美股著名的最大做市商(曾經)如:Knight Capital Group 2009年在Equity的交易達到9.8億筆,平均每小時60萬筆交易,不過平均每筆交易收益.01% – .10%,即盤口價差的量級(注:盤口價差,即Spread,爲(賣一價 - 買一價)/公允價格)。
數字貨幣領域的量化交易,缺乏行業標準,沒有嚴格的Trader/Broker/Clearing House/Exchange 的區分,各個角色目前都是由各個交易所全權代表。好處是,交易員無需像傳統金融領域那樣受到各個環節的監管和剝削,可以直接面對市場;糟糕的是,正因爲是直接面對市場,風險常常是不可控的。尤其是面對一個被有限的幾個“鯨魚”賬戶掌控的數字資產交易領域。
然而,傳統金融領域的交易策略,大部分都可以直接遷移到數字資產交易領域。其中包括Market Making策略。
成交中的對手方
一筆成交中,必定有一個Maker,一個Taker。Maker是詢價方,即提供一定數量的資產在某一價格上出售(或購買)。Taker則是購買者(或出售者)。之所以有Maker/Taker之區分,是因爲Maker作爲詢價方,爲這筆交易提供了“流動性”,使得Taker能夠如願以償。
各個交易所對於一筆交易中的Maker/Taker通常採取不同的手續費規則。規則的不同來自於對該交易所“流動性”的調控。在流動性不足的交易所或市場,通常會採用降低或者返還手續費的方式來激勵Maker單,即詢價訂單,使得“流動性”在數量上得到保障。
目前採用Maker手續費返還策略的數字資產交易所有:
BitMEX 合約(返萬2.5)
WBFex 現貨(返萬2)
MM策略介紹
BitMEX Trading API
設想一下,如果能夠預測下一個Taker賣單的到來時刻,並且能預測該訂單的數量,那麼Maker可以從容的在現有的買一之上幾個Tick的價差上開出自己的新的詢價單,即新的買一價,搶在舊的買一之前與這個Taker訂單成交。在有返傭的交易所,這一筆交易會帶來相對於成交量萬2左右的收益。
成交後,MM策略會立刻預測下一個Taker買單大到來,並且在剛剛的成交價之上幾個Tick處(同時比現有的賣一單低幾個Tick)掛出賣單,靜候Taker來成交。如果成交,會帶來另外一個萬2的收益,而手中持有倉位爲0。
這是個理想場景,即MM策略能夠 從容 的低買高賣,賺取價差和手續費返還。各式各樣的MM策略所做的都是不斷的趨近與這個理想場景,在軟件上使用人工智能算法,或隨機動態規劃算法;在硬件上使用更快的計算集羣以及和交易所共謀一個更快速的網絡連接渠道 (co-location)。
賺取手續費$
在薄利多銷的高頻MM做市策略裏,手續費減免尤其重要。這通常是因爲,短期內(毫秒或微秒)低買高賣的利潤,通常是在萬一~千一的的量級,因此手續費必須低於這個量級,才能保持MM策略的持續盈利。因此,不論是傳統金融交易所,如NASDAQ,NYSE,還是數字資產交易所,如BitMEX,WBFex,都採用了Maker減免甚至負費率的措施,鼓勵Maker訂單,滿足交易所其他用戶的交易需求。
BitMEX Trading API
WBFex Trading API
MM交易代碼
def place_orders(self):
"""Create order items for use in convergence."""
buy_orders = []
sell_orders = []
# Create orders from the outside in. This is intentional - let's say the inner order gets taken;
# then we match orders from the outside in, ensuring the fewest number of orders are amended and only
# a new order is created in the inside. If we did it inside-out, all orders would be amended
# down and a new order would be created at the outside.
for i in reversed(range(1, settings.ORDER_PAIRS + 1)):
if not self.long_position_limit_exceeded():
buy_orders.append(self.prepare_order(-i))
if not self.short_position_limit_exceeded():
sell_orders.append(self.prepare_order(i))
return self.converge_orders(buy_orders, sell_orders)
def prepare_order(self, index):
"""Create an order object."""
if settings.RANDOM_ORDER_SIZE is True:
quantity = random.randint(settings.MIN_ORDER_SIZE, settings.MAX_ORDER_SIZE)
else:
quantity = settings.ORDER_START_SIZE + ((abs(index) - 1) * settings.ORDER_STEP_SIZE)
price = self.get_price_offset(index)
return {'price': price, 'orderQty': quantity, 'side': "Buy" if index < 0 else "Sell"}