def test_hedge(): enter_price = Decimal('989') target_price = Decimal('-1012') prior_pos = Position(pos=Decimal('0.07'), price=Decimal('1020'), side=Side.BID) #hedge over other side hedge_pos = hedge_position(prior_pos, target_price, enter_price) print("---------------- hedge over other side") print("prior " + str(prior_pos)) print("hedge " + str(hedge_pos)) print("fin " + str(prior_pos + hedge_pos)) #hedge over same side hedge_pos = hedge_position(prior_pos, target_price, Decimal('-1011')) print("---------------- hedge over same side") print("prior " + str(prior_pos)) print("hedge " + str(hedge_pos)) print("fin " + str(prior_pos + hedge_pos)) #from reality prior_pos = Position(pos=Decimal('0.07'), price=Decimal('1131.2700'), side=Side.ASK) hedge_pos = hedge_position(prior_pos, Decimal('-1159.2805'), Decimal('1161.4102')) print("---------------- real situation") print("prior " + str(prior_pos)) print("hedge " + str(hedge_pos)) print("fin " + str(prior_pos + hedge_pos)) #test_hedge()
def exit_price_test_func(book, pandl, broker): config = MMParams({ "min_levels": "5", "liq_behind_exit": "0.02", "liq_behind_entry": {"BID": "0.41", "ASK": "0.41"}, "order_sizes": {"BID": "0.07", "ASK": "0.07"}, "min_profit": "0.01", "min_order_size": "0.01", "buried_volume": "1", "taker_exit_profit": "0.1", "price_tolerance": "0.0005" }) start_position = Position(pandl.position(), pandl.balance()) print("before " + str(start_position)) exit_order = remove_exit_price_strategy(book, start_position, config) pandl.execution(exit_order.side(), exit_order.abs_position(), exit_order.price()) broker.request(0, exit_order.side(), exit_order.price(), exit_order.abs_position()) keys = broker.om.by_oid.keys() tmp = '' for x in keys: tmp = x broker.om.on_ack(Ack(tmp, 1, start_position.abs_position(), start_position.abs_position())) print_book_and_orders(book, broker) print("after " + str(exit_order)) assert pandl.closed_pnl > 0 return exit_order
def execution(self, tx: Exec): exec_pos = Position(pos=tx.delta, price=tx.price, side=tx.side) if tx.delta > 0: self.pos += exec_pos + exec_pos.fee_pos(tx.fee) if self.pos.position() == 0: self.closed_pnl += self.pos.balance self.pos = Position(0, 0) self.zero_position_time = time.time()
def __init__(self, fee): self.method = BipolarContainer('NONE', 'NONE') self.pos = Position(0, 0) self.nbbo = BipolarContainer(0, 0) self.closed_pnl = Decimal('0') self.clean_closed_pnl = Decimal('0') self.exit_price = Decimal('0') self.fee = Decimal(fee) self.ema = EMAHolder(5 * 60) self.zero_position_time = time.time()
def test_pos(): bid = Position(pos=Decimal('1'), price=100, side=Side.ASK) + Position(pos=Decimal('0.1'), price=101, side=Side.BID) assert bid.position() == Decimal('-0.9') assert bid.side() == Side.ASK bid = Position(pos=Decimal('1'), price=100, side=Side.BID) + Position(pos=Decimal('0.1'), price=101, side=Side.ASK) assert bid.position() == Decimal('0.9') assert bid.side() == Side.BID print(bid)
def take_pnl(self): if self.pos.position() == 0: return Decimal('0') exit_side = Side.opposite_side(self.position()) take_price = self.nbbo.side(Side.side(self.position())) take_order = Position(pos=self.pos.abs_position(), price=take_price, side=exit_side) take_pos = self.pos + take_order + take_order.fee_pos(self.fee) return take_pos.balance
def test_hedge_order(): position = Position(side=Side.BID, price=Decimal('1500'), pos=Decimal('0.01')) hedge_pos = hedge_positon_size(position, Decimal('1500') * Decimal('1.01'), Decimal('0.01')) print(hedge_pos) print(position + hedge_pos)
def hedge_workflow(prior_pos: Position, target_price, enter_price): # define current state pandl.execution(prior_pos.side(), prior_pos.abs_position(), prior_pos.price()) exit_order = remove_exit_price_strategy(book, prior_pos, None) #place exit order broker.request(0, exit_order.side(), exit_order.price(), exit_order.abs_position()) # calc hedge order hedge_pos = hedge_position(prior_pos, target_price, enter_price) #place hedge order broker.request(1, hedge_pos.side(), hedge_pos.price(), hedge_pos.abs_position()) # exec hedge order pandl.execution(hedge_pos.side(), hedge_pos.abs_position(), hedge_pos.price()) # look at new exit exit_order = remove_exit_price_strategy( book, Position(pandl.pos(), pandl.balance()), None)
def test_exit_price_strategy(): # remove prior_pos = Position(pos=Decimal('0.07'), price=Decimal('900'), side=Side.BID) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('989') assert eo.side() == Side.ASK pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 #remove prior_pos = Position(pos=Decimal('0.07'), price=Decimal('900'), side=Side.BID) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('989') assert eo.side() == Side.ASK pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 prior_pos = Position(pos=Decimal('0.07'), price=Decimal('1100'), side=Side.ASK) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('1011') assert eo.side() == Side.BID pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 #quote prior_pos = Position(pos=Decimal('0.07'), price=Decimal('1010'), side=Side.BID) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('1011') assert eo.side() == Side.ASK pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 prior_pos = Position(pos=Decimal('0.07'), price=Decimal('990'), side=Side.ASK) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('989') assert eo.side() == Side.BID pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 #min profit prior_pos = Position(pos=Decimal('0.07'), price=Decimal('1013'), side=Side.BID) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('1013.1427') assert eo.side() == Side.ASK pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0 prior_pos = Position(pos=Decimal('0.07'), price=Decimal('900'), side=Side.ASK) eo = simulator_env(exit_price_test_func, prior_pos) assert eo.price() == Decimal('899.8572') assert eo.side() == Side.BID pos = prior_pos + eo print("reminder " + str(pos)) assert pos.balance > 0
def order_event(self, ev): try: self.order_manager.market_event(ev) if type(ev) == ErrorRequest: self.event_hub.order_error(str(ev)) if ev.error_class != ErrorRequest.ORDER_NOT_FOUND: self.rm.set_cancel_all() elif type(ev) == Exec and ev.delta > 0: if self.pnl.exit_method() == "REMOVE": ev.fee = self.pnl.fee self.pnl.execution(ev) exec_time = time.strftime("%H:%M:%S", time.localtime()) order_fee = Position(pos=ev.delta, price=ev.price, side=ev.side).fee_pos(ev.fee).balance self.execution_sink.append({ "time": exec_time, 'order_id': ev.order_id, 'side': ev.side, 'price': str(ev.price), 'size': str(ev.delta), 'timestamp': int(1000 * time.time()), 'method': str(self.pnl.exit_method()), 'P&L': str(self.pnl.closed_pnl), 'fee': str(order_fee) }) self.on_exec(ev) except UnknownOid: self.event_hub.order_error('Unknown oid ' + ev.oid) self.rm.set_cancel_all() except UnknownOrderId: self.event_hub.order_error('Unknown order id ' + ev.order_id) self.rm.set_cancel_all() except ExecHasNoEffect: pass except NegativeAmountAfterExec: self.event_hub.order_error('NegativeAmountAfterExec') self.rm.set_cancel_all() except UnknownExec: self.event_hub.order_error('UnknownExec order_id: ' + str(ev)) self.rm.set_cancel_all()
def test_eneter_hedge(pos: Position): print("pos" + str(pos)) book = gen_book() pnl = PNL(Decimal('0.16')) pnl.quote_changed(book.quote(Side.BID)) pnl.quote_changed(book.quote(Side.ASK)) pnl.pos = pos config: AppConfig = load_config('config.json') hedge = enter_hedge(pnl, book, pos.side(), config.algo, config.venue) print("hedge " + str(hedge[0]) + " method " + hedge[1]) print("after hedge " + str(hedge[0] + pos)) #print("after hedge " + str(hedge[0]+pos)) print('-----------') pass
def simulator_env(func, pos=Position(pos=0, balance=0), book_median=1000): pandl = PNL('0.3') book = Book() book.quote_subscribers.append(pandl) broker = Broker(OrderManager()) pandl.execution(pos.side(), abs(pos.position()), abs(pos.price())) for i in range(0, 6): book.increment_level(Side.ASK, Decimal(book_median + 10 + randrange()), Decimal(i / 100)) book.increment_level(Side.BID, Decimal(book_median - 10 - i), Decimal(i / 100)) book.quote_changed(Side.BID) book.quote_changed(Side.ASK) return func(book, pandl, broker)
def __init__(self, algo_class, config: AppConfig): self.config = config self.event_log = ClientEventHandler() self.order_manager = OrderManager() self.book = Book() self.pnl = PNL(config.venue.taker_comission_percent) self.pnl.pos = Position(pos=config.venue.start_pos, balance=config.venue.start_balance) self.book.quote_subscribers.append(self.pnl) self.execution = Broker(self.order_manager) self.algo = algo_class(self) self.execution_sink = [] self.snapid = -1 self.event_hub = EventHub() self.event_hub.subscribe(self.book) self.event_hub.subscribe(self.algo) self.event_hub.subscribe(self.event_log) self.event_hub.subscribe(self.order_manager) self.rm = RiskManager(self.execution, self.event_hub)
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 remove_exit_price_strategy(book: Book, pos: Position, config: MarketmakerConfig, fee=Decimal(0.3)): # pos, last_price = remove_price(book.quote(pos.side()), pos) # if pos.balance > 0: # remove_pos = pos.oppoiste_with_price(last_price) # print("fee " + str(pos * Decimal('0.3'))) remove_pos = pos.oppoiste_with_price(book.quote(pos.side()).price) remove_pos_wfee = Position(pos=remove_pos.position(), balance=remove_pos.balance + (remove_pos.balance / 100) * fee) add_pos = pos.oppoiste_with_price( book.quote(Side.opposite(pos.side())).price) fee_ = remove_pos * Decimal(fee / 100) fin_pos = pos + remove_pos if (pos + remove_pos_wfee).balance > 0: return remove_pos elif (pos + add_pos).balance > 0: return add_pos else: return pos.opposite_with_margin(config.min_profit)
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() ])
from posmath.position import Position, BID, ASK print(Position(pos=10, balance=-100)) print(BID(pos=10, balance=100)) print(Position(pos=-10, balance=100)) print(ASK(pos=10, balance=100)) print(BID(pos=10, balance=99) + ASK(pos=10, balance=100))
def nbbo_pnl(self): exit_side = Side.opposite_side(self.position()) nbbo_price = self.nbbo.side(exit_side) return (self.pos + Position(pos=self.pos.abs_position(), price=nbbo_price, side=exit_side)).balance
class PNL: def __init__(self, fee): self.method = BipolarContainer('NONE', 'NONE') self.pos = Position(0, 0) self.nbbo = BipolarContainer(0, 0) self.closed_pnl = Decimal('0') self.clean_closed_pnl = Decimal('0') self.exit_price = Decimal('0') self.fee = Decimal(fee) self.ema = EMAHolder(5 * 60) self.zero_position_time = time.time() def execution(self, tx: Exec): exec_pos = Position(pos=tx.delta, price=tx.price, side=tx.side) if tx.delta > 0: self.pos += exec_pos + exec_pos.fee_pos(tx.fee) if self.pos.position() == 0: self.closed_pnl += self.pos.balance self.pos = Position(0, 0) self.zero_position_time = time.time() # self.clean_closed_pnl += self.closed_pnl # self.closed_pnl = 0 def position(self): return self.pos.position() def abs_position(self): return self.pos.abs_position() def quote_changed(self, quote): self.nbbo.set_side(quote.side, quote.price) if self.nbbo.bid() == 0 or self.nbbo.ask() == 0: return mid_price = (self.nbbo.bid() + self.nbbo.ask()) / 2 self.ema.add(mid_price) def balance(self): return self.pos.balance def open_pnl(self): return self.balance() + self.position() * self.exit_price def update_open_pnl(self, exit_price): self.exit_price = exit_price def nbbo_pnl(self): exit_side = Side.opposite_side(self.position()) nbbo_price = self.nbbo.side(exit_side) return (self.pos + Position(pos=self.pos.abs_position(), price=nbbo_price, side=exit_side)).balance def take_pnl(self): if self.pos.position() == 0: return Decimal('0') exit_side = Side.opposite_side(self.position()) take_price = self.nbbo.side(Side.side(self.position())) take_order = Position(pos=self.pos.abs_position(), price=take_price, side=exit_side) take_pos = self.pos + take_order + take_order.fee_pos(self.fee) return take_pos.balance def position_zero_price(self): if self.abs_position() == 0: return Decimal('0') else: return abs(self.balance() / self.abs_position()) def position_side(self): return Side.side(self.position()) def set_order_method(self, side, method): self.method.set_side(side, method) def exit_method(self): return str(self.method)
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
def hedge_positon_size(prior_pos, target_price, hedge_size): x = (target_price * (prior_pos.abs_position() + hedge_size) - prior_pos.price() * prior_pos.abs_position()) / hedge_size return Position(price=x, pos=hedge_size, side=prior_pos.side())
def bound_pos_to_lower_quote(quote: Level, pos: Position, tick_size): lower_quote_price = bound_price_to_lower_quote(quote, pos.price(), tick_size) return Position(side=pos.side(), price=lower_quote_price, pos=pos.abs_position())
print("pos" + str(pos)) book = gen_book() pnl = PNL(Decimal('0.16')) pnl.quote_changed(book.quote(Side.BID)) pnl.quote_changed(book.quote(Side.ASK)) pnl.pos = pos config: AppConfig = load_config('config.json') hedge = enter_hedge(pnl, book, pos.side(), config.algo, config.venue) print("hedge " + str(hedge[0]) + " method " + hedge[1]) print("after hedge " + str(hedge[0] + pos)) #print("after hedge " + str(hedge[0]+pos)) print('-----------') pass test_stick_to_quote(Position(pos='0.01', price='1300', side=Side.BID)) test_stick_to_quote(Position(pos='0.01', price='1297', side=Side.BID)) test_stick_to_quote(Position(pos='0.01', price='1300', side=Side.ASK)) test_stick_to_quote(Position(pos='0.01', price='1302', side=Side.ASK)) test_target_price(Position(pos='0.01', price='1300', side=Side.BID)) # good test_target_price(Position(pos='0.01', price='1300', side=Side.BID)) # good test_target_price(Position(pos='0.01', price='1500', side=Side.BID)) # perfect test_target_price(Position(pos='0.01', price='1300', side=Side.ASK)) test_target_price(Position(pos='0.01', price='1500', side=Side.ASK)) test_target_price(Position(pos='0.01', price='1000', side=Side.ASK)) # bought for 1300 # risk - price goes down
def hedge_position(prior_pos, target_price, enter_price): x = (target_price * prior_pos.position() - prior_pos.balance) / (enter_price - target_price) return Position(price=enter_price, pos=x)
def test_fee(): def test_case(pos, order): fee_pos = order.fee_pos('0.18') print("pos " + str(pos)) print("order " + str(order)) print("fee " + str(fee_pos)) wo_fee = pos + order w_fee = pos + order + fee_pos print("wo_fee " + str(wo_fee)) print("w_fee " + str(w_fee)) assert wo_fee.balance >= w_fee.balance, "pos " + str( pos) + " order" + str(order) assert (abs(fee_pos.balance) < abs(order.balance)) or (order.balance ==0 and fee_pos.balance==0)\ , "fee " + fee_pos.balance+" order cost " + order.balance pos = Position(pos=Decimal('1'), balance=Decimal('1199'), side=Side.BID) order = Position(pos=Decimal('1'), balance=Decimal('1200'), side=Side.ASK) test_case(order, pos) print('---------') test_case(pos, order) print('---------') pos = Position(pos=Decimal('0.01'), balance=Decimal('1199'), side=Side.BID) order = Position(pos=Decimal('0.01'), balance=Decimal('1200'), side=Side.ASK) test_case(pos, order) print('---------') pos = Position(pos=Decimal('0.01'), balance=Decimal('12'), side=Side.BID) order = Position(pos=Decimal('0.01'), price=Decimal('1201'), side=Side.ASK) test_case(order, pos) print('---------') test_case(pos, order) print('---------') pos = Position(pos=Decimal('0'), balance=Decimal('0'), side=Side.BID) order = Position(pos=Decimal('0.01'), price=Decimal('1201'), side=Side.ASK) print("order fee " + str(order.fee_pos(Decimal('0.18')).balance)) test_case(pos, order) print('---------') test_case(order, pos) assert pos + order == pos + order + order.fee_pos(0)