Пример #1
0
    def test_volatility(self, estimate_fee_mock, get_mid_price_mock):
        """
        Assert that volatility information is updated after the expected number of intervals
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate with similar balances so the skew is obvious
        usdt_balance = 1000
        eth_balance = 1000

        trading_pairs = list(
            map(lambda quote_asset: "ETH-" + quote_asset, ["USDT"]))
        market, market_infos = self.create_market(trading_pairs, 100, {
            "USDT": usdt_balance,
            "ETH": eth_balance
        })

        strategy = LiquidityMiningStrategy()
        strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=False,
            target_base_pct=Decimal(0.5),  # less base, more quote
            order_refresh_time=1,
            order_refresh_tolerance_pct=Decimal(
                0.1),  # tolerance of 10 % change
            # volatility_interval=2,
            # avg_volatility_period=2,
            # volatility_to_spread_multiplier=2,
        )

        get_mid_price_mock.return_value = Decimal(100.0)
        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start_timestamp + 1)

        # update prices to create volatility after 2 intervals
        get_mid_price_mock.return_value = Decimal(105.0)
        self.clock.backtest_til(self.start_timestamp + 2)

        get_mid_price_mock.return_value = Decimal(110)
        self.clock.backtest_til(self.start_timestamp + 3)

        # assert that volatility is none zero
        self.assertAlmostEqual(float(
            strategy.market_status_df().loc[0, 'Volatility'].strip('%')),
                               10.00,
                               delta=0.1)
    def test_create_order_and_process_fill(self):
        recorder = MarketsRecorder(sql=self.manager,
                                   markets=[self],
                                   config_file_path=self.config_file_path,
                                   strategy_name=self.strategy_name)

        create_event = BuyOrderCreatedEvent(
            timestamp=1642010000,
            type=OrderType.LIMIT,
            trading_pair=self.trading_pair,
            amount=Decimal(1),
            price=Decimal(1000),
            order_id="OID1-1642010000000000",
            creation_timestamp=1640001112.223,
            exchange_order_id="EOID1",
        )

        recorder._did_create_order(MarketEvent.BuyOrderCreated.value, self,
                                   create_event)

        fill_event = OrderFilledEvent(timestamp=1642020000,
                                      order_id=create_event.order_id,
                                      trading_pair=create_event.trading_pair,
                                      trade_type=TradeType.BUY,
                                      order_type=create_event.type,
                                      price=Decimal(1010),
                                      amount=create_event.amount,
                                      trade_fee=AddedToCostTradeFee(),
                                      exchange_trade_id="TradeId1")

        recorder._did_fill_order(MarketEvent.OrderFilled.value, self,
                                 fill_event)

        with self.manager.get_new_session() as session:
            query = session.query(Order)
            orders = query.all()
            order = orders[0]
            order_status = order.status
            trade_fills = order.trade_fills

        self.assertEqual(1, len(orders))
        self.assertEqual(self.config_file_path, orders[0].config_file_path)
        self.assertEqual(create_event.order_id, orders[0].id)
        self.assertEqual(2, len(order_status))
        self.assertEqual(MarketEvent.BuyOrderCreated.name,
                         order_status[0].status)
        self.assertEqual(MarketEvent.OrderFilled.name, order_status[1].status)
        self.assertEqual(1, len(trade_fills))
        self.assertEqual(self.config_file_path,
                         trade_fills[0].config_file_path)
        self.assertEqual(fill_event.order_id, trade_fills[0].order_id)
Пример #3
0
    def test_to_json(self):
        fee = AddedToCostTradeFee(percent=Decimal("0.5"),
                                  percent_token=self.quote_asset)
        trade_update = TradeUpdate(
            trade_id="12345",
            client_order_id=self.client_order_id,
            exchange_order_id="EOID1",
            trading_pair=self.trading_pair,
            fill_timestamp=1640001112,
            fill_price=Decimal("1000.11"),
            fill_base_amount=Decimal("2"),
            fill_quote_amount=Decimal("2000.22"),
            fee=fee,
        )

        order: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        order.order_fills["1"] = trade_update

        order_json = order.to_json()

        self.assertIsInstance(order_json, dict)

        self.assertEqual(order_json["client_order_id"], order.client_order_id)
        self.assertEqual(order_json["exchange_order_id"],
                         order.exchange_order_id)
        self.assertEqual(order_json["trading_pair"], order.trading_pair)
        self.assertEqual(order_json["order_type"], order.order_type.name)
        self.assertEqual(order_json["trade_type"], order.trade_type.name)
        self.assertEqual(order_json["price"], str(order.price))
        self.assertEqual(order_json["amount"], str(order.amount))
        self.assertEqual(order_json["executed_amount_base"],
                         str(order.executed_amount_base))
        self.assertEqual(order_json["executed_amount_quote"],
                         str(order.executed_amount_quote))
        self.assertEqual(order_json["last_state"],
                         str(order.current_state.value))
        self.assertEqual(order_json["leverage"], str(order.leverage))
        self.assertEqual(order_json["position"], order.position.value)
        self.assertEqual(order_json["creation_timestamp"],
                         order.creation_timestamp)
        self.assertEqual(order_json["order_fills"],
                         {"1": trade_update.to_json()})
Пример #4
0
 async def _process_trade_message(self, trade_msg: Dict[str, Any]):
     """
     Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
     event if the total executed amount equals to the specified order amount.
     """
     for order in self._in_flight_orders.values():
         await order.get_exchange_order_id()
     track_order = [o for o in self._in_flight_orders.values() if trade_msg["order_id"] == o.exchange_order_id]
     if not track_order:
         return
     tracked_order = track_order[0]
     updated = tracked_order.update_with_trade_update(trade_msg)
     if not updated:
         return
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp,
             tracked_order.client_order_id,
             tracked_order.trading_pair,
             tracked_order.trade_type,
             tracked_order.order_type,
             Decimal(str(trade_msg["traded_price"])),
             Decimal(str(trade_msg["traded_quantity"])),
             AddedToCostTradeFee(
                 flat_fees=[TokenAmount(trade_msg["fee_currency"], Decimal(str(trade_msg["fee"])))]
             ),
             exchange_trade_id=str(trade_msg.get("trade_id", int(self._time() * 1e6)))
         )
     )
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount:
         tracked_order.last_state = "FILLED"
         self.logger().info(f"The {tracked_order.trade_type.name} order "
                            f"{tracked_order.client_order_id} has completed "
                            f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(event_tag,
                            event_class(self.current_timestamp,
                                        tracked_order.client_order_id,
                                        tracked_order.base_asset,
                                        tracked_order.quote_asset,
                                        tracked_order.executed_amount_base,
                                        tracked_order.executed_amount_quote,
                                        tracked_order.order_type))
         self.stop_tracking_order(tracked_order.client_order_id)
Пример #5
0
    def test_collect_metrics_for_single_event(self):
        self.rate_oracle._prices = {"HBOT-USDT": Decimal("100")}

        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.async_run_with_timeout(
            self.metrics_collector.collect_metrics([event]))

        expected_dispatch_request = {
            "url": f"{self.metrics_collector_url}/client_metrics",
            "method": "POST",
            "request_obj": {
                "headers": {
                    'Content-Type': "application/json"
                },
                "data":
                json.dumps({
                    "source": "hummingbot",
                    "name": TradeVolumeMetricCollector.METRIC_NAME,
                    "instance_id": self.instance_id,
                    "exchange": self.connector_name,
                    "version": self.client_version,
                    "system":
                    f"{platform.system()} {platform.release()}({platform.platform()})",
                    "value": str(event.amount * event.price * 100)
                }),
                "params": {
                    "ddtags":
                    f"instance_id:{self.instance_id},"
                    f"client_version:{self.client_version},"
                    f"type:metrics",
                    "ddsource":
                    "hummingbot-client"
                }
            }
        }

        self.dispatcher_mock.request.assert_called()
        dispatched_metric = self.dispatcher_mock.request.call_args[0][0]

        self.assertEqual(expected_dispatch_request, dispatched_metric)
Пример #6
0
 def order_filled_event_from_binance_execution_report(cls, execution_report: Dict[str, any]) -> "OrderFilledEvent":
     execution_type: str = execution_report.get("x")
     if execution_type != "TRADE":
         raise ValueError(f"Invalid execution type '{execution_type}'.")
     return OrderFilledEvent(
         execution_report["E"] * 1e-3,
         execution_report["c"],
         execution_report["s"],
         TradeType.BUY if execution_report["S"] == "BUY" else TradeType.SELL,
         OrderType[execution_report["o"]],
         Decimal(execution_report["L"]),
         Decimal(execution_report["l"]),
         AddedToCostTradeFee(flat_fees=[TokenAmount(execution_report["N"], Decimal(execution_report["n"]))]),
         exchange_trade_id=execution_report["t"]
     )
Пример #7
0
 def test_process_order_fill_event_sell_no_initial_cost_set(self):
     amount = Decimal("1")
     price = Decimal("9000")
     event = OrderFilledEvent(
         timestamp=1,
         order_id="order1",
         trading_pair=self.trading_pair,
         trade_type=TradeType.SELL,
         order_type=OrderType.LIMIT,
         price=price,
         amount=amount,
         trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
     )
     with self.assertRaises(RuntimeError):
         self.delegate.process_order_fill_event(event)
Пример #8
0
 def get_fee(self,
             base_currency: str,
             quote_currency: str,
             order_type: OrderType,
             order_side: TradeType,
             amount: Decimal,
             price: Decimal = s_decimal_NaN,
             is_maker: Optional[bool] = None) -> AddedToCostTradeFee:
     """
     To get trading fee, this function is simplified by using fee override configuration. Most parameters to this
     function are ignore except order_type. Use OrderType.LIMIT_MAKER to specify you want trading fee for
     maker order.
     """
     is_maker = order_type is OrderType.LIMIT_MAKER
     return AddedToCostTradeFee(percent=self.estimate_fee_pct(is_maker))
    def test_budget_allocation(self, estimate_fee_mock):
        """
        Liquidity mining strategy budget allocation is different from pmm, it depends on the token base and it splits
        its budget between the quote tokens.
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))]
        )

        # initiate
        usdt_balance = 1000
        busd_balance = 900
        eth_balance = 100
        btc_balance = 10

        trading_pairs = list(map(lambda quote_asset: "ETH-" + quote_asset, ["USDT", "BUSD", "BTC"]))
        market, market_infos = self.create_market(trading_pairs, 100, {"USDT": usdt_balance, "BUSD": busd_balance, "ETH": eth_balance, "BTC": btc_balance})

        strategy = LiquidityMiningStrategy()
        strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=False,
            target_base_pct=Decimal(0.5),
            order_refresh_time=5,
            order_refresh_tolerance_pct=Decimal(0.1),  # tolerance of 10 % change
        )

        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start_timestamp + 10)

        # there should be a buy and sell budget for each pair
        self.assertEqual(len(strategy.sell_budgets), 3)
        self.assertEqual(len(strategy.buy_budgets), 3)

        # the buy budgets use all of the available balance for the quote tokens
        self.assertEqual(strategy.buy_budgets["ETH-USDT"], usdt_balance)
        self.assertEqual(strategy.buy_budgets["ETH-BTC"], btc_balance)
        self.assertEqual(strategy.buy_budgets["ETH-BUSD"], busd_balance)

        # the sell budget tries to evenly split the base token between the quote tokens
        self.assertLess(strategy.sell_budgets["ETH-USDT"], eth_balance * 0.4)
        self.assertLess(strategy.sell_budgets["ETH-BTC"], eth_balance * 0.4)
        self.assertLess(strategy.sell_budgets["ETH-BUSD"], eth_balance * 0.4)
Пример #10
0
    def test_fill_events_created_from_order_book_rows_have_unique_trade_ids(
            self):
        rows = [
            OrderBookRow(Decimal(1000), Decimal(1), 1),
            OrderBookRow(Decimal(1001), Decimal(2), 2)
        ]
        fill_events = OrderFilledEvent.order_filled_events_from_order_book_rows(
            timestamp=1640001112.223,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            trade_fee=AddedToCostTradeFee(),
            order_book_rows=rows)

        self.assertEqual("OID1_0", fill_events[0].exchange_trade_id)
        self.assertEqual("OID1_1", fill_events[1].exchange_trade_id)
Пример #11
0
 def _get_fee(self,
              base_currency: str,
              quote_currency: str,
              order_type: OrderType,
              order_side: TradeType,
              amount: Decimal,
              price: Decimal = s_decimal_NaN,
              is_maker: Optional[bool] = None) -> TradeFeeBase:
     """
     Calculates the estimated fee an order would pay based on the connector configuration
     :param base_currency: the order base currency
     :param quote_currency: the order quote currency
     :param order_type: the type of order (MARKET, LIMIT, LIMIT_MAKER)
     :param order_side: if the order is for buying or selling
     :param amount: the order amount
     :param price: the order price
     :param is_maker: if we take into account maker fee (True) or taker fee (None, False)
     :return: the estimated fee for the order
     """
     trading_pair = combine_to_hb_trading_pair(base=base_currency,
                                               quote=quote_currency)
     fee_schema = self._trading_fees.get(trading_pair, None)
     if fee_schema is None:
         self.logger().warning(
             f"For trading pair = {trading_pair} there is no fee schema loaded, using presets!"
         )
         fee = build_trade_fee(exchange=self.name,
                               is_maker=is_maker,
                               base_currency=base_currency,
                               quote_currency=quote_currency,
                               order_type=order_type,
                               order_side=order_side,
                               amount=amount,
                               price=price)
     else:
         if fee_schema.type == LatokenTakeType.PROPORTION or fee_schema.take == LatokenCommissionType.PERCENT:
             pass  # currently not implemented but is nice to have in next release(s)
         percent = fee_schema.maker_fee if order_type is OrderType.LIMIT_MAKER or (
             is_maker is not None and is_maker) else fee_schema.taker_fee
         fee = AddedToCostTradeFee(
             percent=percent
         ) if order_side == TradeType.BUY else DeductedFromReturnsTradeFee(
             percent=percent)
     return fee
Пример #12
0
    def test_multiple_markets(self, estimate_fee_mock):
        """
        Liquidity Mining supports one base asset but multiple quote assets. This shows that the user can successfully
        provide liquidity for two different pairs and the market can execute the other side of them.
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate
        self.clock.add_iterator(self.default_strategy)
        self.clock.backtest_til(self.start_timestamp + self.clock_tick_size)

        # ETH-USDT
        self.simulate_maker_market_trade(False, 50, 1, "ETH-USDT")
        self.clock.backtest_til(self.start_timestamp + 8)

        # ETH-BTC
        self.simulate_maker_market_trade(False, 50, 1, "ETH-BTC")
        self.clock.backtest_til(self.start_timestamp + 16)
Пример #13
0
    def test_attribute_names_for_file_export_are_valid(self):
        trade_fill = TradeFill(config_file_path=self.config_file_path,
                               strategy=self.strategy_name,
                               market=self.display_name,
                               symbol=self.symbol,
                               base_asset=self.base,
                               quote_asset=self.quote,
                               timestamp=int(time.time()),
                               order_id="OID1",
                               trade_type=TradeType.BUY.name,
                               order_type=OrderType.LIMIT.name,
                               price=Decimal(1000),
                               amount=Decimal(1),
                               leverage=1,
                               trade_fee=AddedToCostTradeFee().to_json(),
                               exchange_trade_id="EOID1",
                               position="NILL")

        values = [
            getattr(trade_fill, attribute)
            for attribute in TradeFill.attribute_names_for_file_export()
        ]

        expected_values = [
            trade_fill.exchange_trade_id,
            trade_fill.config_file_path,
            trade_fill.strategy,
            trade_fill.market,
            trade_fill.symbol,
            trade_fill.base_asset,
            trade_fill.quote_asset,
            trade_fill.timestamp,
            trade_fill.order_id,
            trade_fill.trade_type,
            trade_fill.order_type,
            trade_fill.price,
            trade_fill.amount,
            trade_fill.leverage,
            trade_fill.trade_fee,
            trade_fill.position,
        ]

        self.assertEqual(expected_values, values)
Пример #14
0
    def test_calculate_fees_in_quote_for_one_trade_fill_with_fees_different_tokens(
            self):
        rate_oracle = RateOracle()
        rate_oracle._prices["DAI-COINALPHA"] = Decimal("2")
        rate_oracle._prices["USDT-DAI"] = Decimal("0.9")
        RateOracle._shared_instance = rate_oracle

        performance_metric = PerformanceMetrics()
        flat_fees = [
            TokenAmount(token="USDT", amount=Decimal("10")),
            TokenAmount(token="DAI", amount=Decimal("5")),
        ]
        trade = TradeFill(
            config_file_path="some-strategy.yml",
            strategy="pure_market_making",
            market="binance",
            symbol="HBOT-COINALPHA",
            base_asset="HBOT",
            quote_asset="COINALPHA",
            timestamp=int(time.time()),
            order_id="someId0",
            trade_type="BUY",
            order_type="LIMIT",
            price=1000,
            amount=1,
            trade_fee=AddedToCostTradeFee(percent=Decimal("0.1"),
                                          percent_token="COINALPHA",
                                          flat_fees=flat_fees).to_json(),
            exchange_trade_id="someExchangeId0",
            position=PositionAction.NIL.value,
        )

        self.async_run_with_timeout(
            performance_metric._calculate_fees(quote="COINALPHA",
                                               trades=[trade]))

        expected_fee_amount = Decimal(str(trade.amount)) * Decimal(
            str(trade.price)) * Decimal("0.1")
        expected_fee_amount += flat_fees[0].amount * Decimal("0.9") * Decimal(
            "2")
        expected_fee_amount += flat_fees[1].amount * Decimal("2")
        self.assertEqual(expected_fee_amount, performance_metric.fee_in_quote)
Пример #15
0
    def get_fee(self,
                base_currency: str,
                quote_currency: str,
                order_type: OrderType,
                order_side: TradeType,
                amount: Decimal,
                price: Decimal = s_decimal_NaN,
                is_maker: Optional[bool] = None) -> AddedToCostTradeFee:
        """
        Calculates the fee to pay based on the fee information provided by the exchange for the account and the token pair.
        If exchange info is not available it calculates the estimated fee an order would pay based on the connector
            configuration

        :param base_currency: the order base currency
        :param quote_currency: the order quote currency
        :param order_type: the type of order (MARKET, LIMIT, LIMIT_MAKER)
        :param order_side: if the order is for buying or selling
        :param amount: the order amount
        :param price: the order price
        :param is_maker: True if the order is a maker order, False if it is a taker order

        :return: the calculated or estimated fee
        """

        is_maker = is_maker or (order_type is OrderType.LIMIT_MAKER)
        trading_pair = combine_to_hb_trading_pair(base=base_currency, quote=quote_currency)
        if trading_pair in self._trading_fees:
            fees_data = self._trading_fees[trading_pair]
            fee_value = Decimal(fees_data["makerFeeRate"]) if is_maker else Decimal(fees_data["takerFeeRate"])
            fee = AddedToCostTradeFee(percent=fee_value)
        else:
            fee = build_trade_fee(
                self.name,
                is_maker,
                base_currency=base_currency,
                quote_currency=quote_currency,
                order_type=order_type,
                order_side=order_side,
                amount=amount,
                price=price,
            )
        return fee
Пример #16
0
 async def _trigger_order_fill(self,
                               tracked_order: AltmarketsInFlightOrder,
                               update_msg: Dict[str, Any]):
     executed_price = Decimal(str(update_msg.get("price")
                                  if update_msg.get("price") is not None
                                  else update_msg.get("avg_price", "0")))
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp,
             tracked_order.client_order_id,
             tracked_order.trading_pair,
             tracked_order.trade_type,
             tracked_order.order_type,
             executed_price,
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(percent=update_msg["trade_fee"]),
             update_msg.get("exchange_trade_id", update_msg.get("id", update_msg.get("order_id")))
         )
     )
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             (not tracked_order.is_cancelled and tracked_order.is_done):
         tracked_order.last_state = "done"
         self.logger().info(f"The {tracked_order.trade_type.name} order "
                            f"{tracked_order.client_order_id} has completed "
                            f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(event_tag,
                            event_class(self.current_timestamp,
                                        tracked_order.client_order_id,
                                        tracked_order.base_asset,
                                        tracked_order.quote_asset,
                                        tracked_order.fee_asset,
                                        tracked_order.executed_amount_base,
                                        tracked_order.executed_amount_quote,
                                        tracked_order.fee_paid,
                                        tracked_order.order_type))
         self.stop_tracking_order(tracked_order.client_order_id)
Пример #17
0
 def get_fee(
         self,
         base_currency: str,
         quote_currency: str,
         order_type: OrderType,
         order_side: TradeType,
         amount: Decimal,
         price: Decimal = s_decimal_NaN,
         is_maker: Optional[bool] = None
 ) -> AddedToCostTradeFee:
     """For more information: https://ascendex.github.io/ascendex-pro-api/#place-order."""
     trading_pair = f"{base_currency}-{quote_currency}"
     trading_rule = self._trading_rules[trading_pair]
     fee_percent = Decimal("0")
     if order_side == TradeType.BUY:
         if trading_rule.commission_type == AscendExCommissionType.QUOTE:
             fee_percent = trading_rule.commission_reserve_rate
     elif trading_rule.commission_type == AscendExCommissionType.BASE:
         fee_percent = trading_rule.commission_reserve_rate
     return AddedToCostTradeFee(percent=fee_percent)
Пример #18
0
    def test_simulate_maker_market_trade(self, estimate_fee_mock):
        """
        Test that we can set up a liquidity mining strategy, and a trade
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate
        self.clock.add_iterator(self.default_strategy)
        self.clock.backtest_til(self.start_timestamp)

        # assert that there are no active trades on initialization and before clock has moved forward
        self.assertEqual(0, len(self.default_strategy.active_orders))

        # advance by one tick, the strategy will initiate two orders per pair
        self.clock.backtest_til(self.start_timestamp + self.clock_tick_size)
        self.assertEqual(4, len(self.default_strategy.active_orders))

        # assert that a buy and sell order is made for each pair
        self.assertTrue(
            self.has_limit_order(self.default_strategy.active_orders,
                                 'ETH-USDT', True, Decimal(99.95),
                                 Decimal(2.0)))
        self.assertTrue(
            self.has_limit_order(self.default_strategy.active_orders,
                                 'ETH-USDT', False, Decimal(100.05),
                                 Decimal(2.0)))
        self.assertTrue(
            self.has_limit_order(self.default_strategy.active_orders,
                                 'ETH-BTC', True, Decimal(99.95),
                                 Decimal(1.0005)))
        self.assertTrue(
            self.has_limit_order(self.default_strategy.active_orders,
                                 'ETH-BTC', False, Decimal(100.05),
                                 Decimal(2)))

        # Simulate buy order fill
        self.clock.backtest_til(self.start_timestamp + 8)
        self.simulate_maker_market_trade(False, Decimal("50"), Decimal("1"),
                                         "ETH-USDT")
        self.assertEqual(3, len(self.default_strategy.active_orders))
Пример #19
0
 def _trigger_order_fill(self, tracked_order: GateIoInFlightOrder,
                         update_msg: Dict[str, Any]):
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp, tracked_order.client_order_id,
             tracked_order.trading_pair, tracked_order.trade_type,
             tracked_order.order_type,
             Decimal(
                 str(
                     update_msg.get("fill_price",
                                    update_msg.get("price", "0")))),
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(flat_fees=[
                 TokenAmount(tracked_order.fee_asset,
                             tracked_order.fee_paid)
             ]), str(update_msg.get("update_time_ms",
                                    update_msg.get("id")))))
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             tracked_order.is_done:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset, tracked_order.fee_asset,
                         tracked_order.executed_amount_base,
                         tracked_order.executed_amount_quote,
                         tracked_order.fee_paid, tracked_order.order_type,
                         tracked_order.exchange_order_id))
         self.stop_tracking_order(tracked_order.client_order_id)
 async def _trigger_order_fill(self, tracked_order: CoinzoomInFlightOrder,
                               update_msg: Dict[str, Any]):
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp, tracked_order.client_order_id,
             tracked_order.trading_pair, tracked_order.trade_type,
             tracked_order.order_type,
             Decimal(
                 str(
                     update_msg.get("averagePrice",
                                    update_msg.get("price", "0")))),
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(percent=update_msg["trade_fee"]),
             update_msg.get("exchange_trade_id",
                            update_msg.get("id",
                                           update_msg.get("orderId")))))
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             tracked_order.is_done:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         await asyncio.sleep(0.1)
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset,
                         tracked_order.executed_amount_base,
                         tracked_order.executed_amount_quote,
                         tracked_order.order_type))
         self.stop_tracking_order(tracked_order.client_order_id)
Пример #21
0
    def test_update_with_trade_update_duplicate_trade_update(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        trade_update: TradeUpdate = TradeUpdate(
            trade_id="someTradeId",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            fill_price=Decimal("1.0"),
            fill_base_amount=Decimal("500.0"),
            fill_quote_amount=Decimal("500.0"),
            fee=AddedToCostTradeFee(flat_fees=[
                TokenAmount(token=self.quote_asset,
                            amount=self.trade_fee_percent * Decimal("500.0"))
            ]),
            fill_timestamp=1,
        )

        self.assertTrue(order.update_with_trade_update(trade_update))
        self.assertEqual(order.executed_amount_base,
                         trade_update.fill_base_amount)
        self.assertEqual(order.executed_amount_quote,
                         trade_update.fill_quote_amount)
        self.assertEqual(order.last_update_timestamp,
                         trade_update.fill_timestamp)
        self.assertEqual(1, len(order.order_fills))
        self.assertIn(trade_update.trade_id, order.order_fills)

        # Ignores duplicate trade update
        self.assertFalse(order.update_with_trade_update(trade_update))
Пример #22
0
    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)
Пример #23
0
    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, quote_currency,
                        base_currency_traded, quote_currency_traded,
                        Decimal("0"), OrderType.LIMIT))
Пример #24
0
    def test_process_tick_starts_metrics_collection_if_activation_interval_reached(
            self):
        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.metrics_collector._register_fill_event(event)

        last_executed_collection_process = self.metrics_collector._last_executed_collection_process

        self.metrics_collector.process_tick(timestamp=15)

        self.assertEqual(15,
                         self.metrics_collector._last_process_tick_timestamp)
        self.assertNotEqual(
            last_executed_collection_process,
            self.metrics_collector._last_executed_collection_process)
        self.assertNotIn(event, self.metrics_collector._collected_events)
    def test_tolerance_level(self, estimate_fee_mock):
        """
        Test tolerance level
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))]
        )

        # initiate strategy and add active orders
        self.clock.add_iterator(self.default_strategy)
        self.clock.backtest_til(self.start_timestamp + 9)

        # the order tolerance is 1%
        # set the orders to the same values
        proposal = Proposal("ETH-USDT", PriceSize(100, 1), PriceSize(100, 1))
        self.assertTrue(self.default_strategy.is_within_tolerance(self.default_strategy.active_orders, proposal))

        # update orders to withint the tolerance
        proposal = Proposal("ETH-USDT", PriceSize(109, 1), PriceSize(91, 1))
        self.assertTrue(self.default_strategy.is_within_tolerance(self.default_strategy.active_orders, proposal))

        # push the orders beyond the tolerance, this proposal should return False
        proposal = Proposal("ETH-USDT", PriceSize(150, 1), PriceSize(50, 1))
        self.assertFalse(self.default_strategy.is_within_tolerance(self.default_strategy.active_orders, proposal))
Пример #26
0
    def test_performance_metrics_for_derivatives(self, is_trade_fill_mock):
        rate_oracle = RateOracle()
        rate_oracle._prices["USDT-HBOT"] = Decimal("5")
        RateOracle._shared_instance = rate_oracle

        is_trade_fill_mock.return_value = True
        trades = []
        trades.append(
            self.mock_trade(id="order1",
                            amount=Decimal("100"),
                            price=Decimal("10"),
                            position="OPEN",
                            type="BUY",
                            fee=AddedToCostTradeFee(
                                flat_fees=[TokenAmount(quote, Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order2",
                            amount=Decimal("100"),
                            price=Decimal("15"),
                            position="CLOSE",
                            type="SELL",
                            fee=AddedToCostTradeFee(
                                flat_fees=[TokenAmount(quote, Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order3",
                            amount=Decimal("100"),
                            price=Decimal("20"),
                            position="OPEN",
                            type="SELL",
                            fee=AddedToCostTradeFee(
                                Decimal("0.1"),
                                flat_fees=[TokenAmount("USD", Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order4",
                            amount=Decimal("100"),
                            price=Decimal("15"),
                            position="CLOSE",
                            type="BUY",
                            fee=AddedToCostTradeFee(
                                Decimal("0.1"),
                                flat_fees=[TokenAmount("USD", Decimal("0"))])))

        cur_bals = {base: 100, quote: 10000}
        metrics = asyncio.get_event_loop().run_until_complete(
            PerformanceMetrics.create(trading_pair, trades, cur_bals))
        self.assertEqual(metrics.num_buys, 2)
        self.assertEqual(metrics.num_sells, 2)
        self.assertEqual(metrics.num_trades, 4)
        self.assertEqual(metrics.b_vol_base, Decimal("200"))
        self.assertEqual(metrics.s_vol_base, Decimal("-200"))
        self.assertEqual(metrics.tot_vol_base, Decimal("0"))
        self.assertEqual(metrics.b_vol_quote, Decimal("-2500"))
        self.assertEqual(metrics.s_vol_quote, Decimal("3500"))
        self.assertEqual(metrics.tot_vol_quote, Decimal("1000"))
        self.assertEqual(metrics.avg_b_price, Decimal("12.5"))
        self.assertEqual(metrics.avg_s_price, Decimal("17.5"))
        self.assertEqual(metrics.avg_tot_price, Decimal("15"))
        self.assertEqual(metrics.start_base_bal, Decimal("100"))
        self.assertEqual(metrics.start_quote_bal, Decimal("9000"))
        self.assertEqual(metrics.cur_base_bal, 100)
        self.assertEqual(metrics.cur_quote_bal, 10000),
        self.assertEqual(metrics.start_price, Decimal("10")),
        self.assertEqual(metrics.cur_price, Decimal("0.2"))
        self.assertEqual(metrics.trade_pnl, Decimal("1000"))
        self.assertEqual(metrics.total_pnl, Decimal("650"))
Пример #27
0
    def test_update_with_trade_update_multiple_trade_updates(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        initial_fill_price: Decimal = Decimal("0.5")
        initial_fill_amount: Decimal = Decimal("500.0")
        trade_update_1: TradeUpdate = TradeUpdate(
            trade_id="someTradeId_1",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            fill_price=initial_fill_price,
            fill_base_amount=initial_fill_amount,
            fill_quote_amount=initial_fill_price * initial_fill_amount,
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * initial_fill_amount)]),
            fill_timestamp=1,
        )

        subsequent_fill_price: Decimal = Decimal("1.0")
        subsequent_fill_amount: Decimal = Decimal("500.0")
        trade_update_2: TradeUpdate = TradeUpdate(
            trade_id="someTradeId_2",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            fill_price=subsequent_fill_price,
            fill_base_amount=subsequent_fill_amount,
            fill_quote_amount=subsequent_fill_price * subsequent_fill_amount,
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * subsequent_fill_amount)]),
            fill_timestamp=2,
        )

        self.assertTrue(order.update_with_trade_update(trade_update_1))
        self.assertIn(trade_update_1.trade_id, order.order_fills)
        self.assertEqual(order.executed_amount_base, trade_update_1.fill_base_amount)
        self.assertEqual(order.executed_amount_quote, trade_update_1.fill_quote_amount)
        self.assertEqual(order.last_update_timestamp, trade_update_1.fill_timestamp)
        self.assertEqual(1, len(order.order_fills))

        self.assertTrue(order.is_open)

        self.assertTrue(order.update_with_trade_update(trade_update_2))
        self.assertIn(trade_update_2.trade_id, order.order_fills)
        self.assertEqual(order.executed_amount_base, order.amount)
        self.assertEqual(
            order.executed_amount_quote, trade_update_1.fill_quote_amount + trade_update_2.fill_quote_amount
        )
        self.assertEqual(order.last_update_timestamp, trade_update_2.fill_timestamp)
        self.assertEqual(2, len(order.order_fills))
        self.assertEqual(
            order.average_executed_price,
            (trade_update_1.fill_quote_amount + trade_update_2.fill_quote_amount) / order.amount,
        )

        self.assertTrue(order.is_filled)
        self.assertEqual(order.current_state, OrderState.PENDING_CREATE)
Пример #28
0
    def test_average_executed_price(self):
        order_0: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        self.assertIsNone(order_0.average_executed_price)

        trade_update_0: TradeUpdate = TradeUpdate(
            trade_id="someTradeId",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=order_0.trading_pair,
            fill_price=order_0.price,
            fill_base_amount=order_0.amount,
            fill_quote_amount=(order_0.price * order_0.amount),
            fee=AddedToCostTradeFee(flat_fees=[TokenAmount(self.base_asset, Decimal(0.01) * order_0.amount)]),
            fill_timestamp=time.time(),
        )
        # Order completely filled after single trade update
        order_0.order_fills.update({trade_update_0.trade_id: trade_update_0})

        self.assertEqual(order_0.price, order_0.average_executed_price)

        order_1: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        trade_update_1: TradeUpdate = TradeUpdate(
            trade_id="someTradeId_1",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=order_1.trading_pair,
            fill_price=Decimal("0.5"),
            fill_base_amount=(order_1.amount / Decimal("2.0")),
            fill_quote_amount=(order_1.price * (order_1.amount / Decimal("2.0"))),
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(self.base_asset, Decimal(0.01) * (order_1.amount / Decimal("2.0")))]),
            fill_timestamp=time.time(),
        )

        trade_update_2: TradeUpdate = TradeUpdate(
            trade_id="someTradeId_2",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=order_1.trading_pair,
            fill_price=order_1.price,
            fill_base_amount=(order_1.amount / Decimal("2.0")),
            fill_quote_amount=(order_1.price * (order_1.amount / Decimal("2.0"))),
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(self.base_asset, Decimal(0.01) * (order_1.amount / Decimal("2.0")))]),
            fill_timestamp=time.time(),
        )

        # Order completely filled after 2 trade updates
        order_1.order_fills.update(
            {
                trade_update_1.trade_id: trade_update_1,
                trade_update_2.trade_id: trade_update_2,
            }
        )
        expected_average_price = (
            sum([order_fill.fill_price * order_fill.fill_base_amount for order_fill in order_1.order_fills.values()])
            / order_1.amount
        )
        self.assertEqual(expected_average_price, order_1.average_executed_price)
    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,
                                       quote_currency, base_currency_traded,
                                       quote_currency_traded, Decimal(0.0),
                                       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,
                                        quote_currency, base_currency_traded,
                                        quote_currency_traded, Decimal(0.0),
                                        OrderType.LIMIT))
Пример #30
0
 def setUpClass(cls):
     cls._patcher = unittest.mock.patch(
         "hummingbot.connector.connector_base.estimate_fee")
     cls._url_mock = cls._patcher.start()
     cls._url_mock.return_value = AddedToCostTradeFee(percent=Decimal("0"),
                                                      flat_fees=[])