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 = MockPerpConnector(trade_fee_schema) self.budget_checker = self.exchange.budget_checker
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 = MockPerpConnector(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: PerpetualBudgetChecker = exchange.budget_checker order_candidate = PerpetualOrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), leverage=Decimal("2"), ) populated_candidate = budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("10"), 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open
def setUp(self): self.order_fill_logger: EventLogger = EventLogger() self.cancel_order_logger: EventLogger = EventLogger() self.clock: Clock = Clock(ClockMode.BACKTEST, 1, self.start_timestamp, self.end_timestamp) self.spot_connector: BacktestMarket = BacktestMarket() self.spot_obook: MockOrderBookLoader = MockOrderBookLoader(self.trading_pair, self.base_asset, self.quote_asset) self.spot_obook.set_balanced_order_book(mid_price=100, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.spot_connector.add_data(self.spot_obook) self.spot_connector.set_balance("HBOT", 500) self.spot_connector.set_balance("ETH", 5000) self.spot_connector.set_quantization_param( QuantizationParams( self.trading_pair, 6, 6, 6, 6 ) ) self.spot_market_info = MarketTradingPairTuple(self.spot_connector, self.trading_pair, self.base_asset, self.quote_asset) self.perp_connector: MockPerpConnector = MockPerpConnector() self.perp_obook: MockOrderBookLoader = MockOrderBookLoader(self.trading_pair, self.base_asset, self.quote_asset) self.perp_obook.set_balanced_order_book(mid_price=110, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.perp_connector.add_data(self.perp_obook) self.perp_connector.set_balance("HBOT", 500) self.perp_connector.set_balance("ETH", 5000) self.perp_connector.set_quantization_param( QuantizationParams( self.trading_pair, 6, 6, 6, 6 ) ) self.perp_market_info = MarketTradingPairTuple(self.perp_connector, self.trading_pair, self.base_asset, self.quote_asset) self.clock.add_iterator(self.spot_connector) self.clock.add_iterator(self.perp_connector) self.spot_connector.add_listener(MarketEvent.OrderFilled, self.order_fill_logger) self.spot_connector.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger) self.perp_connector.add_listener(MarketEvent.OrderFilled, self.order_fill_logger) self.perp_connector.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger) self.strategy = SpotPerpetualArbitrageStrategy( self.spot_market_info, self.perp_market_info, order_amount=Decimal("1"), derivative_leverage=5, min_divergence=Decimal("0.05"), min_convergence=Decimal("0.01") )
def setUp(self): super().setUp() self.log_records = [] self.market: MockPerpConnector = MockPerpConnector( client_config_map=ClientConfigAdapter(ClientConfigMap()), trade_fee_schema=self.trade_fee_schema) self.market.set_quantization_param( QuantizationParams( self.trading_pair, price_precision=6, price_decimals=2, order_size_precision=6, order_size_decimals=2, ) ) self.market_info: MarketTradingPairTuple = MarketTradingPairTuple( self.market, self.trading_pair, self.base_asset, self.quote_asset ) self.market.set_balanced_order_book(trading_pair=self.trading_pair, mid_price=self.initial_mid_price, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.market.set_balance("COINALPHA", 1000) self.market.set_balance("HBOT", 50000) new_strategy = PerpetualMarketMakingStrategy() new_strategy.init_params( market_info=self.market_info, leverage=10, position_mode=PositionMode.ONEWAY.name.title(), bid_spread=Decimal("0.5"), ask_spread=Decimal("0.4"), order_amount=Decimal("100"), long_profit_taking_spread=self.long_profit_taking_spread, short_profit_taking_spread=self.short_profit_taking_spread, stop_loss_spread=self.stop_loss_spread, time_between_stop_loss_orders=10.0, stop_loss_slippage_buffer=self.stop_loss_slippage_buffer, ) new_strategy._position_mode_ready = True self.clock: Clock = Clock(ClockMode.BACKTEST, self.clock_tick_size, self.start_timestamp, self.end_timestamp) self.clock.add_iterator(self.market) self._configure_strategy(new_strategy) self.clock.backtest_til(self.start_timestamp) self.cancel_order_logger: EventLogger = EventLogger() self.market.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger)
def setUp(self) -> None: super().setUp() self.strategy = None self.markets = {"binance": ExchangeBase(), "kucoin": MockPerpConnector()} self.notifications = [] self.log_errors = [] assign_config_default(strategy_cmap) strategy_cmap.get("spot_connector").value = "binance" strategy_cmap.get("spot_market").value = "BTC-USDT" strategy_cmap.get("perpetual_connector").value = "kucoin" strategy_cmap.get("perpetual_market").value = "BTC-USDT" strategy_cmap.get("order_amount").value = Decimal("1") strategy_cmap.get("perpetual_leverage").value = Decimal("2") strategy_cmap.get("min_opening_arbitrage_pct").value = Decimal("10") strategy_cmap.get("min_closing_arbitrage_pct").value = Decimal("1")
def setUp(self): self.log_records = [] self.order_fill_logger: EventLogger = EventLogger() self.cancel_order_logger: EventLogger = EventLogger() self.clock: Clock = Clock(ClockMode.BACKTEST, 1, self.start_timestamp, self.end_timestamp) self.spot_connector: MockPaperExchange = MockPaperExchange( client_config_map=ClientConfigAdapter(ClientConfigMap())) self.spot_connector.set_balanced_order_book(trading_pair=trading_pair, mid_price=100, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.spot_connector.set_balance(base_asset, 5) self.spot_connector.set_balance(quote_asset, 500) self.spot_connector.set_quantization_param( QuantizationParams(trading_pair, 6, 6, 6, 6)) self.spot_market_info = MarketTradingPairTuple(self.spot_connector, trading_pair, base_asset, quote_asset) self.perp_connector: MockPerpConnector = MockPerpConnector( client_config_map=ClientConfigAdapter(ClientConfigMap())) self.perp_connector.set_leverage(trading_pair, 5) self.perp_connector.set_balanced_order_book(trading_pair=trading_pair, mid_price=110, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.perp_connector.set_balance(base_asset, 5) self.perp_connector.set_balance(quote_asset, 500) self.perp_connector.set_quantization_param( QuantizationParams(trading_pair, 6, 6, 6, 6)) self.perp_market_info = MarketTradingPairTuple(self.perp_connector, trading_pair, base_asset, quote_asset) self.clock.add_iterator(self.spot_connector) self.clock.add_iterator(self.perp_connector) self.spot_connector.add_listener(MarketEvent.OrderFilled, self.order_fill_logger) self.spot_connector.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger) self.perp_connector.add_listener(MarketEvent.OrderFilled, self.order_fill_logger) self.perp_connector.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger) self.strategy = SpotPerpetualArbitrageStrategy() self.strategy.init_params( spot_market_info=self.spot_market_info, perp_market_info=self.perp_market_info, order_amount=Decimal("1"), perp_leverage=5, min_opening_arbitrage_pct=Decimal("0.05"), min_closing_arbitrage_pct=Decimal("0.01"), next_arbitrage_opening_delay=10, ) self.strategy.logger().setLevel(1) self.strategy.logger().addHandler(self) self._last_tick = 0
class PerpetualBudgetCheckerTest(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 = MockPerpConnector(trade_fee_schema) self.budget_checker = self.exchange.budget_checker def test_populate_collateral_fields_buy_order(self): order_candidate = PerpetualOrderCandidate( 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open def test_populate_collateral_fields_taker_buy_order(self): order_candidate = PerpetualOrderCandidate( 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open def test_populate_collateral_fields_buy_order_with_leverage(self): order_candidate = PerpetualOrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), leverage=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("10"), 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open def test_populate_collateral_fields_sell_order(self): order_candidate = PerpetualOrderCandidate( 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.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.assertIsNone(populated_candidate.potential_returns ) # order results in position open def test_populate_collateral_fields_sell_order_with_leverage(self): order_candidate = PerpetualOrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("10"), price=Decimal("2"), leverage=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("10"), 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open 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 = MockPerpConnector(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: PerpetualBudgetChecker = exchange.budget_checker order_candidate = PerpetualOrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal("10"), price=Decimal("2"), leverage=Decimal("2"), ) populated_candidate = budget_checker.populate_collateral_entries( order_candidate) self.assertEqual(self.quote_asset, populated_candidate.order_collateral.token) self.assertEqual(Decimal("10"), 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.assertIsNone(populated_candidate.potential_returns ) # order results in position open def test_populate_collateral_for_position_close(self): order_candidate = PerpetualOrderCandidate( trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal("10"), price=Decimal("2"), leverage=Decimal("2"), position_close=True, ) populated_candidate = self.budget_checker.populate_collateral_entries( order_candidate) self.assertIsNone(populated_candidate.order_collateral ) # the collateral is the contract itself self.assertIsNone(populated_candidate.percent_fee_collateral) self.assertIsNone(populated_candidate.percent_fee_value) 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_adjust_candidate_sufficient_funds(self): self.exchange.set_balance(self.quote_asset, Decimal("100")) order_candidate = PerpetualOrderCandidate( 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(Decimal("10"), adjusted_candidate.amount) 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.assertIsNone(adjusted_candidate.potential_returns ) # order results in position open 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 = PerpetualOrderCandidate( 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) 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.assertIsNone(adjusted_candidate.potential_returns ) # order results in position open def test_adjust_candidate_sell_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 = PerpetualOrderCandidate( 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("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.assertIsNone(adjusted_candidate.potential_returns ) # order results in position open