def test_adjust_candidate_insufficient_funds_for_flat_fees_and_percent_fees_third_token( self): fc_token = "PFC" trade_fee_schema = TradeFeeSchema( percent_fee_token=fc_token, maker_percent_fee_decimal=Decimal("0.01"), taker_percent_fee_decimal=Decimal("0.01"), maker_fixed_fees=[TokenAmount(fc_token, Decimal("1"))]) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) pfc_quote_pair = combine_to_hb_trading_pair(self.quote_asset, fc_token) exchange.set_balanced_order_book( # the quote to pfc price will be 1:2 trading_pair=pfc_quote_pair, mid_price=1.5, min_price=1, max_price=2, price_step_size=1, volume_step_size=1, ) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("20")) exchange.set_balance( fc_token, Decimal("1.2") ) # 0.2 less than required; will result in 50% order reduction order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("10"), adjusted_candidate.order_collateral.amount) self.assertEqual(fc_token, adjusted_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_collateral.amount) self.assertEqual(fc_token, adjusted_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_value.amount) self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals)) fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0] self.assertEqual(fc_token, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("5"), adjusted_candidate.potential_returns.amount)
def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder): quote_currency_traded: Decimal = limit_order.price * limit_order.quantity base_currency_traded: Decimal = limit_order.quantity quote_currency: str = limit_order.quote_currency base_currency: str = limit_order.base_currency if limit_order.is_buy: market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded) market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded) else: market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded) market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded) market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent( market.current_timestamp, limit_order.client_order_id, limit_order.trading_pair, TradeType.BUY if limit_order.is_buy else TradeType.SELL, OrderType.LIMIT, limit_order.price, limit_order.quantity, AddedToCostTradeFee(Decimal("0")) )) event_type = MarketEvent.BuyOrderCompleted if limit_order.is_buy else MarketEvent.SellOrderCompleted event_class = BuyOrderCompletedEvent if limit_order.is_buy else SellOrderCompletedEvent market.trigger_event(event_type, event_class( market.current_timestamp, limit_order.client_order_id, base_currency, quote_currency, base_currency_traded, quote_currency_traded, OrderType.LIMIT ))
def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder, timestamp: float = 0): quote_currency_traded: Decimal = limit_order.price * limit_order.quantity base_currency_traded: Decimal = limit_order.quantity quote_currency: str = limit_order.quote_currency base_currency: str = limit_order.base_currency trade_event: OrderBookTradeEvent = OrderBookTradeEvent( trading_pair=limit_order.trading_pair, timestamp=timestamp, type=TradeType.BUY if limit_order.is_buy else TradeType.SELL, price=limit_order.price, amount=limit_order.quantity ) market.get_order_book(limit_order.trading_pair).apply_trade(trade_event) if limit_order.is_buy: market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded) market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded) market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent( market.current_timestamp, limit_order.client_order_id, limit_order.trading_pair, TradeType.BUY, OrderType.LIMIT, limit_order.price, limit_order.quantity, AddedToCostTradeFee(Decimal(0.0)) )) market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent( market.current_timestamp, limit_order.client_order_id, base_currency, quote_currency, base_currency_traded, quote_currency_traded, OrderType.LIMIT )) else: market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded) market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded) market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent( market.current_timestamp, limit_order.client_order_id, limit_order.trading_pair, TradeType.SELL, OrderType.LIMIT, limit_order.price, limit_order.quantity, AddedToCostTradeFee(Decimal(0.0)) )) market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent( market.current_timestamp, limit_order.client_order_id, base_currency, quote_currency, base_currency_traded, quote_currency_traded, OrderType.LIMIT ))
def test_adjust_candidate_insufficient_funds_for_flat_fees_and_percent_fees( self): trade_fee_schema = TradeFeeSchema( maker_percent_fee_decimal=Decimal("0.1"), maker_fixed_fees=[TokenAmount(self.quote_asset, Decimal("1"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("12")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("10"), adjusted_candidate.order_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("1"), adjusted_candidate.percent_fee_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_value.token) self.assertEqual(Decimal("1"), adjusted_candidate.percent_fee_value.amount) self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals)) fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0] self.assertEqual(self.quote_asset, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("5"), adjusted_candidate.potential_returns.amount)
def test_adjust_candidate_insufficient_funds_for_flat_fees_third_token( self): fee_asset = "FEE" trade_fee_schema = TradeFeeSchema( maker_fixed_fees=[TokenAmount(fee_asset, Decimal("11"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("100")) exchange.set_balance(fee_asset, Decimal("10")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertTrue(adjusted_candidate.is_zero_order)
class BudgetCheckerTest(unittest.TestCase): def setUp(self) -> None: super().setUp() self.base_asset = "COINALPHA" self.quote_asset = "HBOT" self.trading_pair = f"{self.base_asset}-{self.quote_asset}" trade_fee_schema = TradeFeeSchema( maker_percent_fee_decimal=Decimal("0.01"), taker_percent_fee_decimal=Decimal("0.02")) self.exchange = MockPaperExchange( client_config_map=ClientConfigAdapter(ClientConfigMap()), trade_fee_schema=trade_fee_schema) self.budget_checker: BudgetChecker = self.exchange.budget_checker def test_populate_collateral_fields_buy_order(self): order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = self.budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("20"), populated_candidate.order_collateral.amount) self.assertEqual(self.quote_asset, populated_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.2"), populated_candidate.percent_fee_collateral.amount) self.assertEqual(self.quote_asset, populated_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.2"), populated_candidate.percent_fee_value.amount) self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("10"), populated_candidate.potential_returns.amount) def test_populate_collateral_fields_taker_buy_order(self): order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=False, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = self.budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("20"), populated_candidate.order_collateral.amount) self.assertEqual(self.quote_asset, populated_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.4"), populated_candidate.percent_fee_collateral.amount) self.assertEqual(self.quote_asset, populated_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.4"), populated_candidate.percent_fee_value.amount) self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("10"), populated_candidate.potential_returns.amount) def test_populate_collateral_fields_buy_order_percent_fee_from_returns( self): trade_fee_schema = TradeFeeSchema( maker_percent_fee_decimal=Decimal("0.01"), taker_percent_fee_decimal=Decimal("0.01"), buy_percent_fee_deducted_from_returns=True, ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("20"), populated_candidate.order_collateral.amount) self.assertIsNone(populated_candidate.percent_fee_collateral) self.assertEqual(self.base_asset, populated_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.1"), populated_candidate.percent_fee_value.amount) self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("9.90"), populated_candidate.potential_returns.amount) def test_populate_collateral_fields_sell_order(self): order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = self.budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.base_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("10"), populated_candidate.order_collateral.amount) self.assertIsNone(populated_candidate.percent_fee_collateral) self.assertEqual(self.quote_asset, populated_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.2"), populated_candidate.percent_fee_value.amount) self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals)) self.assertEqual(self.quote_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("19.8"), populated_candidate.potential_returns.amount) def test_populate_collateral_fields_percent_fees_in_third_token(self): pfc_token = "PFC" trade_fee_schema = TradeFeeSchema( percent_fee_token=pfc_token, maker_percent_fee_decimal=Decimal("0.01"), taker_percent_fee_decimal=Decimal("0.01"), ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) pfc_quote_pair = combine_to_hb_trading_pair(self.quote_asset, pfc_token) exchange.set_balanced_order_book( # the quote to pfc price will be 1:2 trading_pair=pfc_quote_pair, mid_price=1.5, min_price=1, max_price=2, price_step_size=1, volume_step_size=1, ) budget_checker: BudgetChecker = exchange.budget_checker order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("20"), populated_candidate.order_collateral.amount) self.assertEqual(pfc_token, populated_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.4"), populated_candidate.percent_fee_collateral.amount) self.assertEqual(pfc_token, populated_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.4"), populated_candidate.percent_fee_value.amount) self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("10"), populated_candidate.potential_returns.amount) def test_populate_collateral_fields_fixed_fees_in_quote_token(self): trade_fee_schema = TradeFeeSchema( maker_fixed_fees=[TokenAmount(self.quote_asset, Decimal("1"))], taker_fixed_fees=[TokenAmount(self.base_asset, Decimal("2"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) populated_candidate = budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("20"), populated_candidate.order_collateral.amount) self.assertIsNone(populated_candidate.percent_fee_collateral) self.assertIsNone(populated_candidate.percent_fee_value) self.assertEqual(1, len(populated_candidate.fixed_fee_collaterals)) fixed_fee_collateral = populated_candidate.fixed_fee_collaterals[0] self.assertEqual(self.quote_asset, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, populated_candidate.potential_returns.token) self.assertEqual(Decimal("10"), populated_candidate.potential_returns.amount) def test_adjust_candidate_sufficient_funds(self): self.exchange.set_balance(self.quote_asset, Decimal("100")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = self.budget_checker.adjust_candidate( order_candidate) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("20"), adjusted_candidate.order_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_value.amount) self.assertEqual(0, len(adjusted_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("10"), adjusted_candidate.potential_returns.amount) def test_adjust_candidate_insufficient_funds_all_or_none(self): self.exchange.set_balance(self.quote_asset, Decimal("10")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = self.budget_checker.adjust_candidate( order_candidate, all_or_none=True) self.assertEqual(0, adjusted_candidate.amount) self.assertIsNone(adjusted_candidate.order_collateral) self.assertIsNone(adjusted_candidate.percent_fee_collateral) self.assertIsNone(adjusted_candidate.percent_fee_value) self.assertEqual(0, len(adjusted_candidate.fixed_fee_collaterals)) self.assertIsNone(adjusted_candidate.potential_returns) def test_adjust_candidate_buy_insufficient_funds_partial_adjustment_allowed( self): q_params = QuantizationParams( trading_pair=self.trading_pair, price_precision=8, price_decimals=2, order_size_precision=8, order_size_decimals=2, ) self.exchange.set_quantization_param(q_params) self.exchange.set_balance(self.quote_asset, Decimal("10")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = self.budget_checker.adjust_candidate( order_candidate, all_or_none=False) # order amount quantized to two decimal places self.assertEqual(Decimal("4.95"), adjusted_candidate.amount) # 5 * .99 self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual( Decimal("9.9"), adjusted_candidate.order_collateral.amount) # 4.95 * 2 self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_collateral.token) self.assertEqual( Decimal("0.099"), adjusted_candidate.percent_fee_collateral.amount) # 9.9 * 0.01 self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_value.token) self.assertEqual( Decimal("0.099"), adjusted_candidate.percent_fee_value.amount) # 9.9 * 0.01 self.assertEqual(0, len(adjusted_candidate.fixed_fee_collaterals)) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("4.95"), adjusted_candidate.potential_returns.amount) def test_adjust_candidate_sell_insufficient_funds_partial_adjustment_allowed( self): self.exchange.set_balance(self.base_asset, Decimal("5")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = self.budget_checker.adjust_candidate( order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.base_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("5"), adjusted_candidate.order_collateral.amount) self.assertIsNone(adjusted_candidate.percent_fee_collateral) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_value.token) self.assertEqual( Decimal("0.1"), adjusted_candidate.percent_fee_value.amount) # 10 * 0.01 self.assertEqual(0, len(adjusted_candidate.fixed_fee_collaterals)) self.assertEqual(self.quote_asset, adjusted_candidate.potential_returns.token) self.assertEqual( Decimal("9.9"), adjusted_candidate.potential_returns.amount) # 10 * 0.99 def test_adjust_candidate_insufficient_funds_for_flat_fees_same_token( self): trade_fee_schema = TradeFeeSchema( maker_fixed_fees=[TokenAmount(self.quote_asset, Decimal("1"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("11")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("10"), adjusted_candidate.order_collateral.amount) self.assertIsNone(adjusted_candidate.percent_fee_collateral) self.assertIsNone(adjusted_candidate.percent_fee_value) self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals)) fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0] self.assertEqual(self.quote_asset, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("5"), adjusted_candidate.potential_returns.amount) def test_adjust_candidate_insufficient_funds_for_flat_fees_third_token( self): fee_asset = "FEE" trade_fee_schema = TradeFeeSchema( maker_fixed_fees=[TokenAmount(fee_asset, Decimal("11"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("100")) exchange.set_balance(fee_asset, Decimal("10")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertTrue(adjusted_candidate.is_zero_order) def test_adjust_candidate_insufficient_funds_for_flat_fees_and_percent_fees( self): trade_fee_schema = TradeFeeSchema( maker_percent_fee_decimal=Decimal("0.1"), maker_fixed_fees=[TokenAmount(self.quote_asset, Decimal("1"))], ) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("12")) order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("10"), adjusted_candidate.order_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("1"), adjusted_candidate.percent_fee_collateral.amount) self.assertEqual(self.quote_asset, adjusted_candidate.percent_fee_value.token) self.assertEqual(Decimal("1"), adjusted_candidate.percent_fee_value.amount) self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals)) fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0] self.assertEqual(self.quote_asset, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("5"), adjusted_candidate.potential_returns.amount) def test_adjust_candidate_insufficient_funds_for_flat_fees_and_percent_fees_third_token( self): fc_token = "PFC" trade_fee_schema = TradeFeeSchema( percent_fee_token=fc_token, maker_percent_fee_decimal=Decimal("0.01"), taker_percent_fee_decimal=Decimal("0.01"), maker_fixed_fees=[TokenAmount(fc_token, Decimal("1"))]) exchange = MockPaperExchange(client_config_map=ClientConfigAdapter( ClientConfigMap()), trade_fee_schema=trade_fee_schema) pfc_quote_pair = combine_to_hb_trading_pair(self.quote_asset, fc_token) exchange.set_balanced_order_book( # the quote to pfc price will be 1:2 trading_pair=pfc_quote_pair, mid_price=1.5, min_price=1, max_price=2, price_step_size=1, volume_step_size=1, ) budget_checker: BudgetChecker = exchange.budget_checker exchange.set_balance(self.quote_asset, Decimal("20")) exchange.set_balance( fc_token, Decimal("1.2") ) # 0.2 less than required; will result in 50% order reduction order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), ) adjusted_candidate = budget_checker.adjust_candidate(order_candidate, all_or_none=False) self.assertEqual(Decimal("5"), adjusted_candidate.amount) self.assertEqual(self.quote_asset, adjusted_candidate.order_collateral.token) self.assertEqual(Decimal("10"), adjusted_candidate.order_collateral.amount) self.assertEqual(fc_token, adjusted_candidate.percent_fee_collateral.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_collateral.amount) self.assertEqual(fc_token, adjusted_candidate.percent_fee_value.token) self.assertEqual(Decimal("0.2"), adjusted_candidate.percent_fee_value.amount) self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals)) fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0] self.assertEqual(fc_token, fixed_fee_collateral.token) self.assertEqual(Decimal("1"), fixed_fee_collateral.amount) self.assertEqual(self.base_asset, adjusted_candidate.potential_returns.token) self.assertEqual(Decimal("5"), adjusted_candidate.potential_returns.amount) def test_adjust_candidate_and_lock_available_collateral(self): self.exchange.set_balance(self.base_asset, Decimal("10")) first_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("7"), price=Decimal("2"), ) first_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( first_order_candidate, all_or_none=False) second_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) second_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( second_order_candidate, all_or_none=False) third_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) third_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( third_order_candidate, all_or_none=False) self.assertEqual(Decimal("7"), first_adjusted_candidate.amount) self.assertEqual(Decimal("3"), second_adjusted_candidate.amount) self.assertEqual(Decimal("3"), second_adjusted_candidate.order_collateral.amount) self.assertEqual(Decimal("0"), third_adjusted_candidate.amount) self.assertIsNone(third_adjusted_candidate.order_collateral) def test_reset_locked_collateral(self): self.exchange.set_balance(self.base_asset, Decimal("10")) first_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("7"), price=Decimal("2"), ) first_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( first_order_candidate, all_or_none=False) self.budget_checker.reset_locked_collateral() second_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) second_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( second_order_candidate, all_or_none=False) self.assertEqual(Decimal("7"), first_adjusted_candidate.amount) self.assertEqual(Decimal("5"), second_adjusted_candidate.amount) def test_adjust_candidates(self): self.exchange.set_balance(self.base_asset, Decimal("10")) first_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("7"), price=Decimal("2"), ) second_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) third_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) first_adjusted_candidate, second_adjusted_candidate, third_adjusted_candidate = ( self.budget_checker.adjust_candidates([ first_order_candidate, second_order_candidate, third_order_candidate ], all_or_none=False)) self.assertEqual(Decimal("7"), first_adjusted_candidate.amount) self.assertEqual(Decimal("3"), second_adjusted_candidate.amount) self.assertEqual(Decimal("3"), second_adjusted_candidate.order_collateral.amount) self.assertEqual(Decimal("0"), third_adjusted_candidate.amount) self.assertIsNone(third_adjusted_candidate.order_collateral) def test_adjust_candidates_resets_locked_collateral(self): self.exchange.set_balance(self.base_asset, Decimal("10")) first_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("7"), price=Decimal("2"), ) first_adjusted_candidate, = self.budget_checker.adjust_candidates( [first_order_candidate], all_or_none=False) second_order_candidate = OrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("5"), price=Decimal("2"), ) second_adjusted_candidate = self.budget_checker.adjust_candidate_and_lock_available_collateral( second_order_candidate, all_or_none=False) self.assertEqual(Decimal("7"), first_adjusted_candidate.amount) self.assertEqual(Decimal("5"), second_adjusted_candidate.amount)
def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder): quote_currency_traded: Decimal = limit_order.price * limit_order.quantity base_currency_traded: Decimal = limit_order.quantity quote_currency: str = limit_order.quote_currency base_currency: str = limit_order.base_currency if limit_order.is_buy: market.set_balance( quote_currency, market.get_balance(quote_currency) - quote_currency_traded) market.set_balance( base_currency, market.get_balance(base_currency) + base_currency_traded) market.trigger_event( MarketEvent.BuyOrderCreated, BuyOrderCreatedEvent(market.current_timestamp, OrderType.LIMIT, limit_order.trading_pair, limit_order.quantity, limit_order.price, limit_order.client_order_id, limit_order.creation_timestamp * 1e-6)) market.trigger_event( MarketEvent.OrderFilled, OrderFilledEvent( market.current_timestamp, limit_order.client_order_id, limit_order.trading_pair, TradeType.BUY, OrderType.LIMIT, limit_order.price, limit_order.quantity, AddedToCostTradeFee(Decimal(0)), ), ) market.trigger_event( MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent( market.current_timestamp, limit_order.client_order_id, base_currency, quote_currency, base_currency_traded, quote_currency_traded, OrderType.LIMIT, ), ) else: market.set_balance( quote_currency, market.get_balance(quote_currency) + quote_currency_traded) market.set_balance( base_currency, market.get_balance(base_currency) - base_currency_traded) market.trigger_event( MarketEvent.BuyOrderCreated, SellOrderCreatedEvent( market.current_timestamp, OrderType.LIMIT, limit_order.trading_pair, limit_order.quantity, limit_order.price, limit_order.client_order_id, limit_order.creation_timestamp * 1e-6, )) market.trigger_event( MarketEvent.OrderFilled, OrderFilledEvent( market.current_timestamp, limit_order.client_order_id, limit_order.trading_pair, TradeType.SELL, OrderType.LIMIT, limit_order.price, limit_order.quantity, AddedToCostTradeFee(Decimal(0)), ), ) market.trigger_event( MarketEvent.SellOrderCompleted, SellOrderCompletedEvent( market.current_timestamp, limit_order.client_order_id, base_currency, quote_currency, base_currency_traded, quote_currency_traded, OrderType.LIMIT, ), )