淺談比特幣期貨做市策略

論壇 期權論壇 期權     
zanks   2019-11-20 08:55   312   0
一、什么是做市策略做市策略(market-maker strategy)是一種風險中立(risk-neutral)盤口價差套利策略。其基本原理是:在盤口的賣一和買一價之間,插入委買單和委賣單,如果插入的兩個單子都成交的話,做市商就吃到了買賣單之間的價差,而整個過程結束后,做市商所持有的頭寸并沒有變化。如果買賣單之間的價差扣除各種交易手續費之后還有盈余,那么該做市商就獲得了相應的盈利。
做市策略是一種增加交易所流動性的策略,一般來講,成熟的交易市場為了提升自身的流動性,會用低傭金(甚至為做市商提供流動性獎勵金)的辦法,吸引做市商來該市場做市。
二、做市策略需要注意的事項
1 做市時機的選擇。做市商本質上是整個市場的交易對手方。如果市場呈現急劇的單邊行情,做市商下達的買賣委托單會大概率出現單邊成交的情況,因此做市商手中就會積累大量的風險頭寸,這是做市商不想承擔的風險。因此,做市商在選擇是否下達做市指令之前,都會預判一下市場的趨勢明顯程度,如果市場短期內呈現非常明顯的趨勢信號,做市商就會相應地減少自己的做市單數量(甚至停止做市)
2 凈頭寸的處理。做市商手中累計的凈頭寸,可以通過很多種辦法來處理,下面列舉其中兩種:
(1)在下一次做市時,處理掉累積的凈頭寸。比如做市商目前凈頭寸有2BTC,下次做市時,他就可以下達一個賣3BTC的委賣單,一個買1BTC的委買單。這種做法好處是凈頭寸可以及時得到處理,壞處是凈頭寸處理的時機(價格)可能不是最優的。
(2)第二種方法是開立另外一個獨立的程序,對累積的凈頭寸進行成本計算,然后按照成本價*(1+一定比例的手續費+一定比例的profit margin),將該頭寸反向甩出市場,甩出方法又有兩種:a. 按限價單從價優到價劣依次甩出市場,超時不成交的部分則撤單,等待下次機會;b. 先按限價單從價優到價劣依次甩出市場,超時不成交的部分,則按市價單甩貨。第一種方法的好處是甩貨成本可控,但是甩貨周期可能會拖得比較長;第二種方法則能有效地控制甩貨周期,但是成本不可控,孰優孰劣,需要做市商根據自己的風險偏好慎重考慮。
3 期貨換合約(移倉)的處理
期貨合約都有一個到期日,在到期日結束時刻的前幾個小時,我們不建議做市商繼續做市,而是利用這幾個小時,將即將到期的期貨合約進行移倉。移倉的意思就是平掉當前期貨合約的倉位,然后再開同樣倉位大小的下周合約。當然,移倉也是需要考慮成本的。如果當前持倉是多頭,那么我們希望移倉的時間點是在下周期貨貼水最厲害的時候;反之,我們則希望移倉的時間點是在下周期貨升水最厲害的時候。等移倉做完以后,做市策略繼續恢復執行。

三、一個典型的比特幣期貨做市策略源碼分享

注意,以下的策略需要其他WeQuant基礎類庫的支持才能運行,這里僅給出策略核心源碼,是為了讓讀者對做市策略本身有一個具體的認識,而不用去太糾結底層下單、統計收益、收發郵件、進程監控等技術細節。關于代碼的詳細解釋,歡迎入QQ群討論:519538535。
期貨做市策略源碼:


  1. #!/usr/bin/env python  # -*- coding: utf-8 -*-
  2.   from signalGenerator.futureSpotArb import *
  3.   from signalGenerator.strategyConfig import changeFutureContractConfig as rollCfg
  4.   import time, threading
  5.   class FutureMarketMaker(FutureSpotArb):
  6.   def __init__(self, startRunningTime, orderRatio, timeInterval, orderWaitingTime,
  7.   coinMarketType, open_diff, close_diff, heart_beat_time, depth_data, account_info, transaction_info, maximum_qty_multiplier=None,
  8.   dailyExitTime=None):
  9.   super(FutureMarketMaker, self).__init__(startRunningTime, orderRatio, timeInterval, orderWaitingTime,
  10.   coinMarketType, open_diff, close_diff, heart_beat_time, depth_data, account_info, transaction_info, maximum_qty_multiplier=maximum_qty_multiplier,
  11.   dailyExitTime=dailyExitTime)
  12.   # 顯示在郵件中的策略名字
  13.   self.strat_name = "期貨做市-%s" % startRunningTime.strftime("%Y%m%d_%H%M%S")
  14.   self.trade_threshold = 0.0003 * 1.01
  15.   self.sell_cut = 0.6
  16.   self.buy_cut = 0.6
  17.   self.leverage = 5
  18.   self.remaining_delta_cash = 0
  19.   # 策略下單參數
  20.   self.coin_type = helper.HUOBI_COIN_TYPE_BTC
  21.   self.contract_type = helper.CONTRACT_TYPE_WEEK
  22.   self.initial_acct_info = None
  23.   # cancel all pending orders
  24.   def cancel_pending_orders(self):
  25.   orders = self.BitVCService.order_list(self.coin_type,self.contract_type)
  26.   while orders is not None and len(componentExtract(orders, "week", [])) > 0:
  27.   orders = componentExtract(orders, "week", [])
  28.   for order in orders:
  29.   if componentExtract(order, u"id", "") != "":
  30.   order_id = order[u"id"]
  31.   self.BitVCService.order_cancel(self.coin_type,self.contract_type, order_id)
  32.   orders = self.BitVCService.order_list(self.coin_type,self.contract_type)
  33.   def go(self):
  34.   self.timeLog("日志啟動于 %s" % self.getStartRunningTime().strftime(self.TimeFormatForLog))
  35.   self.timeLog("開始cancel pending orders")
  36.   self.cancel_pending_orders()
  37.   self.timeLog("完成cancel pending orders")
  38.   while True:
  39.   # 期貨移倉期間,程序一直sleep
  40.   if self.in_time_period(datetime.datetime.now(), rollCfg.CHANGE_CONTRACT_START_WEEK_DAY_FOR_NORMAL,
  41.   rollCfg.CHANGE_CONTRACT_END_WEEK_DAY_FOR_NORMAL, rollCfg.CHANGE_CONTRACT_START_TIME_FOR_NORMAL,
  42.   rollCfg.CHANGE_CONTRACT_END_TIME_FOR_NORMAL):
  43.   self.timeLog("當前處于移倉時間,程序進入睡眠狀態……")
  44.   time.sleep(60)
  45.   continue
  46.   if self.timeInterval > 0:
  47.   self.timeLog("等待 %d 秒進入下一個循環..." % self.timeInterval)
  48.   time.sleep(self.timeInterval)
  49.   self.order_info_list = []
  50.   # 獲取賬戶持倉信息
  51.   try:
  52.   account = copy.deepcopy(self.account_info)
  53.   acct_info = account["account_info"]
  54.   account_update_time = account["time"]
  55.   except Exception:
  56.   self.timeLog("尚未取得賬戶信息")
  57.   continue
  58.   # 檢查賬戶獲取時間
  59.   if account_update_time < self.latest_trade_time:
  60.   self.timeLog("當前賬戶信息時間晚于最近交易時間,需要重新獲取")
  61.   continue
  62.   # setup initial account info
  63.   if self.initial_acct_info is None:
  64.   self.initial_acct_info = acct_info
  65.   short_pos_money_delta = acct_info["bitvc_btc_hold_money_week_short"] - self.initial_acct_info["bitvc_btc_hold_money_week_short"]
  66.   long_pos_money_delta = acct_info["bitvc_btc_hold_money_week_long"] - self.initial_acct_info["bitvc_btc_hold_money_week_long"]
  67.   self.remaining_delta_cash = long_pos_money_delta - short_pos_money_delta  # 代表著增加了多少開多的money,需要減去(sell)
  68.   if self.remaining_delta_cash != 0:
  69.   self.timeLog("剩余 %.4f 數量還沒有平" % self.remaining_delta_cash)
  70.   # 查詢bitvc深度數據
  71.   try:
  72.   bitvcDepth = copy.deepcopy(self.depth_data)["bitvc"]
  73.   except Exception:
  74.   self.timeLog("尚未取得bitvc深度數據")
  75.   continue
  76.   # 查看行情信息時間戳是否合理
  77.   timestamp_list = [bitvcDepth["time"]]
  78.   if not self.check_time(timestamp_list):
  79.   self.timeLog("獲取的行情信息時間延遲過大,被舍棄,進入下一循環")
  80.   continue
  81.   self.timeLog("記錄心跳信息...")
  82.   self.heart_beat_time.value = time.time()
  83.   asks = bitvcDepth["asks"]
  84.   bids = bitvcDepth["bids"]
  85.   bitvc_sell_1_price = float(asks[len(asks) - 1][0])
  86.   bitvc_buy_1_price = float(bids[0][0])
  87.   margin = bitvc_sell_1_price - bitvc_buy_1_price
  88.   future_order_sell_price = bitvc_sell_1_price - 0.5*margin*self.sell_cut
  89.   future_order_buy_price = bitvc_buy_1_price + 0.5*margin*self.buy_cut
  90.   future_order_sell_money = 100
  91.   future_order_buy_money = 100
  92.   if self.remaining_delta_cash > 0: #bought too much
  93.   future_order_sell_money += self.remaining_delta_cash
  94.   future_order_sell_price -= 0.2*margin*self.sell_cut
  95.   future_order_buy_price -= 0.1*margin*self.buy_cut
  96.   else:
  97.   future_order_buy_money += abs(self.remaining_delta_cash)
  98.   future_order_buy_price += 0.2*margin*self.buy_cut
  99.   future_order_sell_price += 0.1*margin*self.sell_cut
  100.   diff_percentage = (future_order_sell_price - future_order_buy_price)/future_order_sell_price
  101.   if diff_percentage < self.trade_threshold:
  102.   self.timeLog("future_order_sell_price: %.2f, future_order_buy_price: %.2f, diff percentage: %.6f%% smaller than trade threshold: %.6f%%, so ignore and continue" % ( future_order_sell_price, future_order_buy_price, diff_percentage*100, self.trade_threshold*100))
  103.   continue
  104.   bitvc_btc_hold_money_week_long = acct_info["bitvc_btc_hold_money_week_long"]
  105.   bitvc_btc_hold_money_week_short = acct_info["bitvc_btc_hold_money_week_short"]
  106.   global sold_money
  107.   sold_money = 0
  108.   global bought_money
  109.   bought_money = 0
  110.   # 策略下單參數
  111.   coin_type = self.coin_type
  112.   contract_type = self.contract_type
  113.   def loop1():
  114.   # place sell order
  115.   order_id_list_sell = []
  116.   if bitvc_btc_hold_money_week_long > future_order_sell_money:
  117.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_SELL, future_order_sell_price, future_order_sell_money, leverage=self.leverage))
  118.   else:
  119.   if bitvc_btc_hold_money_week_long > 0:
  120.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_SELL, future_order_sell_price, bitvc_btc_hold_money_week_long, leverage=self.leverage))
  121.   if future_order_sell_money-bitvc_btc_hold_money_week_long > 0:
  122.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_OPEN, helper.CONTRACT_TRADE_TYPE_SELL, future_order_sell_price, future_order_sell_money-bitvc_btc_hold_money_week_long, leverage=self.leverage))
  123.   if self.remaining_delta_cash > 0:
  124.   bitvc_order_query_retry_maximum_times = 100
  125.   bitvc_order_cancel_query_retry_maximum_times = 10
  126.   else:
  127.   bitvc_order_query_retry_maximum_times = 100
  128.   bitvc_order_cancel_query_retry_maximum_times = 10
  129.   global sold_money
  130.   for order_id in order_id_list_sell:
  131.   if order_id is not None:
  132.   tmp = self.bitvc_order_wait_and_cancel(coin_type, contract_type, order_id, returnProcessedMoney=True, bitvc_order_query_retry_maximum_times=bitvc_order_query_retry_maximum_times, bitvc_order_cancel_query_retry_maximum_times=bitvc_order_cancel_query_retry_maximum_times)
  133.   if tmp is not None:
  134.   sold_money += tmp
  135.   order_id_list_sell = []
  136.   if sold_money < future_order_sell_money and bought_money > 0: # buy side is partially filled or filled
  137.   adjusted_future_order_sell_price = future_order_buy_price * (1 + 0.0003)
  138.   adjusted_future_order_sell_money = future_order_sell_money - sold_money
  139.   if bitvc_btc_hold_money_week_long - sold_money > adjusted_future_order_sell_money:
  140.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_SELL, adjusted_future_order_sell_price, adjusted_future_order_sell_money, leverage=self.leverage))
  141.   else:
  142.   if bitvc_btc_hold_money_week_long - sold_money > 0:
  143.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_SELL, adjusted_future_order_sell_price, bitvc_btc_hold_money_week_long - sold_money, leverage=self.leverage))
  144.   if bitvc_btc_hold_money_week_long - sold_money < 0:
  145.   #already opened short
  146.   remaining_short = adjusted_future_order_sell_money
  147.   else:
  148.   remaining_short = adjusted_future_order_sell_money - (bitvc_btc_hold_money_week_long - sold_money)
  149.   if remaining_short > 0:
  150.   order_id_list_sell.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_OPEN, helper.CONTRACT_TRADE_TYPE_SELL, adjusted_future_order_sell_price, remaining_short, leverage=self.leverage))
  151.   for order_id in order_id_list_sell:
  152.   if order_id is not None:
  153.   tmp = self.bitvc_order_wait_and_cancel(coin_type, contract_type, order_id, returnProcessedMoney=True, bitvc_order_query_retry_maximum_times=bitvc_order_query_retry_maximum_times, bitvc_order_cancel_query_retry_maximum_times=bitvc_order_cancel_query_retry_maximum_times)
  154.   if tmp is not None:
  155.   sold_money += tmp
  156.   def loop2():
  157.   # place buy order
  158.   order_id_list_buy = []
  159.   if bitvc_btc_hold_money_week_short > future_order_buy_money:
  160.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_BUY, future_order_buy_price, future_order_buy_money, leverage=self.leverage))
  161.   else:
  162.   if bitvc_btc_hold_money_week_short > 0:
  163.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_BUY, future_order_buy_price, bitvc_btc_hold_money_week_short, leverage=self.leverage))
  164.   if future_order_buy_money-bitvc_btc_hold_money_week_short > 0:
  165.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_OPEN, helper.CONTRACT_TRADE_TYPE_BUY, future_order_buy_price, future_order_buy_money-bitvc_btc_hold_money_week_short, leverage=self.leverage))
  166.   if self.remaining_delta_cash < 0:
  167.   bitvc_order_query_retry_maximum_times = 100
  168.   bitvc_order_cancel_query_retry_maximum_times = 10
  169.   else:
  170.   bitvc_order_query_retry_maximum_times = 100
  171.   bitvc_order_cancel_query_retry_maximum_times = 10
  172.   global bought_money
  173.   for order_id in order_id_list_buy:
  174.   if order_id is not None:
  175.   tmp = self.bitvc_order_wait_and_cancel(coin_type, contract_type, order_id, returnProcessedMoney=True, bitvc_order_query_retry_maximum_times=bitvc_order_query_retry_maximum_times, bitvc_order_cancel_query_retry_maximum_times=bitvc_order_cancel_query_retry_maximum_times)
  176.   if tmp is not None:
  177.   bought_money += tmp
  178.   order_id_list_buy = []
  179.   if bought_money < future_order_buy_money and sold_money > 0: # sell side is partially filled or filled
  180.   adjusted_future_order_buy_price = future_order_sell_price * (1 - 0.0003)
  181.   adjusted_future_order_buy_money = future_order_buy_money - bought_money
  182.   if bitvc_btc_hold_money_week_short - bought_money > adjusted_future_order_buy_money:
  183.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_BUY, adjusted_future_order_buy_price, adjusted_future_order_buy_money, leverage=self.leverage))
  184.   else:
  185.   if bitvc_btc_hold_money_week_short - bought_money > 0:
  186.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_CLOSE, helper.CONTRACT_TRADE_TYPE_BUY, adjusted_future_order_buy_price, bitvc_btc_hold_money_week_short - bought_money, leverage=self.leverage))
  187.   if bitvc_btc_hold_money_week_short - bought_money < 0:
  188.   # already opened long
  189.   remaining_long = adjusted_future_order_buy_money
  190.   else:
  191.   remaining_long = adjusted_future_order_buy_money - (bitvc_btc_hold_money_week_short - bought_money)
  192.   if remaining_long > 0:
  193.   order_id_list_buy.append(self.bitvc_order(coin_type, contract_type, helper.CONTRACT_ORDER_TYPE_OPEN, helper.CONTRACT_TRADE_TYPE_BUY, adjusted_future_order_buy_price, remaining_long, leverage=self.leverage))
  194.   for order_id in order_id_list_buy:
  195.   if order_id is not None:
  196.   tmp = self.bitvc_order_wait_and_cancel(coin_type, contract_type, order_id, returnProcessedMoney=True, bitvc_order_query_retry_maximum_times=bitvc_order_query_retry_maximum_times, bitvc_order_cancel_query_retry_maximum_times=bitvc_order_cancel_query_retry_maximum_times)
  197.   if tmp is not None:
  198.   bought_money += tmp
  199.   t1 = threading.Thread(target=loop1, name='LoopThread1')
  200.   t2 = threading.Thread(target=loop2, name='LoopThread2')
  201.   t1.start()
  202.   t2.start()
  203.   t1.join()
  204.   t2.join()
  205.   if len(self.order_info_list) > 0:
  206.   transaction_id = helper.getUUID()
  207.   for order_info in self.order_info_list:
  208.   coinType = self.coinMarketType
  209.   marketType = order_info["marketType"]
  210.   order_id = order_info["order_id"]
  211.   self.put_order_info_in_queue(coinType, marketType, order_id, transaction_id)
  212.   self.cancel_pending_orders()
  213.   self.latest_trade_time = time.time()
復制代碼
四、策略實盤運行結果展示策略在運行了半個小時之后,成功地抓住了兩次做市機會,勝率100%!在比特幣基準收益為-0.03%的情況下,本策略30分鐘收益達到0.12%。



分享到 :
0 人收藏
高級模式
您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

積分:530
帖子:163
精華:0
期權論壇
發布
內容

下載期權論壇手機APP

深海捕鱼下载排名