Example #1
0
    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
Example #7
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