def create_order_schedule(self,lob=None,spread=5,delta=1,time=0): qty_density=self.qty_density depth=self.depth try: best_bid=lob['bids']['best'] except KeyError: best_bid=1+depth try: best_ask=lob['asks']['best'] except KeyError: best_ask=200-depth if self.inventory!=0: if self.inventory<0: order_type='Bid' spread_mult=-1 elif self.inventory>0: order_type='Ask' spread_mult=1 df=pd.DataFrame(list(self.trade_manager.fifo)).groupby('price').count() for row in df.iterrows(): price=row[0] quantity=row[1][0] order=Order(self.tid,order_type,price+spread_mult*spread,quantity,time,oid=self.make_oid(time)) self.add_order(order,verbose=False) # if abs(self.inventory)<self.depth: # #need to supplement bids or offers to get to requisite quote depth # too_add=self.depth-abs(self.inventory) # while too_add>0: # price=price+spread_mult # quantity=1 # order=Order(self.tid,order_type,price,quantity,time,oid=self.make_oid(time)) # too_add-=1 if self.inventory<0: order_type='Ask' best_ask=max(best_ask,max(df.index.values)) for price in range(best_ask,best_ask+depth): order=Order(self.tid,'Ask',price,qty_density,time,oid=self.make_oid(time)) self.add_order(order,verbose=False) else: #work out the bids order_type='Bid' best_bid=min(best_bid,min(df.index.values)) for price in range(best_bid-depth,best_bid): order=Order(self.tid,'Bid',price,qty_density,time,oid=self.make_oid(time)) self.add_order(order,verbose=False) else: #no inventory, issue bids and offers around best bid and ask for price in range(best_ask,best_ask+depth): order=Order(self.tid,'Ask',price,qty_density,time,oid=self.make_oid(time)) self.add_order(order,verbose=False) #work out the bids for price in range(best_bid-depth,best_bid): order=Order(self.tid,'Bid',price,qty_density,time,oid=self.make_oid(time)) self.add_order(order,verbose=False) return self.orders_dic.copy() #to stop subsequent mutation
def get_order(self, tape=None): inventory = self.inventory buy_order = None sell_order = None rmean = self.estimates[self.time] bid_possible = inventory < self.qty_max ask_possible = inventory > self.qty_min buy_surplus = 0 sell_surplus = 0 if bid_possible: buy_pref = self.preference[inventory + 1] buy_valuation = rmean + buy_pref buy_price = np.random.randint(rmean - self.rmax + buy_pref, rmean - self.rmin + buy_pref) buy_price = self.price_or_best_price( buy_price, 'Bid') #choose price no better than best buy_surplus = buy_valuation - buy_price if ask_possible: sell_pref = self.preference[inventory] sell_valuation = rmean + sell_pref sell_price = np.random.randint(rmean + self.rmin + sell_pref, rmean + self.rmax + sell_pref) sell_price = self.price_or_best_price( sell_price, 'Ask') #choose price no better than best sell_surplus = sell_price - sell_valuation if self.market_make: #market maker formulation - submit bid and ask where possible if bid_possible and buy_surplus > 0: buy_order = Order(tid=self.tid, otype='Bid', price=buy_price, qty=1, time=self.time, oid=self.get_oid()) #record internally self.add_order(buy_order) if ask_possible and sell_surplus > 0: sell_order = Order(tid=self.tid, otype='Ask', price=sell_price, qty=1, time=self.time, oid=self.get_oid()) #record internally self.add_order(sell_order) else: #single order formulation if bid_possible and buy_surplus > 0 and buy_surplus >= sell_surplus: buy_order = Order(tid=self.tid, otype='Bid', price=buy_price, qty=1, time=self.time, oid=self.get_oid()) #record internally self.add_order(buy_order) elif ask_possible and sell_surplus > 0: sell_order = Order(tid=self.tid, otype='Ask', price=sell_price, qty=1, time=self.time, oid=self.get_oid()) #record internally self.add_order(sell_order) return buy_order, sell_order
def customer_orders(time, last_update, traders, n_buyers, n_sellers, os, pending, verbose, quantity=None, oid=-1): #oid=-1 number we start at for unique oid codes. Will increase negatively (to quickly differentiate from qid) def sysmin_check(price): if price < bse_sys_minprice: print('WARNING: price < bse_sys_min -- clipped') price = bse_sys_minprice return price def sysmax_check(price): if price > bse_sys_maxprice: print('WARNING: price > bse_sys_max -- clipped') price = bse_sys_maxprice return price def getorderprice(i, sched, n, mode, issuetime): # does the first schedule range include optional dynamic offset function(s)? if len(sched[0]) > 2: offsetfn = sched[0][2] if callable(offsetfn): # same offset for min and max offset_min = offsetfn(issuetime) offset_max = offset_min else: sys.exit( 'FAIL: 3rd argument of sched in getorderprice() not callable' ) if len(sched[0]) > 3: # if second offset function is specfied, that applies only to the max value offsetfn = sched[0][3] if callable(offsetfn): # this function applies to max offset_max = offsetfn(issuetime) else: sys.exit( 'FAIL: 4th argument of sched in getorderprice() not callable' ) else: offset_min = 0.0 offset_max = 0.0 pmin = sysmin_check(offset_min + min(sched[0][0], sched[0][1])) pmax = sysmax_check(offset_max + max(sched[0][0], sched[0][1])) prange = pmax - pmin stepsize = prange / (n - 1) halfstep = round(stepsize / 2.0) if mode == 'fixed': orderprice = pmin + int(i * stepsize) elif mode == 'jittered': orderprice = pmin + int(i * stepsize) + random.randint( -halfstep, halfstep) elif mode == 'random': if len(sched) > 1: # more than one schedule: choose one equiprobably s = random.randint(0, len(sched) - 1) pmin = sysmin_check(min(sched[s][0], sched[s][1])) pmax = sysmax_check(max(sched[s][0], sched[s][1])) orderprice = random.randint(pmin, pmax) else: sys.exit('FAIL: Unknown mode in schedule') orderprice = sysmin_check(sysmax_check(orderprice)) return orderprice def getissuetimes(n_traders, mode, interval, shuffle, fittointerval): interval = float(interval) if n_traders < 1: sys.exit('FAIL: n_traders < 1 in getissuetime()') elif n_traders == 1: tstep = interval else: tstep = interval / (n_traders - 1) arrtime = 0 issuetimes = [] for t in range(n_traders): if mode == 'periodic': arrtime = interval elif mode == 'drip-fixed': arrtime = t * tstep elif mode == 'drip-jitter': arrtime = t * tstep + tstep * random.random() elif mode == 'drip-poisson': # poisson requires a bit of extra work interarrivaltime = random.expovariate(n_traders / interval) arrtime += interarrivaltime else: sys.exit('FAIL: unknown time-mode in getissuetimes()') issuetimes.append(arrtime) # at this point, arrtime is the last arrival time if fittointerval and ((arrtime > interval) or (arrtime < interval)): # generated sum of interarrival times longer than the interval # squish them back so that last arrival falls at t=interval for t in range(n_traders): issuetimes[t] = interval * (issuetimes[t] / arrtime) # optionally randomly shuffle the times if shuffle: for t in range(n_traders): i = (n_traders - 1) - t j = random.randint(0, i) tmp = issuetimes[i] issuetimes[i] = issuetimes[j] issuetimes[j] = tmp return issuetimes def getschedmode(time, os): got_one = False for sched in os: if (sched['from'] <= time) and (time < sched['to']): # within the timezone for this schedule schedrange = sched['ranges'] mode = sched['stepmode'] got_one = True break # jump out the loop -- so the first matching timezone has priority over any others if not got_one: sys.exit('Fail: time=%5.2f not within any timezone in os=%s' % (time, os)) return (schedrange, mode) #n_buyers = trader_stats['n_buyers'] #n_sellers = trader_stats['n_sellers'] shuffle_times = True cancellations = [] dispatched_orders = [] if len(pending) < 1: # list of pending (to-be-issued) customer orders is empty, so generate a new one new_pending = [] # demand side (buyers) issuetimes = getissuetimes(n_buyers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Bid' (sched, mode) = getschedmode(time, os['dem']) for t in range(n_buyers): issuetime = time + issuetimes[t] tname = 'B%02d' % t orderprice = getorderprice(t, sched, n_buyers, mode, issuetime) order = Order(tname, ordertype, orderprice, quantity(), issuetime, qid=None, oid=oid) oid -= 1 new_pending.append(order) # supply side (sellers) issuetimes = getissuetimes(n_sellers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Ask' (sched, mode) = getschedmode(time, os['sup']) for t in range(n_sellers): issuetime = time + issuetimes[t] tname = 'S%02d' % t orderprice = getorderprice(t, sched, n_sellers, mode, issuetime) order = Order(tname, ordertype, orderprice, quantity(), issuetime, qid=None, oid=oid) oid -= 1 new_pending.append(order) else: # there are pending future orders: issue any whose timestamp is in the past new_pending = [] for order in pending: if order.time < time: dispatched_orders.append(order) # this order should have been issued by now # issue it to the trader tname = order.tid response = traders[tname].add_order(order, verbose) if verbose: print('Customer order: %s %s' % (response[0], order)) if response[0] == 'LOB_Cancel': assert tname == response[1]['tid'] cancellations.append(response[1]) if verbose: print('Cancellations: %s' % (cancellations)) # and then don't add it to new_pending (i.e., delete it) else: # this order stays on the pending list new_pending.append(order) return [new_pending, cancellations, dispatched_orders, oid]