def test_process_order_fill_event_buy(self): amount = Decimal("1") price = Decimal("9000") event = OrderFilledEvent( timestamp=1, order_id="order1", trading_pair=self.trading_pair, trade_type=TradeType.BUY, order_type=OrderType.LIMIT, price=price, amount=amount, trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]), ) # first event creates DB record self.delegate.process_order_fill_event(event) with self.trade_fill_sql.get_new_session() as session: count = session.query(InventoryCost).count() self.assertEqual(count, 1) # second event causes update to existing record self.delegate.process_order_fill_event(event) record = InventoryCost.get_record(session, self.base_asset, self.quote_asset) self.assertEqual(record.base_volume, amount * 2) self.assertEqual(record.quote_volume, price * 2)
def get_price(self) -> Optional[Decimal]: record = InventoryCost.get_record(self._session, self.base_asset, self.quote_asset) if record is None or record.base_volume is None or record.base_volume is None: return None try: price = record.quote_volume / record.base_volume except InvalidOperation: # decimal.InvalidOperation: [<class 'decimal.DivisionUndefined'>] - both volumes are 0 return None return Decimal(price)
def get_price(self) -> Optional[Decimal]: record = InventoryCost.get_record(self._session, self.base_asset, self.quote_asset) if record is None or record.base_volume is None or record.base_volume is None: return None try: price = record.quote_volume / record.base_volume except InvalidOperation: # use both volume = 0 return None return Decimal(price)
def get_price(self) -> Optional[Decimal]: with self.sql_manager.get_new_session() as session: with session.begin(): record = InventoryCost.get_record(session, self.base_asset, self.quote_asset) if record is None or record.base_volume is None or record.base_volume is None: return None try: price = record.quote_volume / record.base_volume except InvalidOperation: return None return Decimal(price)
def process_order_fill_event(self, fill_event: OrderFilledEvent) -> None: base_asset, quote_asset = fill_event.trading_pair.split("-") quote_volume = fill_event.amount * fill_event.price base_volume = fill_event.amount for fee_asset, fee_amount in fill_event.trade_fee.flat_fees: if fill_event.trade_type == TradeType.BUY: if fee_asset == base_asset: base_volume -= fee_amount elif fee_asset == quote_asset: quote_volume += fee_amount else: # Ok, some other asset used (like BNB), assume that we paid in base asset for simplicity base_volume /= 1 + fill_event.trade_fee.percent else: if fee_asset == base_asset: base_volume += fee_amount elif fee_asset == quote_asset: # TODO: with new logic, this quote volume adjustment does not impacts anything quote_volume -= fee_amount else: # Ok, some other asset used (like BNB), assume that we paid in base asset for simplicity base_volume /= 1 + fill_event.trade_fee.percent with self.sql_manager.get_new_session() as session: with session.begin(): if fill_event.trade_type == TradeType.SELL: record = InventoryCost.get_record(session, base_asset, quote_asset) if not record: raise RuntimeError( "Sold asset without having inventory price set. This should not happen." ) # We're keeping initial buy price intact. Profits are not changing inventory price intentionally. quote_volume = -(Decimal(record.quote_volume / record.base_volume) * base_volume) base_volume = -base_volume InventoryCost.add_volume(session, base_asset, quote_asset, base_volume, quote_volume)
def test_process_order_fill_event_sell(self): amount = Decimal("1") price = Decimal("9000") # Test when no records self.assertIsNone(self.delegate.get_price()) with self.trade_fill_sql.get_new_session() as session: with session.begin(): record = InventoryCost( base_asset=self.base_asset, quote_asset=self.quote_asset, base_volume=amount, quote_volume=amount * price, ) session.add(record) amount_sell = Decimal("0.5") price_sell = Decimal("10000") event = OrderFilledEvent( timestamp=1, order_id="order1", trading_pair=self.trading_pair, trade_type=TradeType.SELL, order_type=OrderType.LIMIT, price=price_sell, amount=amount_sell, trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]), ) self.delegate.process_order_fill_event(event) with self.trade_fill_sql.get_new_session() as session: record = InventoryCost.get_record(session, self.base_asset, self.quote_asset) # Remaining base volume reduced by sold amount self.assertEqual(record.base_volume, amount - amount_sell) # Remaining quote volume has been reduced using original price self.assertEqual(record.quote_volume, amount_sell * price)
def process_order_fill_event(self, fill_event: OrderFilledEvent) -> None: base_asset, quote_asset = fill_event.trading_pair.split("-") quote_volume = fill_event.amount * fill_event.price base_volume = fill_event.amount for fee_asset, fee_amount in fill_event.trade_fee.flat_fees: if fill_event.trade_type == TradeType.BUY: if fee_asset == base_asset: base_volume -= fee_amount elif fee_asset == quote_asset: quote_volume += fee_amount else: # used for exchange native token used as base fee . base_volume /= 1 + fill_event.trade_fee.percent else: if fee_asset == base_asset: base_volume += fee_amount elif fee_asset == quote_asset: # volume adjustment with new logic. quote_volume -= fee_amount else: base_volume /= 1 + fill_event.trade_fee.percent if fill_event.trade_type == TradeType.SELL: record = InventoryCost.get_record(self._session, base_asset, quote_asset) if not record: raise RuntimeError( "Sold asset without having inventory price set. This should not happen." ) # keeping initial buy price intact. Profits are not changing inventory price intentionally. quote_volume = -(Decimal(record.quote_volume / record.base_volume) * base_volume) base_volume = -base_volume InventoryCost.add_volume(self._session, base_asset, quote_asset, base_volume, quote_volume)