def specific_margin_price(entry_price, entry_side, margin, entry_commisiion=0, exit_commision=0): return entry_price \ + Side.sign(entry_side) * (margin + entry_commisiion) \ - Side.sign(entry_side) * exit_commision
def enter_hedge(pnl: PNL, book: Book, side, cfg: HedgeConfig, vc: VenueConfig): quote = book.quote(side) side = quote.side pos = pnl.pos order_size = cfg.order_size.side(side) theo = (book.quote(Side.BID).price + book.quote(Side.ASK).price) / 2 if pos.abs_position() < vc.min_order_size: # depth or ema order_size = adjusted_size(order_size, side, pos.abs_position()) method, price = depth_ema_price(cfg, order_size, pnl, quote, side, vc) return Position(pos=order_size, side=side, price=price), method elif Side.opposite( pos.side()) == side and pos.abs_position() >= vc.min_order_size: # exit order # depth or zero method, price = depth_ema_price(cfg, order_size, pnl, quote, side, vc) add_pos = pos.oppoiste_with_price(price) min_margin = pos.opposite_with_margin(vc.tick_size) min_margin = bound_pos_to_lower_quote(quote, min_margin, vc.tick_size) if (pos + add_pos).balance > 0: return add_pos, "QUOTE" else: return min_margin, "MIN PROFIT" elif pos.side( ) == side and cfg.max_pos - pos.abs_position() >= vc.min_order_size: #depth #hedge #order_size = adjusted_size(order_size, side, pos.abs_position()) method, price_depth = depth_ema_price(cfg, order_size, pnl, quote, side, vc) order_size = min(order_size, cfg.max_pos - pos.abs_position()) depth_pos = Position(pos=order_size, side=side, price=price_depth) sign = Side.sign(pos.side()) target_price = theo - Side.sign(pos.side()) * theo * cfg.hedge_perc hedge_pos = hedge_positon_size(pos, Decimal(target_price), order_size) hedge_pos = bound_pos_to_lower_quote(quote, hedge_pos, vc.tick_size) #return hedge_pos, "HEDGE HEDGE" if (side == Side.BID and hedge_pos.price() < depth_pos.price()) \ or (side == Side.ASK and hedge_pos.price() > depth_pos.price()): return hedge_pos, "HEDGE HEDGE" else: return depth_pos, "HEDGE DEPTH" else: #zero return Position(side=side, pos=0, balance=0), "CANCEL"
def __init__(self, pos=None, balance=None, price=None, side=None): assert pos is None or type(pos) in (str, Decimal, int) assert balance is None or type(balance) in (str, Decimal, int) assert price is None or type(price) in (str, Decimal, int) assert side is None or side in Side.sides if pos: pos = Decimal(pos) if balance: balance = Decimal(balance) if price: price = Decimal(price) if balance is None and price is None: raise RuntimeError if balance and price and pos: raise RuntimeError if balance is not None and pos is not None: self.pos = pos self.balance = balance elif pos is not None and price is not None: self.pos = pos self.balance = pos * price elif price is not None and balance is not None: self.pos = balance / price self.balance = balance if side is not None: self.pos = abs(self.pos) * Side.sign(side) self.balance = Side.opposite_sign(side) * abs(self.balance)
def before(self, level): if level.side != self.side: raise RuntimeError if self.price == level.price: raise RuntimeError return (self.price - level.price) / abs(self.price - level.price) == Side.sign(self.side)
def price(self): if self.pos == 0: return Decimal('0') prec = Decimal('10000') price, reminder = divmod(prec * abs(self.balance), abs(self.pos)) price /= prec if reminder != 0: price += Side.sign(self.side()) * Decimal('0.0001') return round(price, 4)
def gen_prices(side): sign = -Side.sign(side) prev_price = Decimal(median + sign * spread / 2) for i in range(0, levels): next_price = prev_price + Decimal( sign * randrange(1, 1000, 1) / 1000) final_next_price = round(Decimal(next_price), 4) size = round(Decimal(randrange(1, 100, 1) / 100), 2) yield final_next_price, size prev_price = final_next_price
def calc_price_between_levels(quote, liq_behind, min_step, place_to_spread=False): quote_liq = quote.size dt = abs(0 - quote.price) if place_to_spread else 0 while quote_liq < liq_behind: dt = abs(quote.price - quote.next_level.price) quote = quote.next_level quote_liq += quote.size if dt > min_step: return quote.price + Side.sign(quote.side) * min_step else: return quote.price
def enter_ema(quote: Level, ema: Decimal, ema_work_perc, vc: VenueConfig): sign = Decimal(Side.sign(quote.side)) def calc_ema_price(): return round(Decimal(ema - sign * (ema / 100 * ema_work_perc)), 4) def stick_to_quote(price): try: under_price = next( (x.price for x in quote if sign * x.price - sign * price <= 0)) return under_price + sign * vc.tick_size if under_price != price else under_price except StopIteration: return price return stick_to_quote(calc_ema_price())
def price_on_a_depth(top_quote, size, ac: MarketmakerConfig, vc: VenueConfig): quote_array = [] quote_liq = Decimal('0') side = top_quote.side liq_adj = ac.liq_behind.side(side) - size prev_price = Decimal('0') for quote in top_quote: quote_liq += quote.size quote_array.append((quote.price, quote.size, quote_liq, abs(prev_price - quote.price))) prev_price = quote.price if quote_liq >= liq_adj: break last_quote = quote_array[-1] if last_quote[2] > liq_adj: return last_quote[0] + Side.sign(side) * vc.tick_size else: return last_quote[0]
def ema_constraint(depth_price, ema_price, side): sign = Side.sign(side) # ema 100 price 101 side bid delta = sign * ema_price - sign * depth_price return (ema_price, "EMA") if delta < 0 else (depth_price, "ENTER")
def volume_behind_order(min_pos: Position): sign = Side.sign(min_pos.side()) return sum([ level.volume() for level in book.quote(min_pos.side()) if sign * level.price > sign * min_pos.price() ])
def price_not_better_than(calc_price, ema_price, side): sign = (calc_price - ema_price) / abs(calc_price - ema_price) if sign == Side.sign(side): return ema_price else: return calc_price
def epsilon_from_theo(theo, perc, side): return theo - Side.sign(side) * theo * perc
def calc_target_price(theo: Decimal, pos: Position, hedge_perc: Decimal): #exit_side = Side.opposite(pos.side()) sign = Side.sign(pos.side()) theo_target = theo - Side.sign(pos.side()) * theo * hedge_perc pos_target = pos.price() - sign * theo * hedge_perc return theo_target