class trading: # Real Trading def __init__(self,client_id = Tokens['Deribit']['Read_and_Write']['id'],client_secret = Tokens['Deribit']['Read_and_Write']['secret']): # Login read only account self.MyAccount = RestClient(client_id,client_secret) self.Fee = { 'Future':{ 'Market':0.00075,'Limit':-0.0025 }, 'Option':0.0004 } self.Max_Order_Num = 3 self.Trading_Frequency = 1 def post_order(self,Instrument,Short_or_Long,Post_Size): def get_Post_Dist(Orderbook,Bid_or_Ask,Order_Num,Post_Size): Top_Order = [Orderbook[Bid_or_Ask][i]['quantity'] for i in range(Order_Num)] Order_Weight = [Top_Order[i]*Post_Size/sum(Top_Order) for i in range(Order_Num)] # If the order weight is smaller than 0.1 then add it to 0.1 Order_Weight = [round(w,1) for w in Order_Weight] Order_Weight[0] = Order_Weight[0] + (Post_Size - sum(Order_Weight)) # Transfer it as order dictionary Post_Dist = [{'price':Orderbook[Bid_or_Ask][i]['price'],'size':Order_Weight[i]} for i in range(Order_Num)] return Post_Dist Orderbook = self.MyAccount.getorderbook(Instrument) # Post orders by weighted-size if Short_or_Long == 'Short': Bid_or_Ask = 'asks' elif Short_or_Long == 'Long': Bid_or_Ask = 'bids' Order_Num = min(int(Post_Size/0.1),min(self.Max_Order_Num,len(Orderbook[Bid_or_Ask]))) # Total number of orders will be posted Post_Dist = [] # Store price and amount of each orders will be posted if Orderbook['bids'][min(2,len(Orderbook['bids']))]['cm']>Orderbook['asks'][min(2,len(Orderbook['bids']))]['cm']: # Bid pressure is larger than Ask pressure, then post order at the top orders of asks book Post_Dist = get_Post_Dist(Orderbook,Bid_or_Ask,Order_Num,Post_Size) else: # Bid pressure is smaller than Ask pressure, then post order above and at the orders of ask book if Orderbook['asks'][0]['price'] - Orderbook['bids'][0]['price']<0.001: Post_Dist = [{'price':Orderbook['bids'][0]['price'],'size':Post_Size}] else: First_Order = max(0.1,round(Post_Size/3,1)) Post_Dist = [{'price':Orderbook['asks'][0]['price']-0.0005,'size':First_Order}] Post_Dist = Post_Dist + get_Post_Dist(Orderbook,Bid_or_Ask,Order_Num - 1,Post_Size - First_Order) Post_Dist = [{'price':Post_Dist[i]['price'],'size':round(Post_Dist[i]['size'],1)} for i in range(len(Post_Dist))] # Print the result print("There will be %d %s orders posted:"%(Order_Num,Short_or_Long.lower())) pprint(Post_Dist) Execute_or_Not = input('Post those orders? [y/n]') if Execute_or_Not == 'y': ###---Post Orders---### if Short_or_Long == 'Short': [self.MyAccount.sell(Instrument, p['size'], p['price'], False) for p in Post_Dist] for p in Post_Dist: if p['size']!=0: self.MyAccount.sell(Instrument, p['size'], p['price'], False) time.sleep(self.Trading_Frequency) elif Short_or_Long == 'Long': for p in Post_Dist: if p['size']!=0: self.MyAccount.buy(Instrument, p['size'], p['price'], False) time.sleep(self.Trading_Frequency) else: print('Error occurs') return False ###------### return True else: return False def get_current_btc_postition(self,Instrument): if len(self.MyAccount.positions())!=0: if sum([p['instrument'] == 'BTC-PERPETUAL' for p in self.MyAccount.positions()])!= 0: # list is not empty Perpetual_Position = self.MyAccount.positions()\ [\ [p['instrument'] == 'BTC-PERPETUAL' for p in self.MyAccount.positions()].index(True)\ ]['sizeBtc'] else: Perpetual_Position = 0 if sum([p['instrument'] == Instrument for p in self.MyAccount.positions()]) != 0: # list is not empty Option_Position = self.MyAccount.positions()\ [\ [p['instrument'] == Instrument for p in self.MyAccount.positions()].index(True)\ ]['size'] else: Option_Position = 0 else: Perpetual_Position = 0 Option_Position = 0 return Perpetual_Position,Option_Position def hedge(self,Instrument,Strike_Price): BTC_Price = float(self.MyAccount.index()['btc']) Perpetual_Position,Option_Position = self.get_current_btc_postition(Instrument) # Hedge current risk exposure while (BTC_Price < Strike_Price) and (Perpetual_Position> Option_Position): # Hedging sms_notification('Hedging Start.') Execute_or_Not = input('Cancel all orders?[y/n]') if Execute_or_Not == 'y': self.MyAccount.cancelall() else: return False Orderbook = self.MyAccount.getorderbook('BTC-PERPETUAL') Post_Size = abs(float(Perpetual_Position - Option_Position)) Top_Order_Size = Orderbook['bids'][0]['quantity']/Orderbook['bids'][0]['price'] # Top_Order_Size is counted as BTC print("There will be %lf perpetual shorted at %lf"%(min(Post_Size,Top_Order_Size),\ Orderbook['bids'][0]['price'])) sms_notification("There will be %lf perpetual shorted at %lf [y/n]"%(min(Post_Size,Top_Order_Size),\ Orderbook['bids'][0]['price'])) Execute_or_Not = input('Post those orders? [y/n]') if Execute_or_Not == 'y': ###------ Post Order ------### Sell_Size = int(min(\ Post_Size,\ Top_Order_Size\ )*Orderbook['bids'][0]['price']/10\ ) if Sell_Size >=1: self.MyAccount.sell('BTC-PERPETUAL',Sell_Size , Orderbook['bids'][0]['price'], False) time.sleep(self.Trading_Frequency) else: print("Sell Size is smaller than the minimal requirement.") ###------ Post Order Completed ------### else: print("Trading Stop") return False BTC_Price = self.MyAccount.index()['btc'] Perpetual_Position,Option_Position = self.get_current_btc_postition(Instrument) # Clear the previous perpetual position Orderbook = self.MyAccount.getorderbook('BTC-PERPETUAL') while (int(Orderbook['bids'][0]['price']*Perpetual_Position/10-1e-3)<=-1) and (BTC_Price>=Strike_Price): # Closing position Execute_or_Not = input('Cancel all orders?[y/n]') if Execute_or_Not == 'y': self.MyAccount.cancelall() else: return False Perpetual_Position,Option_Position = self.get_current_btc_postition(Instrument) BTC_Price = self.MyAccount.index()['btc'] if Perpetual_Position>0: # Short Perpetual Orderbook = self.MyAccount.getorderbook('BTC-PERPETUAL') Post_Size = min(abs(float(Perpetual_Position)),Orderbook['bids'][0]['quantity']/Orderbook['bids'][0]['price']) print("There will be %lf perpetual shorted at %lf[y/n]"%(Post_Size,Orderbook['bids'][0]['price'])) sms_notification("There will be %lf perpetual shorted at %lf"%(Post_Size,Orderbook['bids'][0]['price'])) Execute_or_Not = input('Post those orders? [y/n]') if Execute_or_Not == 'y': ###------ Post Order ------### Sell_Size = int(Post_Size*Orderbook['bids'][0]['price']/10) if Sell_Size >= 1: self.MyAccount.sell('BTC-PERPETUAL', Sell_Size, Orderbook['bids'][0]['price'], False) # Post_Size is counted as BTC, but sell perpetual is counted as 10 USD, so we do a bit transfer. time.sleep(self.Trading_Frequency) else: print("Sell Size is smaller than the minimal requirement.") ###------ Post Order Completed ------### else: return False if Perpetual_Position<0: # Long Perpetual Orderbook = self.MyAccount.getorderbook('BTC-PERPETUAL') Post_Size = min(abs(float(Perpetual_Position)),Orderbook['asks'][0]['quantity']/Orderbook['asks'][0]['price']) print("There will be %lf perpetual longed at %lf"%(Post_Size,Orderbook['asks'][0]['price'])) sms_notification("There will be %lf perpetual longed at %lf[y/n]"%(Post_Size,Orderbook['asks'][0]['price'])) Execute_or_Not = input('Post those orders? [y/n]') if Execute_or_Not == 'y': ###------ Post Order ------### Buy_Size = int(Post_Size*Orderbook['asks'][0]['price']/10) if Buy_Size >= 1: self.MyAccount.buy('BTC-PERPETUAL', Buy_Size, Orderbook['asks'][0]['price'], False) # Post_Size is counted as BTC, but sell perpetual is counted as 10 USD, so we do a bit transfer. time.sleep(self.Trading_Frequency) else: print("Buy Size is smaller than the minimal requirement.") ###------ Post Order Completed ------### else: return False return True def short_option(self,Date,Year,Strike_Price,Contract_Type): Instrument = "BTC-%s%s-%s-%s"%(Date.upper(),Year,Strike_Price,Contract_Type.upper()) Strike_Price = float(Strike_Price) # Compute margin and fee cost markPrice = self.MyAccount.getsummary(Instrument)['markPrice'] btcPrice = self.MyAccount.index()['btc'] MM_Option = max(0.075,0.075*markPrice)+markPrice # Maintianence margin IM_Option = max(max(0.15 - (btcPrice - float(Strike_Price))/btcPrice,0.1)+markPrice,MM_Option)# Initial margin size = 0.25 IM_Future = 0.01+(0.005/100)*size Required_Balance = (IM_Option+self.Fee['Option'])*size + size*(IM_Future+self.Fee['Future']['Market']) Account = self.MyAccount.account() while Required_Balance<=Account['availableFunds']: print('Required Balance = %lf if size = %lf'%(Required_Balance,size)) size += 0.05 IM_Future = 0.01+(0.005/100)*size Required_Balance = (IM_Option+self.Fee['Option'])*size + size*(IM_Future+self.Fee['Future']['Market']) # Short Option Short_Size = float(input("Short Size:")) # Total position will be shorted pprint({\ 'availableFunds':self.MyAccount.account()['availableFunds']\ ,'balance':self.MyAccount.account()['balance']\ ,'equity':self.MyAccount.account()['equity']\ ,'initialMargin':self.MyAccount.account()['initialMargin']\ ,'maintenanceMargin':self.MyAccount.account()['maintenanceMargin']\ }) while Short_Size !=0: if len(self.MyAccount.positions()) !=0: Current_Size = self.MyAccount.positions()\ [\ [p['instrument'] == Instrument for p in self.MyAccount.positions()].index(True)\ ]['size'] else: Current_Size = 0 Short_Size = Short_Size - abs(float(Current_Size)) print("Short Option: %lf"%Short_Size) # Print Short Size # Clear all open orders Execute_or_Not = input('Cancel all orders?[y/n]') if Execute_or_Not == 'y': self.MyAccount.cancelall() else: return False # Post new orders if not self.post_order(Instrument,'Short',Short_Size): print("Trading stop") pprint(self.MyAccount.account()) return 0 Continue_or_Not = input('Continue Short Options or Not [y/n]') # Hedge Mon = list(calendar.month_abbr).index(Date[(len(Date)-3):len(Date)][0].upper()+Date[(len(Date)-3):len(Date)][1:3].lower()) Day = int(Date[0:(len(Date)-3)]) settle_time = datetime.datetime(int("20%s"%Year),Mon,Day,16,0) while datetime.datetime.now() <=settle_time: if not self.hedge(Instrument,Strike_Price): print("Trading stop") pprint(self.MyAccount.account()) return 0 return 0 def alert(self): BTC_Price = self.MyAccount.index()['btc'] if BTC_Price<9125: sms_notification("Wake up! Price is out of bound")
class MarketMaker( object ): def __init__( self, monitor = True, output = True ): self.longperp = None self.equity_usd = None self.LEV_LIM = {} self.place_asks = {} self.place_bids = {} self.skew_size = {} self.LEV = 0 self.IM = 0 self.MAX_SKEW = 0 self.equity_btc = None self.equity_usd_init = None self.equity_btc_init = None self.con_size = float( CONTRACT_SIZE ) self.client = None self.deltas = OrderedDict() self.futures = OrderedDict() self.futures_prv = OrderedDict() self.logger = None self.mean_looptime = 1 self.monitor = monitor self.output = output or monitor self.positions = OrderedDict() self.spread_data = None self.this_mtime = None self.ts = None self.vols = OrderedDict() def create_client( self ): self.client = RestClient( KEY, SECRET, URL ) def get_bbo( self, contract ): # Get best b/o excluding own orders # Get orderbook ob = self.client.getorderbook( contract ) bids = ob[ 'bids' ] asks = ob[ 'asks' ] ords = self.client.getopenorders( contract ) bid_ords = [ o for o in ords if o[ 'direction' ] == 'buy' ] ask_ords = [ o for o in ords if o[ 'direction' ] == 'sell' ] best_bid = None best_ask = None err = 10 ** -( self.get_precision( contract ) + 1 ) for b in bids: match_qty = sum( [ o[ 'quantity' ] for o in bid_ords if math.fabs( b[ 'price' ] - o[ 'price' ] ) < err ] ) if match_qty < b[ 'quantity' ]: best_bid = b[ 'price' ] break for a in asks: match_qty = sum( [ o[ 'quantity' ] for o in ask_ords if math.fabs( a[ 'price' ] - o[ 'price' ] ) < err ] ) if match_qty < a[ 'quantity' ]: best_ask = a[ 'price' ] break return { 'bid': best_bid, 'ask': best_ask } def get_futures( self ): # Get all current futures instruments self.futures_prv = cp.deepcopy( self.futures ) insts = self.client.getinstruments() self.futures = sort_by_key( { i[ 'instrumentName' ]: i for i in insts if i[ 'kind' ] == 'future' and 'BTC' in i['instrumentName'] } ) for k, v in self.futures.items(): self.futures[ k ][ 'expi_dt' ] = datetime.strptime( v[ 'expiration' ][ : -4 ], '%Y-%m-%d %H:%M:%S' ) def get_pct_delta( self ): self.update_status() return sum( self.deltas.values()) / self.equity_btc def get_spot( self ): return self.client.index()[ 'btc' ] def get_precision( self, contract ): return self.futures[ contract ][ 'pricePrecision' ] def get_ticksize( self, contract ): return self.futures[ contract ][ 'tickSize' ] def output_status( self ): if not self.output: return None self.update_status() now = datetime.utcnow() days = ( now - self.start_time ).total_seconds() / SECONDS_IN_DAY #print( '********************************************************************' ) #print( 'Start Time: %s' % self.start_time.strftime( '%Y-%m-%d %H:%M:%S' )) #print( 'Current Time: %s' % now.strftime( '%Y-%m-%d %H:%M:%S' )) #print( 'Days: %s' % round( days, 1 )) #print( 'Hours: %s' % round( days * 24, 1 )) #print( 'Spot Price: %s' % self.get_spot()) pnl_usd = self.equity_usd - self.equity_usd_init pnl_btc = self.equity_btc - self.equity_btc_init #print( 'Equity ($): %7.2f' % self.equity_usd) #print( 'P&L ($) %7.2f' % pnl_usd) #print( 'Equity (BTC): %7.4f' % self.equity_btc) #print( 'P&L (BTC) %7.4f' % pnl_btc) #print( '%% Delta: %s%%'% round( self.get_pct_delta() / PCT, 1 )) #print( 'Total Delta (BTC): %s' % round( sum( self.deltas.values()), 2 )) print_dict_of_dicts( { k: { 'BTC': self.deltas[ k ] } for k in self.deltas.keys() }, roundto = 2, title = 'Deltas' ) print_dict_of_dicts( { k: { 'Contracts': self.positions[ k ][ 'size' ] } for k in self.positions.keys() }, title = 'Positions' ) if not self.monitor: print_dict_of_dicts( { k: { '%': self.vols[ k ] } for k in self.vols.keys() }, multiple = 100, title = 'Vols' ) #print( '\nMean Loop Time: %s' % round( self.mean_looptime, 2 )) #print( '' ) def place_orders( self ): if self.monitor: return None con_sz = self.con_size print(['BTC-PERPETUAL', 'BTC-25DEC20', 'BTC-25SEP20', 'BTC-26MAR21']) for fut in ['BTC-PERPETUAL', 'BTC-25DEC20', 'BTC-25SEP20', 'BTC-26MAR21']: print(fut) account = self.client.account() spot = self.get_spot() bal_btc = account[ 'equity' ] pos = self.positions[ fut ][ 'sizeBtc' ] expi = self.futures[ fut ][ 'expi_dt' ] tte = max( 0, ( expi - datetime.utcnow()).total_seconds() / SECONDS_IN_DAY ) pos_decay = 1.0 - math.exp( -DECAY_POS_LIM * tte ) min_order_size_btc = MIN_ORDER_SIZE / spot * CONTRACT_SIZE qtybtc = max( PCT_QTY_BASE * bal_btc, min_order_size_btc) nbids = MAX_LAYERS nasks = MAX_LAYERS #place_bids = nbids > 0 #place_asks = nasks > 0 self.MAX_SKEW = MIN_ORDER_SIZE * MAX_LAYERS * 10 #print(fut) spot = self.get_spot() ##print(res) t = 0 for pos in self.positions: t = t + math.fabs(self.positions[pos]['size']) self.equity_usd = self.equity_btc * spot self.LEV = t / self.equity_usd self.IM = self.LEV / 2 ##print(' ') ##print(fut) ##print(' ') for k in self.positions: self.skew_size[k[0:3]] = 0 ####print('skew_size: ' + str(self.skew_size)) ####print(self.positions) for k in self.positions: self.skew_size[k[0:3]] = self.skew_size[k[0:3]] + self.positions[k]['size'] ####print('skew_size: ' + str(self.skew_size)) #print(self.skew_size) psize = self.positions[fut]['size'] self.place_bids[fut] = True self.place_asks[fut] = True ###print(self.PCT_LIM_LONG) a = {} for pos in self.positions: a[pos] = 0 for pos in self.positions: a[pos] = a[pos] + math.fabs(self.positions[pos]['size']) ##print((((a[pos[0:3]] / self.equity_usd) / self.LEV_LIM[pos[0:3]] * 1000 ) / 10 )) ##print((((a[pos[0:3]] / self.equity_usd) / self.LEV_LIM[pos[0:3]] * 1000 ) / 10 )) ##print((((a[pos[0:3]] / self.equity_usd) / self.LEV_LIM[pos[0:3]] * 1000 ) / 10 )) #print(self.LEV_LIM) #print(a) if self.LEV_LIM[fut] == 0: ##print('lev lim 0!') if self.positions[fut]['size'] < 0: self.place_asks[fut] = False if self.positions[fut]['size'] > 0: self.place_bids[fut] = False elif (((a[fut] / self.equity_usd) / self.LEV_LIM[fut] * 1000 ) / 10 ) > 100: if self.positions[fut]['size'] < 0: self.place_asks[fut] = False if self.positions[pos]['size'] > 0: self.place_bids[pos] = False #print((((a[fut] / self.equity_usd) / self.LEV_LIM[fut] * 1000 ) / 10 )) if self.place_asks[fut] == False and self.place_bids[fut] == False: try: prc = self.get_bbo(fut)['bid'] qty = self.equity_usd / 48 / 10 / 2 qty = float(min( qty, MIN_ORDER_SIZE)) # ###print('qty: ' + str(qty)) ###print(max_bad_arb) qty = round(qty) for k in self.positions: self.skew_size[k[0:3]] = 0 for k in self.positions: self.skew_size[k[0:3]] = self.skew_size[k[0:3]] + self.positions[k]['size'] token = fut[0:3] if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 if self.positions[fut]['size'] >= 0: ##print(' ') ##print(' ') ##print(qty) ##print(self.skew_size[token]) ##print(self.MAX_SKEW) if qty + self.skew_size[token] * -1 <= self.MAX_SKEW: mexAsks = [] for i in range(0, round(MAX_LAYERS)): r = random.randint(0, 100) #print(r) if qty + self.skew_size[fut[0:3]] * -1 < self.MAX_SKEW: if i >= len_ask_ords: self.client.sell( fut, qty, asks[i], 'true' ) if self.positions[fut]['size'] <= 0: if qty + self.skew_size[token] < self.MAX_SKEW: mexBids = [] for i in range(0, round(MAX_LAYERS)): r = random.randint(0, 100) #print(r) if qty + self.skew_size[fut[0:3]] < self.MAX_SKEW: if i >= len_bid_ords: self.client.buy( fut, qty, bids[i], 'true' ) except: PrintException() tsz = self.get_ticksize( fut ) # Perform pricing bbo = self.get_bbo( fut ) bid_mkt = bbo[ 'bid' ] ask_mkt = bbo[ 'ask' ] if bid_mkt is None and ask_mkt is None: bid_mkt = ask_mkt = spot elif bid_mkt is None: bid_mkt = min( spot, ask_mkt ) elif ask_mkt is None: ask_mkt = max( spot, bid_mkt ) mid_mkt = 0.5 * ( bid_mkt + ask_mkt ) ords = self.client.getopenorders( fut ) cancel_oids = [] bid_ords = ask_ords = [] riskfac = 1 asks = [] bids = [] if self.place_bids[fut]: bid_ords = [ o for o in ords if o[ 'direction' ] == 'buy' ] len_bid_ords = min( len( bid_ords ), nbids ) bid0 = mid_mkt * math.exp( -MKT_IMPACT ) bids = [ bid0 * riskfac ** -i for i in range( 1, nbids + 1 ) ] bids[ 0 ] = ticksize_floor( bids[ 0 ], tsz ) if self.place_asks[fut]: ask_ords = [ o for o in ords if o[ 'direction' ] == 'sell' ] len_ask_ords = min( len( ask_ords ), nasks ) ask0 = mid_mkt * math.exp( MKT_IMPACT ) asks = [ ask0 * riskfac ** i for i in range( 1, nasks + 1 ) ] asks[ 0 ] = ticksize_ceil( asks[ 0 ], tsz ) nbids = len(bids) nasks = len(asks) # BIDS for i in range( 0, MAX_LAYERS ): if i < nbids: ###print(i) if i > 0: prc = ticksize_floor( min( bids[ i ], bids[ i - 1 ] - tsz ), tsz ) else: prc = bids[ 0 ] #qty = ( prc * qtybtc / con_sz ) qty = self.equity_usd / 48 / 10 / 2 qty = float(min( qty, MIN_ORDER_SIZE)) max_bad_arb = int(self.MAX_SKEW ) # ###print('qty: ' + str(qty)) ###print(max_bad_arb) qty = qty qty = round(qty) * (i+1) if qty <= 1: qty = 1 ###print('qty: ' + str(qty)) if self.MAX_SKEW < qty * 10 : self.MAX_SKEW = qty * 10 if self.place_asks[fut] == False: self.MAX_SKEW = self.MAX_SKEW * 3 qty = qty * 10 ob = self.client.getorderbook( fut ) bids1 = ob[ 'bids' ] asks1 = ob[ 'asks' ] prc = ticksize_floor(( bids1[0]['price']), tsz ) if qty + self.skew_size[fut[0:3]] > self.MAX_SKEW: print(fut+ ' bid self.MAX_SKEW return ...') for xyz in bid_ords: cancel_oids.append( xyz['orderId'] ) #self.execute_cancels(fut, nbids, nasks, bid_ords, ask_ords, qtybtc, con_sz, tsz, cancel_oids, len_bid_ords, len_ask_ords) continue ##print(len_bid_ords) ###print('i less') try: if i < len_bid_ords: sleep(RATE_LIMIT) ###print('i less') try: oid = bid_ords[ i ][ 'orderId' ] self.restartflag = False self.client.edit( oid, qty, prc ) except (SystemExit, KeyboardInterrupt): raise except Exception as e: PrintException() elif len_bid_ords <= MAX_LAYERS: if 'PERPETUAL' not in fut: if self.longperp == False: if self.arbmult[fut]['arb'] > 0 and (self.positions[fut]['size'] - qty <= max_bad_arb ): if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] < 0 : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('buy qty * 1.1, 1' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) else: print(self.longperp) if self.longperp == True: qty = qty * 2 print(qty) print(self.arbmult[fut]['arb']) print(self.positions[fut]['size']) print(max_bad_arb) if self.arbmult[fut]['arb'] < 0 and (self.positions[fut]['size'] - qty <= max_bad_arb ): if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] > 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('buy qty * 1.1, 2' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) elif len_bid_ords <= MAX_LAYERS: sleep(RATE_LIMIT) if 'PERPETUAL' not in fut: if self.longperp == False: if self.arbmult[fut]['arb'] > 0 and (self.positions[fut]['size'] - qty <= max_bad_arb ): if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] < 0 : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('buy qty * 1.1, 1' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) else: if self.longperp == True: qty = qty * 2 if self.arbmult[fut]['arb'] < 0 and (self.positions[fut]['size'] - qty <= max_bad_arb ): if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] > 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('buy qty * 1.1, 2' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.buy( fut, qty, prc, 'true' ) except (SystemExit, KeyboardInterrupt): raise except Exception as e: PrintException() self.logger.warn( 'Bid order failed: %s bid for %s' % ( prc, qty )) # OFFERS # ASKS ###print('# OFFERS') if i < nasks: if i > 0: prc = ticksize_ceil( max( asks[ i ], asks[ i - 1 ] + tsz ), tsz ) else: prc = asks[ 0 ] qty = self.equity_usd / 48 / 10 / 2 qty = float(min( qty, MIN_ORDER_SIZE)) #10 max_bad_arb = int(self.MAX_SKEW ) ###print('qty: ' + str(qty)) ###print(max_bad_arb) qty = qty qty = round(qty) * (i+1) if qty <= 1: qty = 1 if self.MAX_SKEW < qty * 10 : self.MAX_SKEW = qty * 10 if self.place_bids[fut] == False: self.MAX_SKEW = self.MAX_SKEW * 3 qty = qty * 10 ob = self.client.getorderbook( fut ) bids1 = ob[ 'bids' ] asks1 = ob[ 'asks' ] prc = ticksize_ceil(( asks1[0]['price']), tsz ) ###print('qty: ' + str(qty)) ####print('skew_size: ' + str(self.skew_size)) ####print('max_soew: ' + str(self.MAX_SKEW)) if qty + self.skew_size[fut[0:3]] * -1 > self.MAX_SKEW: print(fut + ' offer self.MAX_SKEW return ...') for xyz in ask_ords: cancel_oids.append( xyz['orderId'] ) #self.execute_cancels(fut, nbids, nasks, bid_ords, ask_ords, qtybtc, con_sz, tsz, cancel_oids, len_bid_ords, len_ask_ords) continue ##print(len_ask_ords) try: if i < len_ask_ords: sleep(RATE_LIMIT) ###print('i less') try: oid = ask_ords[ i ][ 'orderId' ] self.restartflag = False self.client.edit( oid, qty, prc ) except (SystemExit, KeyboardInterrupt): raise except Exception as e: PrintException() elif len_ask_ords <= MAX_LAYERS: if 'PERPETUAL' not in fut: if self.longperp == True: if self.arbmult[fut]['arb'] >= 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('sell qty * 1.1, 1' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] <= 0 and self.positions[fut]['size'] + qty * -1 >=-1 * max_bad_arb : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) else: if self.longperp == False: #print(self.arbmult) qty = qty * 2 if self.arbmult[fut]['arb'] <= 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('sell qty * 1.1, 2' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] >= 0 and self.positions[fut]['size'] + qty * -1 >=-1 * max_bad_arb : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) elif len_ask_ords <= MAX_LAYERS: sleep(RATE_LIMIT) if 'PERPETUAL' not in fut: if self.longperp == True: if self.arbmult[fut]['arb'] >= 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('sell qty * 1.1, 1' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] <= 0 and self.positions[fut]['size'] + qty * -1 >=-1 * max_bad_arb : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) else: if self.longperp == False: qty = qty * 2 if self.arbmult[fut]['arb'] <= 0: if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) qty = int(qty * 3) ###print('sell qty * 1.1, 2' + fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) if self.arbmult[fut]['arb'] >= 0 and self.positions[fut]['size'] + qty * -1 >=-1 * max_bad_arb : if 'ETH' in fut or 'XRP' in fut: qty = qty / self.get_bbo(fut[0:3] + 'USD')['bid'] if 'USD' in fut: qty = qty / self.get_multiplier(fut) if qty <= 1: qty = 1 self.restartflag = False self.client.sell( fut, qty, prc, 'true' )#self.client.sell( fut, qty, prc, 'true' ) except (SystemExit, KeyboardInterrupt): raise except Exception as e: PrintException() self.logger.warn( 'Offer order failed: %s at %s' % ( qty, prc )) if nbids < len( bid_ords ): cancel_oids += [ o[ 'orderId' ] for o in bid_ords[ nbids : ]] if nasks < len( ask_ords ): cancel_oids += [ o[ 'orderId' ] for o in ask_ords[ nasks : ]] for oid in cancel_oids: try: self.client.cancel( oid ) except: self.logger.warn( 'Order cancellations failed: %s' % oid ) def restart( self ): try: strMsg = 'RESTARTING' #print( strMsg ) self.client.cancelall() strMsg += ' ' for i in range( 0, 5 ): strMsg += '.' #print( strMsg ) sleep( 1 ) except: pass finally: os.execv( sys.executable, [ sys.executable ] + sys.argv ) def run( self ): self.run_first() self.output_status() t_ts = t_out = t_loop = t_mtime = datetime.utcnow() while True: bbos = [] arbs = {} #self.client.buy( 'BTC-PERPETUAL', size, mid * 1.02) expdays = {} for k in self.futures.keys(): bbo = self.get_bbo( k[0:3] + '-PERPETUAL' ) bid_mkt = bbo[ 'bid' ] ask_mkt = bbo[ 'ask' ] mid = 0.5 * ( bbo[ 'bid' ] + bbo[ 'ask' ] ) m = self.get_bbo(k) bid = m['bid'] ask=m['ask'] mid1 = 0.5 * (bid + ask) if k == 'ETHM20' or k == 'XRPM20': mid1 = mid1 * self.get_spot() arb = mid1 / mid arbs[k] = float(arb) #if 'PERPETUAL' not in k: ###print('perp is ' + str(mid) + ' ' + k + ' is ' + str(mid1) + ' and arb is ' + str(arb) + ' positive is sell negative is bbuy') expsecs = (self.futures[k]['expi_dt'] - datetime.now()).total_seconds() expday = expsecs / 60 / 24 expdays[k]=float(expday) bbos.append({k: mid1 - self.get_spot()}) fundvsprem = {} newarbs = {} self.arbmult = {} for k in arbs: if 'PERPETUAL' not in k: doin = ((-1*(1-arbs[k]) / expdays[k])* 100) ###print(k[0:3]) #print(k + '! Premium arb is ' + str(math.fabs(doin)) + ' %!') fundvsprem[k] = 'premium' newarbs[k] = doin self.arbmult[k] = {} arbs = newarbs for token in arbs: if arbs[token] < 0: self.arbmult[token] = ({'coin': token, 'long': 'futs', 'short': 'perp', 'arb': (arbs[token])}) else: self.arbmult[token] = ({'coin': token, 'long': 'perp', 'short': 'futs', 'arb': (arbs[token])}) #funding: {'ETH': {'coin': 'ETH', 'long': 'futs', 'short': 'perp', 'arb': -0.00030000000000000003}, 'XBT': {'coin': 'XBT', 'long': 'perp', 'short': 'futs', 'arb': 0.000153}, 'XRP': {'coin': 'XRP', 'long': 'futs', 'short': 'perp', 'arb': -0.0007440000000000001}} #premium: {'ETH': {'coin': 'ETH', 'long': 'futs', 'short': 'perp', 'arb': -0.00013291636050582245}, 'XBT': {'coin': 'XBT', 'long': 'perp', 'short': 'futs', 'arb': 3.722894995661838e-05}, 'XRP': {'coin': 'XRP', 'long': 'futs', 'short': 'perp', 'arb': -6.462857850516617e-05}} perplongs = 0 perpshorts = 0 for arb in self.arbmult: if self.arbmult[arb]['long'] == 'perp': perplongs = perplongs + 1 else: perpshorts = perpshorts + 1 if perplongs >= perpshorts: self.longperp = True self.arbmult['BTC-PERPETUAL'] = ({'coin': 'BTC-PERPETUAL', 'long': 'perp', 'short': 'futs', 'arb': 0.5}) else: self.longperp = False self.arbmult['BTC-PERPETUAL'] = ({'coin': 'BTC-PERPETUAL', 'long': 'futs', 'short': 'perp', 'arb': -0.5}) #print('self.longpperp? ' + str(self.longperp)) #print(self.arbmult) t = 0 c = 0 for arb in self.arbmult: t = t + math.fabs(self.arbmult[arb]['arb']) c = c + 1 #print('t: ' + str(t)) for arb in self.arbmult: self.arbmult[arb]['perc'] = math.fabs(round((self.arbmult[arb]['arb']) / t * 1000) / 1000) #* 1.41425 #print(self.arbmult) ##print(self.arbmult) for coin in arbs: self.LEV_LIM[coin] = LEVERAGE_MAX / 2 for coin in arbs: self.LEV_LIM[coin] = self.LEV_LIM[coin] * self.arbmult[coin]['perc'] self.LEV_LIM['BTC-PERPETUAL'] = LEVERAGE_MAX / 2 #print(self.LEV_LIM) skewingpos = 0 skewingneg = 0 positionSize = 0 for p in self.positions: positionSize = positionSize + self.positions[p]['size'] if self.positions[p]['size'] > 0: skewingpos = skewingpos + 1 elif self.positions[p]['size'] < 0: skewingneg = skewingneg + 1 self.get_futures() # Restart if a new contract is listed if len( self.futures ) != len( self.futures_prv ): self.restart() self.update_positions() t_now = datetime.utcnow() # Update time series and vols if ( t_now - t_ts ).total_seconds() >= WAVELEN_TS: t_ts = t_now self.update_timeseries() self.update_vols() self.place_orders() # Display status to terminal if self.output: t_now = datetime.utcnow() if ( t_now - t_out ).total_seconds() >= WAVELEN_OUT: self.output_status(); t_out = t_now # Restart if file change detected t_now = datetime.utcnow() if ( t_now - t_mtime ).total_seconds() > WAVELEN_MTIME_CHK: t_mtime = t_now if getmtime( __file__ ) > self.this_mtime: self.restart() t_now = datetime.utcnow() looptime = ( t_now - t_loop ).total_seconds() # Estimate mean looptime w1 = EWMA_WGT_LOOPTIME w2 = 1.0 - w1 t1 = looptime t2 = self.mean_looptime self.mean_looptime = w1 * t1 + w2 * t2 t_loop = t_now sleep_time = MIN_LOOP_TIME - looptime if sleep_time > 0: time.sleep( sleep_time ) if self.monitor: time.sleep( WAVELEN_OUT ) def run_first( self ): self.create_client() self.client.cancelall() self.logger = get_logger( 'root', LOG_LEVEL ) # Get all futures contracts self.get_futures() self.this_mtime = getmtime( __file__ ) self.symbols = [ BTC_SYMBOL ] + list( self.futures.keys()); self.symbols.sort() self.deltas = OrderedDict( { s: None for s in self.symbols } ) # Create historical time series data for estimating vol ts_keys = self.symbols + [ 'timestamp' ]; ts_keys.sort() self.ts = [ OrderedDict( { f: None for f in ts_keys } ) for i in range( NLAGS + 1 ) ] self.vols = OrderedDict( { s: VOL_PRIOR for s in self.symbols } ) self.start_time = datetime.utcnow() self.update_status() self.equity_usd_init = self.equity_usd self.equity_btc_init = self.equity_btc def update_status( self ): account = self.client.account() spot = self.get_spot() self.equity_btc = account[ 'equity' ] self.equity_usd = self.equity_btc * spot self.update_positions() self.deltas = OrderedDict( { k: self.positions[ k ][ 'sizeBtc' ] for k in self.futures.keys()} ) self.deltas[ BTC_SYMBOL ] = account[ 'equity' ] def update_positions( self ): self.positions = OrderedDict( { f: { 'size': 0, 'sizeBtc': 0, 'indexPrice': None, 'markPrice': None } for f in self.futures.keys() } ) positions = self.client.positions() for pos in positions: if pos[ 'instrument' ] in self.futures: self.positions[ pos[ 'instrument' ]] = pos def update_timeseries( self ): if self.monitor: return None for t in range( NLAGS, 0, -1 ): self.ts[ t ] = cp.deepcopy( self.ts[ t - 1 ] ) spot = self.get_spot() self.ts[ 0 ][ BTC_SYMBOL ] = spot for c in self.futures.keys(): bbo = self.get_bbo( c ) bid = bbo[ 'bid' ] ask = bbo[ 'ask' ] if not bid is None and not ask is None: mid = 0.5 * ( bbo[ 'bid' ] + bbo[ 'ask' ] ) else: continue self.ts[ 0 ][ c ] = mid self.ts[ 0 ][ 'timestamp' ] = datetime.utcnow() def update_vols( self ): if self.monitor: return None w = EWMA_WGT_COV ts = self.ts t = [ ts[ i ][ 'timestamp' ] for i in range( NLAGS + 1 ) ] p = { c: None for c in self.vols.keys() } for c in ts[ 0 ].keys(): p[ c ] = [ ts[ i ][ c ] for i in range( NLAGS + 1 ) ] if any( x is None for x in t ): return None for c in self.vols.keys(): if any( x is None for x in p[ c ] ): return None NSECS = SECONDS_IN_YEAR cov_cap = COV_RETURN_CAP / NSECS for s in self.vols.keys(): x = p[ s ] dx = x[ 0 ] / x[ 1 ] - 1 dt = ( t[ 0 ] - t[ 1 ] ).total_seconds() v = min( dx ** 2 / dt, cov_cap ) * NSECS v = w * v + ( 1 - w ) * self.vols[ s ] ** 2 self.vols[ s ] = math.sqrt( v )
class myDERIBIT: Access_key = "no" Access_secret = "no" client = 0 frontMfuture = 0 lastInfo = 'nothing yet' killDeribitThread = False contractCount = str(1) timeLeft = 500 * 60 emailSentTime = 5 def __init__(self): self.Access_key = os.environ.get('Access_key', 'no') self.Access_secret = os.environ.get('Access_secret', 'no') self.contractCount = os.environ.get('count', 'no') self.client = RestClient(self.Access_key, self.Access_secret) self.frontMfuture = self.getFronFutureName() timestampim = self.getLastTradetimeToDate() def ClosePosition(self, posParams, direction): closeDirection = 0 if direction == 'buy': closeDirection = 'sell' elif direction == 'sell': closeDirection = 'buy' self.BuyOrSellMarket(posParams, closeDirection) def BuyOrSellMarket(self, posParams, direction): response = 0 posParams['type'] = 'market' posParams['price'] = '' if direction == 'buy': try: response = self.client.buy(posParams['instrument'], posParams['quantity'], posParams['price']) except Exception: nonce = int(time.time() * 1000) signature = self.deribit_signature(nonce, '/api/v1/private/buy', posParams, self.Access_key, self.Access_secret) response = self.client.session.post( 'https://www.deribit.com' + '/api/v1/private/buy', data=posParams, headers={'x-deribit-sig': signature}, verify=True) elif direction == 'sell': try: self.client.sell(posParams['instrument'], posParams['quantity'], posParams['price']) except Exception: nonce = int(time.time() * 1000) signature = self.deribit_signature(nonce, '/api/v1/private/sell', posParams, self.Access_key, self.Access_secret) response = self.client.session.post( 'https://www.deribit.com' + '/api/v1/private/sell', data=posParams, headers={'x-deribit-sig': signature}, verify=True) def getPositionsWithSlippage(self): posParams = {} posJson = 0 direction = 0 try: posJson = self.value(self.client.positions()) except Exception: nonce = int(time.time() * 1000) signature = self.deribit_signature(nonce, '/api/v1/private/positions', posParams, self.Access_key, self.Access_secret) response = self.client.session.post( 'https://www.deribit.com' + '/api/v1/private/positions', data=posParams, headers={'x-deribit-sig': signature}, verify=True) posJson = self.value(response.json())['result'] if posJson.empty: direction = 0 else: posParams['instrument'] = str(posJson['instrument'][0]) direction = str(posJson['direction'][0]) posParams['quantity'] = str(abs(posJson['size'][0])) if direction == 'buy': posParams['price'] = str(posJson['markPrice'][0] - 5) elif direction == 'sell': posParams['price'] = str(posJson['markPrice'][0] + 5) return posParams, direction def deribit_signature(self, nonce, uri, params, access_key, access_secret): sign = '_=%s&_ackey=%s&_acsec=%s&_action=%s' % (nonce, access_key, access_secret, uri) for key in sorted(params.keys()): sign += '&' + key + '=' + "".join(params[key]) return '%s.%s.%s' % (access_key, nonce, base64.b64encode(hashlib.sha256(sign).digest())) def value(self, df): return json_normalize(df) def getFronFutureName(self): instruments = self.client.getinstruments() instruments0 = json_normalize(instruments) instruments0 = pd.DataFrame.from_dict(instruments) futures = instruments0[instruments0['kind'].str.contains("uture")] frontMfuture = futures.iloc[0]['instrumentName'] now = datetime.datetime.now() for i in xrange(len(futures)): notPERPETUAL = 'PERPETUAL' not in futures.iloc[i]['instrumentName'] yearDif = int(futures.iloc[i]['expiration'][0:4]) - int(now.year) monthDif = int(futures.iloc[i]['expiration'][5:7]) - int(now.month) dayDif = int(futures.iloc[i]['expiration'][8:10]) - int(now.day) if notPERPETUAL and yearDif == 1: frontMfuture = futures.iloc[i]['instrumentName'] break if notPERPETUAL and yearDif == 0 and monthDif > 0: frontMfuture = futures.iloc[i]['instrumentName'] break if notPERPETUAL and monthDif == 0 and dayDif > 4: frontMfuture = futures.iloc[i]['instrumentName'] break return frontMfuture def getLastTradetimeToDate(self, instrument=None): if instrument == None: instrument = self.frontMfuture timestampim = (self.value( self.client.getlasttrades(instrument))['timeStamp'][0]) timestampim = time.strftime("%a %d %b %Y %H:%M:%S GMT", time.gmtime(timestampim / 1000.0)) return timestampim def getBestBidAsk(self, instrument=None): if instrument == None: instrument = self.frontMfuture ask = self.value(self.client.getorderbook(instrument)['asks'][0])[ 'price'] # satmaq isdiyenner bid = self.value(self.client.getorderbook(instrument)['bids'][0])[ 'price'] #almaq istiyenner return ask, bid
if p > biggest: biggest = p smallest = 9999999999999999 for c in costed: #print(costed[c]) if float(costed[c]) < smallest: smallest = float(costed[c]) w1 = c try: print(' ') print('exposure: ' + str(therisk)) print('cost to buy: ' + str(smallest)) print('profit per unit at +/- 5%: ' + str(w1)) print('exposure covered: ' + str(smallest / profits[w1]['price'] * w1)) print(profits[w1]) #self.options[profits[w1]['call'] + profits[w1]['put']] = smallest / profits[w1]['price'] qty = smallest / 2 qty = qty * 10 qty = math.ceil(qty) qty = qty / 10 print(profits[w1]['put']) print(qty) print(qty, profits[w1]['costp']) client.buy(profits[w1]['put'], qty, profits[w1]['costp']) client.buy(profits[w1]['call'], qty, profits[w1]['costc']) #self.calls.append(profits[w1]['call']) #self.puts.append(profits[w1]['put']) except Exception as e: e = e
class MarketMaker(object): def __init__(self, monitor=True, output=True): self.equity_usd = None self.equity_btc = None self.eth = 0 self.equity_usd_init = None self.equity_btc_init = None self.con_size = float(CONTRACT_SIZE) self.client = None self.deltas = OrderedDict() self.futures = OrderedDict() self.futures_prv = OrderedDict() self.logger = None self.volatility = 0 self.bbw = {} self.atr = {} self.diffdeltab = {} self.diff2 = 0 self.diff3 = 0 self.bands = {} self.quantity_switch = [] self.price = [] self.buysellsignal = {} self.directional = [] self.seriesData = {} self.seriesData[(datetime.strptime( (date.today() - timedelta(days=1)).strftime('%Y-%m-%d'), '%Y-%m-%d'))] = 0 self.seriesPercent = {} self.startUsd = {} self.firstfirst = True self.dsrsi = 50 self.minMaxDD = None self.maxMaxDD = None self.ws = {} self.ohlcv = {} self.mean_looptime = 1 self.monitor = monitor self.output = output or monitor self.positions = OrderedDict() self.spread_data = None self.this_mtime = None self.ts = None self.vols = OrderedDict() self.multsShort = {} self.multsLong = {} self.diff = 1 def create_client(self): self.client = RestClient(KEY, SECRET, URL) def get_bbo(self, contract): # Get best b/o excluding own orders j = self.ohlcv[contract].json() fut2 = contract #print(contract) best_bids = [] best_asks = [] o = [] h = [] l = [] c = [] v = [] for b in j['result']['open']: o.append(b) for b in j['result']['high']: h.append(b) for b in j['result']['low']: l.append(b) for b in j['result']['close']: c.append(b) for b in j['result']['volume']: v.append(b) abc = 0 ohlcv2 = [] for b in j['result']['open']: ohlcv2.append([o[abc], h[abc], l[abc], c[abc], v[abc]]) abc = abc + 1 ddf = pd.DataFrame(ohlcv2, columns=['open', 'high', 'low', 'close', 'volume']) if 1 in self.directional: sleep(0) try: self.dsrsi = TA.STOCHRSI(ddf).iloc[-1] * 100 except: self.dsrsi = 50 ##print(self.dsrsi) # Get orderbook if 2 in self.volatility or 3 in self.price or 4 in self.quantity_switch: self.bands[fut2] = TA.BBANDS(ddf).iloc[-1] self.bbw[fut2] = (TA.BBWIDTH(ddf).iloc[-1]) #print(float(self.bands[fut2]['BB_UPPER'] - self.bands[fut2]['BB_LOWER'])) if (float(self.bands[fut2]['BB_UPPER'] - self.bands[fut2]['BB_LOWER'])) > 0: deltab = (self.get_spot() - self.bands[fut2]['BB_LOWER']) / ( self.bands[fut2]['BB_UPPER'] - self.bands[fut2]['BB_LOWER']) if deltab > 50: self.diffdeltab[fut2] = (deltab - 50) / 100 + 1 if deltab < 50: self.diffdeltab[fut2] = (50 - deltab) / 100 + 1 else: self.diffdeltab[fut2] = 25 / 100 + 1 if 3 in self.volatility: self.atr[fut2] = TA.ATR(ddf).iloc[-1] if 0 in self.price: ob = self.client.getorderbook(contract) bids = ob['bids'] asks = ob['asks'] ords = self.client.getopenorders(contract) bid_ords = [o for o in ords if o['direction'] == 'buy'] ask_ords = [o for o in ords if o['direction'] == 'sell'] best_bid = None best_ask = None err = 10**-(self.get_precision(contract) + 1) for b in bids: match_qty = sum([ o['quantity'] for o in bid_ords if math.fabs(b['price'] - o['price']) < err ]) if match_qty < b['quantity']: best_bid = b['price'] break for a in asks: match_qty = sum([ o['quantity'] for o in ask_ords if math.fabs(a['price'] - o['price']) < err ]) if match_qty < a['quantity']: best_ask = a['price'] break best_asks.append(best_ask) best_bids.append(best_bid) if 1 in self.price: dvwap = TA.VWAP(ddf) ##print(dvwap) tsz = self.get_ticksize(contract) try: bid = ticksize_floor(dvwap.iloc[-1], tsz) ask = ticksize_ceil(dvwap.iloc[-1], tsz) except: bid = ticksize_floor(self.get_spot(), tsz) ask = ticksize_ceil(self.get_spot(), tsz) #print( { 'bid': bid, 'ask': ask }) best_asks.append(best_ask) best_bids.append(best_bid) if 2 in self.quantity_switch: dppo = TA.PPO(ddf) self.buysellsignal[fut2] = 1 try: if (dppo.iloc[-1].PPO > 0): self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 + PRICE_MOD) else: self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 - PRICE_MOD) if (dppo.iloc[-1].HISTO > 0): self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 + PRICE_MOD) else: self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 - PRICE_MOD) if (dppo.iloc[-1].SIGNAL > 0): self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 + PRICE_MOD) else: self.buysellsignal[fut2] = self.buysellsignal[fut2] * ( 1 - PRICE_MOD) except: self.buysellsignal[fut2] = 1 ##print({ 'bid': best_bid, 'ask': best_ask }) return { 'bid': self.cal_average(best_bids), 'ask': self.cal_average(best_asks) } def get_futures(self): # Get all current futures instruments self.futures_prv = cp.deepcopy(self.futures) insts = self.client.getinstruments() self.futures = sort_by_key({ i['instrumentName']: i for i in insts if ('BTC-PERPETUAL' in i['instrumentName']) and i['kind'] == 'future' # }) for k, v in self.futures.items(): self.futures[k]['expi_dt'] = datetime.strptime( v['expiration'][:-4], '%Y-%m-%d %H:%M:%S') def get_pct_delta(self): self.update_status() return sum(self.deltas.values()) / self.equity_btc def get_spot(self): return self.client.index()['btc'] def get_precision(self, contract): return self.futures[contract]['pricePrecision'] def get_ticksize(self, contract): return self.futures[contract]['tickSize'] def output_status(self): startLen = (len(self.seriesData)) if self.startUsd != {}: startUsd = self.startUsd nowUsd = self.equity_usd diff = 100 * ((nowUsd / startUsd) - 1) print('diff') print(diff) if diff < self.diff2: self.diff2 = diff if diff > self.diff3: self.diff3 = diff if self.diff3 > self.maxMaxDD: print('broke max max dd! sleep 24hr') time.sleep(60 * 60 * 24) self.diff3 = 0 self.startUsd = self.equity_usd if self.diff2 < self.minMaxDD: print('broke min max dd! sleep 24hr') time.sleep(60 * 60 * 24) self.diff2 = 0 self.startUsd = self.equity_usd self.seriesData[(datetime.strptime( datetime.today().strftime('%Y-%m-%d'), '%Y-%m-%d'))] = self.diff2 endLen = (len(self.seriesData)) if endLen != startLen: self.seriesPercent[(datetime.strptime( datetime.today().strftime('%Y-%m-%d'), '%Y-%m-%d'))] = diff self.diff2 = 0 self.diff3 = 0 self.startUsd = self.equity_usd s = pd.Series(self.seriesData) print(s) print(qs.stats.max_drawdown(s)) if not self.output: return None self.update_status() now = datetime.utcnow() days = (now - self.start_time).total_seconds() / SECONDS_IN_DAY print( '********************************************************************' ) print('Start Time: %s' % self.start_time.strftime('%Y-%m-%d %H:%M:%S')) print('Current Time: %s' % now.strftime('%Y-%m-%d %H:%M:%S')) print('Days: %s' % round(days, 1)) print('Hours: %s' % round(days * 24, 1)) print('Spot Price: %s' % self.get_spot()) pnl_usd = self.equity_usd - self.equity_usd_init pnl_btc = self.equity_btc - self.equity_btc_init if self.firstfirst == True: self.startUsd = self.equity_usd self.firstfirst = False print('Equity ($): %7.2f' % self.equity_usd) print('P&L ($) %7.2f' % pnl_usd) print('Equity (BTC): %7.4f' % self.equity_btc) print('P&L (BTC) %7.4f' % pnl_btc) ##print( '%% Delta: %s%%'% round( self.get_pct_delta() / PCT, 1 )) ##print( 'Total Delta (BTC): %s' % round( sum( self.deltas.values()), 2 )) #print_dict_of_dicts( { # k: { # 'BTC': self.deltas[ k ] # } for k in self.deltas.keys() # }, # roundto = 2, title = 'Deltas' ) print_dict_of_dicts( { k: { 'Contracts': self.positions[k]['size'] } for k in self.positions.keys() }, title='Positions') if not self.monitor: print_dict_of_dicts( {k: { '%': self.vols[k] } for k in self.vols.keys()}, multiple=100, title='Vols') #print( '\nMean Loop Time: %s' % round( self.mean_looptime, 2 )) #print( '' ) for k in self.positions.keys(): self.multsShort[k] = 1 self.multsLong[k] = 1 if 'sizeEth' in self.positions[k]: key = 'sizeEth' else: key = 'sizeBtc' if self.positions[k][key] > 10: self.multsShort[k] = (self.positions[k][key] / 10) * POS_MOD if self.positions[k][key] < -1 * 10: self.multsLong[k] = (-1 * self.positions[k]['currentQty'] / 10) * POS_MOD #Vols #print(self.multsLong) #print(self.multsShort) def place_orders(self): if self.monitor: return None con_sz = self.con_size for fut in self.futures.keys(): account = self.client.account() spot = self.get_spot() bal_btc = account['equity'] * 100 pos_lim_long = bal_btc * PCT_LIM_LONG / len(self.futures) pos_lim_short = bal_btc * PCT_LIM_SHORT / len(self.futures) expi = self.futures[fut]['expi_dt'] ##print(self.futures[ fut ][ 'expi_dt' ]) if self.eth is 0: self.eth = 200 if 'ETH' in fut: if 'sizeEth' in self.positions[fut]: pos = self.positions[fut][ 'sizeEth'] * self.eth / self.get_spot() else: pos = 0 else: pos = self.positions[fut]['sizeBtc'] tte = max(0, (expi - datetime.utcnow()).total_seconds() / SECONDS_IN_DAY) pos_decay = 1.0 - math.exp(-DECAY_POS_LIM * tte) pos_lim_long *= pos_decay pos_lim_short *= pos_decay pos_lim_long -= pos pos_lim_short += pos pos_lim_long = max(0, pos_lim_long) pos_lim_short = max(0, pos_lim_short) min_order_size_btc = MIN_ORDER_SIZE / spot * CONTRACT_SIZE #qtybtc = max( PCT_QTY_BASE * bal_btc, min_order_size_btc) qtybtc = PCT_QTY_BASE * bal_btc nbids = min(math.trunc(pos_lim_long / qtybtc), MAX_LAYERS) nasks = min(math.trunc(pos_lim_short / qtybtc), MAX_LAYERS) place_bids = nbids > 0 place_asks = nasks > 0 #buy bid sell ask if self.dsrsi > 80: #over place_bids = 0 if self.dsrsi < 20: #under place_asks = 0 if not place_bids and not place_asks: #print( 'No bid no offer for %s' % fut, min_order_size_btc ) continue tsz = self.get_ticksize(fut) # Perform pricing vol = max(self.vols[BTC_SYMBOL], self.vols[fut]) if 1 in self.volatility: eps = BP * vol * RISK_CHARGE_VOL if 0 in self.volatility: eps = BP * 0.5 * RISK_CHARGE_VOL if 2 in self.price: eps = eps * self.diff if 3 in self.price: if self.diffdeltab[fut] > 0 or self.diffdeltab[fut] < 0: eps = eps * self.diffdeltab[fut] if 2 in self.volatility: eps = eps * (1 + self.bbw[fut]) if 3 in self.volatility: eps = eps * (self.atr[fut] / 100) riskfac = math.exp(eps) bbo = self.get_bbo(fut) bid_mkt = bbo['bid'] ask_mkt = bbo['ask'] mid = 0.5 * (bbo['bid'] + bbo['ask']) mid_mkt = 0.5 * (bid_mkt + ask_mkt) ords = self.client.getopenorders(fut) cancel_oids = [] bid_ords = ask_ords = [] if place_bids: bid_ords = [o for o in ords if o['direction'] == 'buy'] len_bid_ords = min(len(bid_ords), nbids) bid0 = mid_mkt * math.exp(-MKT_IMPACT) bids = [bid0 * riskfac**-i for i in range(1, nbids + 1)] bids[0] = ticksize_floor(bids[0], tsz) if place_asks: ask_ords = [o for o in ords if o['direction'] == 'sell'] len_ask_ords = min(len(ask_ords), nasks) ask0 = mid_mkt * math.exp(MKT_IMPACT) asks = [ask0 * riskfac**i for i in range(1, nasks + 1)] asks[0] = ticksize_ceil(asks[0], tsz) for i in range(max(nbids, nasks)): # BIDS #print('nbids') #print(nbids) #print('nasks') #print(nasks) if place_bids and i < nbids: if i > 0: prc = ticksize_floor(min(bids[i], bids[i - 1] - tsz), tsz) else: prc = bids[0] qty = round(prc * qtybtc / (con_sz / 1)) if 'ETH' in fut: qty = round(prc * 450 * qtybtc / (con_sz / 1)) if 4 in self.quantity_switch: if self.diffdeltab[fut] > 0 or self.diffdeltab[fut] < 0: qty = round(qty / (self.diffdeltab[fut])) if 2 in self.quantity_switch: qty = round(qty * self.buysellsignal[fut]) if 3 in self.quantity_switch: qty = round(qty * self.multsLong[fut]) if 1 in self.quantity_switch: qty = round(qty / self.diff) if qty < 0: qty = qty * -1 if i < len_bid_ords: oid = bid_ords[i]['orderId'] try: self.client.edit(oid, qty, prc) except (SystemExit, KeyboardInterrupt): raise except: try: self.client.buy(fut, qty, prc, 'true') cancel_oids.append(oid) self.logger.warn('Edit failed for %s' % oid) except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Bid order failed: %s bid for %s' % (prc, qty)) else: try: self.client.buy(fut, qty, prc, 'true') except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Bid order failed: %s bid for %s' % (prc, qty)) # OFFERS if place_asks and i < nasks: if i > 0: prc = ticksize_ceil(max(asks[i], asks[i - 1] + tsz), tsz) else: prc = asks[0] qty = round(prc * qtybtc / (con_sz / 1)) if 'ETH' in fut: qty = round(prc * 450 * qtybtc / (con_sz / 1)) #print(qty) #print(qty) #print(qty) #print(qty) if 4 in self.quantity_switch: if self.diffdeltab[fut] > 0 or self.diffdeltab[fut] < 0: qty = round(qty / (self.diffdeltab[fut])) if 2 in self.quantity_switch: qty = round(qty / self.buysellsignal[fut]) if 3 in self.quantity_switch: qty = round(qty * self.multsShort[fut]) #print(qty) #print(qty) #print(qty) #print(qty) if 1 in self.quantity_switch: qty = round(qty / self.diff) if qty < 0: qty = qty * -1 if i < len_ask_ords: oid = ask_ords[i]['orderId'] try: self.client.edit(oid, qty, prc) except (SystemExit, KeyboardInterrupt): raise except: try: self.client.sell(fut, qty, prc, 'true') cancel_oids.append(oid) self.logger.warn('Sell Edit failed for %s' % oid) except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Offer order failed: %s at %s' % (qty, prc)) else: try: self.client.sell(fut, qty, prc, 'true') except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn('Offer order failed: %s at %s' % (qty, prc)) if nbids < len(bid_ords): cancel_oids += [o['orderId'] for o in bid_ords[nbids:]] if nasks < len(ask_ords): cancel_oids += [o['orderId'] for o in ask_ords[nasks:]] for oid in cancel_oids: try: self.client.cancel(oid) except: self.logger.warn('Order cancellations failed: %s' % oid) def restart(self): try: strMsg = 'RESTARTING' #print( strMsg ) self.client.cancelall() strMsg += ' ' for i in range(0, 5): strMsg += '.' #print( strMsg ) sleep(1) except: pass finally: os.execv(sys.executable, [sys.executable] + sys.argv) def run(self): self.run_first() self.output_status() t_ts = t_out = t_loop = t_mtime = datetime.utcnow() while True: self.get_futures() # Directional # 0: none # 1: StochRSI # # Price # 0: none # 1: vwap # 2: ppo # # Volatility # 0: none # 1: ewma with open('deribit-settings.json', 'r') as read_file: data = json.load(read_file) self.maxMaxDD = data['maxMaxDD'] self.minMaxDD = data['minMaxDD'] self.directional = data['directional'] self.price = data['price'] self.volatility = data['volatility'] self.quantity_switch = data['quantity'] # Restart if a new contract is listed if len(self.futures) != len(self.futures_prv): self.restart() self.update_positions() t_now = datetime.utcnow() # Update time series and vols if (t_now - t_ts).total_seconds() >= WAVELEN_TS: t_ts = t_now for contract in self.futures.keys(): self.ohlcv[contract] = requests.get( 'https://test.deribit.com/api/v2/public/get_tradingview_chart_data?instrument_name=' + contract + '&start_timestamp=' + str(int(time.time()) * 1000 - 1000 * 60 * 60) + '&end_timestamp=' + str(int(time.time()) * 1000) + '&resolution=1') self.update_timeseries() self.update_vols() self.place_orders() # Display status to terminal if self.output: t_now = datetime.utcnow() if (t_now - t_out).total_seconds() >= WAVELEN_OUT: self.output_status() t_out = t_now # Restart if file change detected t_now = datetime.utcnow() if (t_now - t_mtime).total_seconds() > WAVELEN_MTIME_CHK: t_mtime = t_now if getmtime(__file__) > self.this_mtime: self.restart() t_now = datetime.utcnow() looptime = (t_now - t_loop).total_seconds() # Estimate mean looptime w1 = EWMA_WGT_LOOPTIME w2 = 1.0 - w1 t1 = looptime t2 = self.mean_looptime self.mean_looptime = w1 * t1 + w2 * t2 t_loop = t_now sleep_time = MIN_LOOP_TIME - looptime if sleep_time > 0: time.sleep(sleep_time) if self.monitor: time.sleep(WAVELEN_OUT) def cal_average(self, num): sum_num = 0 for t in num: sum_num = sum_num + t avg = sum_num / len(num) return avg def run_first(self): self.create_client() self.client.cancelall() self.logger = get_logger('root', LOG_LEVEL) # Get all futures contracts self.get_futures() for k in self.futures.keys(): self.ohlcv[k] = requests.get( 'https://test.deribit.com/api/v2/public/get_tradingview_chart_data?instrument_name=' + k + '&start_timestamp=' + str(int(time.time()) * 1000 - 1000 * 60 * 60) + '&end_timestamp=' + str(int(time.time()) * 1000) + '&resolution=1') self.bbw[k] = 0 self.bands[k] = [] self.atr[k] = 0 self.diffdeltab[k] = 0 self.buysellsignal[k] = 1 self.this_mtime = getmtime(__file__) self.symbols = [BTC_SYMBOL] + list(self.futures.keys()) self.symbols.sort() self.deltas = OrderedDict({s: None for s in self.symbols}) # Create historical time series data for estimating vol ts_keys = self.symbols + ['timestamp'] ts_keys.sort() self.ts = [ OrderedDict({f: None for f in ts_keys}) for i in range(NLAGS + 1) ] self.vols = OrderedDict({s: VOL_PRIOR for s in self.symbols}) self.start_time = datetime.utcnow() self.update_status() self.equity_usd_init = self.equity_usd self.equity_btc_init = self.equity_btc def update_status(self): account = self.client.account() spot = self.get_spot() self.equity_btc = account['equity'] self.equity_usd = self.equity_btc * spot self.update_positions() # self.deltas = OrderedDict( # { k: self.positions[ k ][ 'sizeBtc' ] for k in self.futures.keys()} # ) # self.deltas[ BTC_SYMBOL ] = account[ 'equity' ] def update_positions(self): self.positions = OrderedDict({ f: { 'size': 0, 'sizeBtc': 0, 'indexPrice': None, 'markPrice': None } for f in self.futures.keys() }) positions = self.client.positions() for pos in positions: if 'ETH' in pos['instrument']: pos['size'] = pos['size'] / 10 if pos['instrument'] in self.futures: self.positions[pos['instrument']] = pos def update_timeseries(self): if self.monitor: return None for t in range(NLAGS, 0, -1): self.ts[t] = cp.deepcopy(self.ts[t - 1]) spot = self.get_spot() self.ts[0][BTC_SYMBOL] = spot for contract in self.futures.keys(): ob = self.client.getorderbook(contract) bids = ob['bids'] asks = ob['asks'] ords = self.client.getopenorders(contract) bid_ords = [o for o in ords if o['direction'] == 'buy'] ask_ords = [o for o in ords if o['direction'] == 'sell'] best_bid = None best_ask = None err = 10**-(self.get_precision(contract) + 1) for b in bids: match_qty = sum([ o['quantity'] for o in bid_ords if math.fabs(b['price'] - o['price']) < err ]) if match_qty < b['quantity']: best_bid = b['price'] break for a in asks: match_qty = sum([ o['quantity'] for o in ask_ords if math.fabs(a['price'] - o['price']) < err ]) if match_qty < a['quantity']: best_ask = a['price'] break bid = best_bid ask = best_ask if not bid is None and not ask is None: mid = 0.5 * (bid + ask) else: continue self.ts[0][contract] = mid self.ts[0]['timestamp'] = datetime.utcnow() def update_vols(self): if self.monitor: return None w = EWMA_WGT_COV ts = self.ts t = [ts[i]['timestamp'] for i in range(NLAGS + 1)] p = {c: None for c in self.vols.keys()} for c in ts[0].keys(): p[c] = [ts[i][c] for i in range(NLAGS + 1)] if any(x is None for x in t): return None for c in self.vols.keys(): if any(x is None for x in p[c]): return None NSECS = SECONDS_IN_YEAR cov_cap = COV_RETURN_CAP / NSECS for s in self.vols.keys(): x = p[s] #print(x) dx = x[0] / x[1] - 1 #print(dx) dt = (t[0] - t[1]).total_seconds() v = min(dx**2 / dt, cov_cap) * NSECS v = w * v + (1 - w) * self.vols[s]**2 self.vols[s] = math.sqrt(v)
class MarketMaker(object): def __init__(self, monitor=True, output=True): self.equity_usd = None self.equity_btc = None self.equity_usd_init = None self.equity_btc_init = None self.con_size = float(CONTRACT_SIZE) self.client = None self.deltas = OrderedDict() self.futures = OrderedDict() self.futures_prv = OrderedDict() self.logger = None self.mean_looptime = 1 self.monitor = monitor self.output = output or monitor self.positions = OrderedDict() self.spread_data = None self.this_mtime = None self.ts = None self.vols = OrderedDict() def create_client(self): self.client = RestClient(KEY, SECRET, URL) def get_bbo(self, contract): # Get best b/o excluding own orders # Get orderbook ob = self.client.getorderbook(contract) bids = ob['bids'] asks = ob['asks'] ords = self.client.getopenorders(contract) bid_ords = [o for o in ords if o['direction'] == 'buy'] ask_ords = [o for o in ords if o['direction'] == 'sell'] best_bid = None best_ask = None err = 10**-(self.get_precision(contract) + 1) for b in bids: match_qty = sum([ o['quantity'] for o in bid_ords if math.fabs(b['price'] - o['price']) < err ]) if match_qty < b['quantity']: best_bid = b['price'] break for a in asks: match_qty = sum([ o['quantity'] for o in ask_ords if math.fabs(a['price'] - o['price']) < err ]) if match_qty < a['quantity']: best_ask = a['price'] break return {'bid': best_bid, 'ask': best_ask} def get_futures(self): # Get all current futures instruments self.futures_prv = cp.deepcopy(self.futures) insts = self.client.getinstruments() self.futures = sort_by_key( {i['instrumentName']: i for i in insts if i['kind'] == 'future'}) for k, v in self.futures.items(): self.futures[k]['expi_dt'] = datetime.strptime( v['expiration'][:-4], '%Y-%m-%d %H:%M:%S') def get_pct_delta(self): self.update_status() return sum(self.deltas.values()) / self.equity_btc def get_spot(self): return self.client.index()['btc'] def get_precision(self, contract): return self.futures[contract]['pricePrecision'] def get_ticksize(self, contract): return self.futures[contract]['tickSize'] def output_status(self): if not self.output: return None self.update_status() now = datetime.utcnow() days = (now - self.start_time).total_seconds() / SECONDS_IN_DAY print( '********************************************************************' ) print('Start Time: %s' % self.start_time.strftime('%Y-%m-%d %H:%M:%S')) print('Current Time: %s' % now.strftime('%Y-%m-%d %H:%M:%S')) print('Days: %s' % round(days, 1)) print('Hours: %s' % round(days * 24, 1)) print('Spot Price: %s' % self.get_spot()) pnl_usd = self.equity_usd - self.equity_usd_init pnl_btc = self.equity_btc - self.equity_btc_init print('Equity ($): %7.2f' % self.equity_usd) print('P&L ($) %7.2f' % pnl_usd) print('Equity (BTC): %7.4f' % self.equity_btc) print('P&L (BTC) %7.4f' % pnl_btc) print('%% Delta: %s%%' % round(self.get_pct_delta() / PCT, 1)) print('Total Delta (BTC): %s' % round(sum(self.deltas.values()), 2)) print_dict_of_dicts( {k: { 'BTC': self.deltas[k] } for k in self.deltas.keys()}, roundto=2, title='Deltas') print_dict_of_dicts( { k: { 'Contracts': self.positions[k]['size'] } for k in self.positions.keys() }, title='Positions') if not self.monitor: print_dict_of_dicts( {k: { '%': self.vols[k] } for k in self.vols.keys()}, multiple=100, title='Vols') print('\nMean Loop Time: %s' % round(self.mean_looptime, 2)) print('') def place_orders(self): if self.monitor: return None con_sz = self.con_size for fut in self.futures.keys(): account = self.client.account() spot = self.get_spot() bal_btc = account['equity'] pos = self.positions[fut]['sizeBtc'] pos_lim_long = bal_btc * PCT_LIM_LONG / len(self.futures) pos_lim_short = bal_btc * PCT_LIM_SHORT / len(self.futures) expi = self.futures[fut]['expi_dt'] tte = max(0, (expi - datetime.utcnow()).total_seconds() / SECONDS_IN_DAY) pos_decay = 1.0 - math.exp(-DECAY_POS_LIM * tte) pos_lim_long *= pos_decay pos_lim_short *= pos_decay pos_lim_long -= pos pos_lim_short += pos pos_lim_long = max(0, pos_lim_long) pos_lim_short = max(0, pos_lim_short) min_order_size_btc = MIN_ORDER_SIZE / spot * CONTRACT_SIZE qtybtc = max(PCT_QTY_BASE * bal_btc, min_order_size_btc) nbids = min(math.trunc(pos_lim_long / qtybtc), MAX_LAYERS) nasks = min(math.trunc(pos_lim_short / qtybtc), MAX_LAYERS) place_bids = nbids > 0 place_asks = nasks > 0 if not place_bids and not place_asks: print('No bid no offer for %s' % fut, pos_lim_long) continue tsz = self.get_ticksize(fut) # Perform pricing vol = max(self.vols[BTC_SYMBOL], self.vols[fut]) eps = BP * vol * RISK_CHARGE_VOL riskfac = math.exp(eps) bbo = self.get_bbo(fut) bid_mkt = bbo['bid'] ask_mkt = bbo['ask'] if bid_mkt is None and ask_mkt is None: bid_mkt = ask_mkt = spot elif bid_mkt is None: bid_mkt = min(spot, ask_mkt) elif ask_mkt is None: ask_mkt = max(spot, bid_mkt) mid_mkt = 0.5 * (bid_mkt + ask_mkt) ords = self.client.getopenorders(fut) cancel_oids = [] bid_ords = ask_ords = [] if place_bids: bid_ords = [o for o in ords if o['direction'] == 'buy'] len_bid_ords = min(len(bid_ords), nbids) bid0 = mid_mkt * math.exp(-MKT_IMPACT) bids = [bid0 * riskfac**-i for i in range(1, nbids + 1)] bids[0] = ticksize_floor(bids[0], tsz) if place_asks: ask_ords = [o for o in ords if o['direction'] == 'sell'] len_ask_ords = min(len(ask_ords), nasks) ask0 = mid_mkt * math.exp(MKT_IMPACT) asks = [ask0 * riskfac**i for i in range(1, nasks + 1)] asks[0] = ticksize_ceil(asks[0], tsz) for i in range(max(nbids, nasks)): # BIDS if place_bids and i < nbids: if i > 0: prc = ticksize_floor(min(bids[i], bids[i - 1] - tsz), tsz) else: prc = bids[0] qty = round(prc * qtybtc / con_sz) if i < len_bid_ords: oid = bid_ords[i]['orderId'] try: self.client.edit(oid, qty, prc) except (SystemExit, KeyboardInterrupt): raise except: try: self.client.buy(fut, qty, prc, 'true') cancel_oids.append(oid) self.logger.warn('Edit failed for %s' % oid) except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Bid order failed: %s bid for %s' % (prc, qty)) else: try: self.client.buy(fut, qty, prc, 'true') except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Bid order failed: %s bid for %s' % (prc, qty)) # OFFERS if place_asks and i < nasks: if i > 0: prc = ticksize_ceil(max(asks[i], asks[i - 1] + tsz), tsz) else: prc = asks[0] qty = round(prc * qtybtc / con_sz) if i < len_ask_ords: oid = ask_ords[i]['orderId'] try: self.client.edit(oid, qty, prc) except (SystemExit, KeyboardInterrupt): raise except: try: self.client.sell(fut, qty, prc, 'true') cancel_oids.append(oid) self.logger.warn('Sell Edit failed for %s' % oid) except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn( 'Offer order failed: %s at %s' % (qty, prc)) else: try: self.client.sell(fut, qty, prc, 'true') except (SystemExit, KeyboardInterrupt): raise except Exception as e: self.logger.warn('Offer order failed: %s at %s' % (qty, prc)) if nbids < len(bid_ords): cancel_oids += [o['orderId'] for o in bid_ords[nbids:]] if nasks < len(ask_ords): cancel_oids += [o['orderId'] for o in ask_ords[nasks:]] for oid in cancel_oids: try: self.client.cancel(oid) except: self.logger.warn('Order cancellations failed: %s' % oid) def restart(self): try: strMsg = 'RESTARTING' print(strMsg) self.client.cancelall() strMsg += ' ' for i in range(0, 5): strMsg += '.' print(strMsg) sleep(1) except: pass finally: os.execv(sys.executable, [sys.executable] + sys.argv) def run(self): self.run_first() self.output_status() t_ts = t_out = t_loop = t_mtime = datetime.utcnow() while True: self.get_futures() # Restart if a new contract is listed if len(self.futures) != len(self.futures_prv): self.restart() self.update_positions() t_now = datetime.utcnow() # Update time series and vols if (t_now - t_ts).total_seconds() >= WAVELEN_TS: t_ts = t_now self.update_timeseries() self.update_vols() self.place_orders() # Display status to terminal if self.output: t_now = datetime.utcnow() if (t_now - t_out).total_seconds() >= WAVELEN_OUT: self.output_status() t_out = t_now # Restart if file change detected t_now = datetime.utcnow() if (t_now - t_mtime).total_seconds() > WAVELEN_MTIME_CHK: t_mtime = t_now if getmtime(__file__) > self.this_mtime: self.restart() t_now = datetime.utcnow() looptime = (t_now - t_loop).total_seconds() # Estimate mean looptime w1 = EWMA_WGT_LOOPTIME w2 = 1.0 - w1 t1 = looptime t2 = self.mean_looptime self.mean_looptime = w1 * t1 + w2 * t2 t_loop = t_now sleep_time = MIN_LOOP_TIME - looptime if sleep_time > 0: time.sleep(sleep_time) if self.monitor: time.sleep(WAVELEN_OUT) def run_first(self): self.create_client() self.client.cancelall() self.logger = get_logger('root', LOG_LEVEL) # Get all futures contracts self.get_futures() self.this_mtime = getmtime(__file__) self.symbols = [BTC_SYMBOL] + list(self.futures.keys()) self.symbols.sort() self.deltas = OrderedDict({s: None for s in self.symbols}) # Create historical time series data for estimating vol ts_keys = self.symbols + ['timestamp'] ts_keys.sort() self.ts = [ OrderedDict({f: None for f in ts_keys}) for i in range(NLAGS + 1) ] self.vols = OrderedDict({s: VOL_PRIOR for s in self.symbols}) self.start_time = datetime.utcnow() self.update_status() self.equity_usd_init = self.equity_usd self.equity_btc_init = self.equity_btc def update_status(self): account = self.client.account() spot = self.get_spot() self.equity_btc = account['equity'] self.equity_usd = self.equity_btc * spot self.update_positions() self.deltas = OrderedDict( {k: self.positions[k]['sizeBtc'] for k in self.futures.keys()}) self.deltas[BTC_SYMBOL] = account['equity'] def update_positions(self): self.positions = OrderedDict({ f: { 'size': 0, 'sizeBtc': 0, 'indexPrice': None, 'markPrice': None } for f in self.futures.keys() }) positions = self.client.positions() for pos in positions: if pos['instrument'] in self.futures: self.positions[pos['instrument']] = pos def update_timeseries(self): if self.monitor: return None for t in range(NLAGS, 0, -1): self.ts[t] = cp.deepcopy(self.ts[t - 1]) spot = self.get_spot() self.ts[0][BTC_SYMBOL] = spot for c in self.futures.keys(): bbo = self.get_bbo(c) bid = bbo['bid'] ask = bbo['ask'] if not bid is None and not ask is None: mid = 0.5 * (bbo['bid'] + bbo['ask']) else: continue self.ts[0][c] = mid self.ts[0]['timestamp'] = datetime.utcnow() def update_vols(self): if self.monitor: return None w = EWMA_WGT_COV ts = self.ts t = [ts[i]['timestamp'] for i in range(NLAGS + 1)] p = {c: None for c in self.vols.keys()} for c in ts[0].keys(): p[c] = [ts[i][c] for i in range(NLAGS + 1)] if any(x is None for x in t): return None for c in self.vols.keys(): if any(x is None for x in p[c]): return None NSECS = SECONDS_IN_YEAR cov_cap = COV_RETURN_CAP / NSECS for s in self.vols.keys(): x = p[s] dx = x[0] / x[1] - 1 dt = (t[0] - t[1]).total_seconds() v = min(dx**2 / dt, cov_cap) * NSECS v = w * v + (1 - w) * self.vols[s]**2 self.vols[s] = math.sqrt(v)