Beispiel #1
0
    def test_aggregated_position_with_two_related_trades_from_three(self):
        trades = []

        trades.append(self.mock_trade(id="order1", amount=100, price=10))
        trades.append(self.mock_trade(id="order2", amount=200, price=15))
        trades.append(self.mock_trade(id="order1", amount=300, price=20))

        aggregated_buys, aggregated_sells = PerformanceMetrics.aggregate_position_order(
            trades, [])

        self.assertEqual(len(aggregated_buys), 2)
        trade = aggregated_buys[0]
        self.assertTrue(trade.order_id == "order1" and trade.amount == 400
                        and trade.price == 15)
        self.assertEqual(aggregated_buys[1], trades[1])
        self.assertEqual(len(aggregated_sells), 0)

        trades = []

        trades.append(self.mock_trade(id="order1", amount=100, price=10))
        trades.append(self.mock_trade(id="order2", amount=200, price=15))
        trades.append(self.mock_trade(id="order1", amount=300, price=20))

        aggregated_buys, aggregated_sells = PerformanceMetrics.aggregate_position_order(
            [], trades)

        self.assertEqual(len(aggregated_buys), 0)
        self.assertEqual(len(aggregated_sells), 2)
        trade = aggregated_sells[0]
        self.assertTrue(trade.order_id == "order1" and trade.amount == 400
                        and trade.price == 15)
        self.assertEqual(aggregated_sells[1], trades[1])
Beispiel #2
0
    def test_calculate_fees_in_quote_for_one_trade_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 = Trade(trading_pair="HBOT-COINALPHA",
                      side=TradeType.BUY,
                      price=Decimal("1000"),
                      amount=Decimal("1"),
                      order_type=OrderType.LIMIT,
                      market="binance",
                      timestamp=1640001112.223,
                      trade_fee=AddedToCostTradeFee(percent=Decimal("0.1"),
                                                    percent_token="COINALPHA",
                                                    flat_fees=flat_fees))

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

        expected_fee_amount = trade.amount * trade.price * trade.trade_fee.percent
        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)
Beispiel #3
0
    def test_position_order_returns_nothing_when_no_open_and_no_close_orders(
            self):
        trade_for_open = [
            self.mock_trade(id=f"order{i}",
                            amount=100,
                            price=10,
                            position="INVALID") for i in range(3)
        ]
        trades_for_close = [
            self.mock_trade(id=f"order{i}",
                            amount=100,
                            price=10,
                            position="INVALID") for i in range(2)
        ]

        self.assertIsNone(
            PerformanceMetrics.position_order(trade_for_open,
                                              trades_for_close))

        trade_for_open[1].position = "OPEN"

        self.assertIsNone(
            PerformanceMetrics.position_order(trade_for_open,
                                              trades_for_close))

        trade_for_open[1].position = "INVALID"
        trades_for_close[-1].position = "CLOSE"

        self.assertIsNone(
            PerformanceMetrics.position_order(trade_for_open,
                                              trades_for_close))
    def test_aggregated_position_for_unrelated_trades(self):
        trades = []

        trades.append(self.mock_trade(id="order1", amount=100, price=10))
        trades.append(self.mock_trade(id="order2", amount=200, price=15))
        trades.append(self.mock_trade(id="order3", amount=300, price=20))

        aggregated_buys, aggregated_sells = PerformanceMetrics.aggregate_position_order(trades, [])

        self.assertEqual(aggregated_buys, trades)
        self.assertEqual(len(aggregated_sells), 0)

        aggregated_buys, aggregated_sells = PerformanceMetrics.aggregate_position_order([], trades)

        self.assertEqual(len(aggregated_buys), 0)
        self.assertEqual(aggregated_sells, trades)
Beispiel #5
0
 async def exchange_balances_extra_df(
         self,  # type: HummingbotApplication
         ex_balances: Dict[str, Decimal],
         ex_avai_balances: Dict[str, Decimal]):
     total_col_name = f"Total ({RateOracle.global_token_symbol})"
     allocated_total = Decimal("0")
     rows = []
     for token, bal in ex_balances.items():
         if bal == Decimal(0):
             continue
         avai = Decimal(ex_avai_balances.get(
             token.upper(),
             0)) if ex_avai_balances is not None else Decimal(0)
         allocated = f"{(bal - avai) / bal:.0%}"
         rate = await RateOracle.global_rate(token)
         rate = Decimal("0") if rate is None else rate
         global_value = rate * bal
         allocated_total += rate * (bal - avai)
         rows.append({
             "Asset":
             token.upper(),
             "Total":
             round(bal, 4),
             total_col_name:
             PerformanceMetrics.smart_round(global_value),
             "Allocated":
             allocated,
         })
     df = pd.DataFrame(
         data=rows, columns=["Asset", "Total", total_col_name, "Allocated"])
     df.sort_values(by=["Asset"], inplace=True)
     return df, allocated_total
Beispiel #6
0
    async def market_trades_report(
            self,  # type: HummingbotApplication
            connector,
            days: float,
            market: str):
        trades: List[Trade] = await connector.get_my_trades(market, days)
        g_sym = RateOracle.global_token_symbol
        if not trades:
            self._notify(f"There is no trade on {market}.")
            return
        data = []
        amount_g_col_name = f" Amount ({g_sym})"
        columns = ["Time", " Side", " Price", "Amount", amount_g_col_name]
        trades = sorted(trades, key=lambda x: (x.trading_pair, x.timestamp))
        fees = {}  # a dict of token and total fee amount
        fee_usd = 0

        for trade in trades:
            time = f"{datetime.fromtimestamp(trade.timestamp / 1e3).strftime('%Y-%m-%d %H:%M:%S')} "
            side = "buy" if trade.side is TradeType.BUY else "sell"
            usd = await RateOracle.global_value(
                trade.trading_pair.split("-")[0], trade.amount)
            data.append([
                time, side,
                PerformanceMetrics.smart_round(trade.price),
                PerformanceMetrics.smart_round(trade.amount),
                round(usd)
            ])
            for fee in trade.trade_fee.flat_fees:
                if fee[0] not in fees:
                    fees[fee[0]] = fee[1]
                else:
                    fees[fee[0]] += fee[1]
                fee_usd += await RateOracle.global_value(fee[0], fee[1])

        lines = []
        df: pd.DataFrame = pd.DataFrame(data=data, columns=columns)
        lines.extend([f"  {market.upper()}"])
        lines.extend(
            ["    " + line for line in df.to_string(index=False).split("\n")])
        self._notify("\n" + "\n".join(lines))
        fee_text = ",".join(k + ": " + f"{v:.4f}" for k, v in fees.items())
        self._notify(
            f"\n  Total traded: {g_sym} {df[amount_g_col_name].sum():.0f}    "
            f"Fees: {fee_text} ({g_sym} {fee_usd:.2f})")
Beispiel #7
0
    def quotes_rate_df(self):
        columns = ["Quotes pair", "Rate"]
        quotes_pair: str = f"{self._market_info_2.quote_asset}-{self._market_info_1.quote_asset}"
        data = [[
            quotes_pair,
            PerformanceMetrics.smart_round(self._rate_source.rate(quotes_pair))
        ]]

        return pd.DataFrame(data=data, columns=columns)
Beispiel #8
0
 def profitability_df(self):
     data = []
     columns = [
         "Id", f"{self.base_asset} Change", f"{self.quote_asset} Change",
         f"Unclaimed {self.base_asset}", f"Unclaimed {self.quote_asset}",
         "Total Profit/loss (%)"
     ]
     for position in self.active_positions:
         profit_data = self.calculate_profitability(position)
         data.append([
             position.token_id,
             PerformanceMetrics.smart_round(profit_data['base_change'], 8),
             PerformanceMetrics.smart_round(profit_data['quote_change'], 8),
             PerformanceMetrics.smart_round(profit_data['base_fee'], 8),
             PerformanceMetrics.smart_round(profit_data['quote_fee'], 8),
             PerformanceMetrics.smart_round(profit_data['profitability'], 8)
         ])
     return pd.DataFrame(data=data, columns=columns)
 def test_performance_metrics(self):
     trades: List[Trade] = [
         Trade(trading_pair, TradeType.BUY, 100, 10, None, trading_pair, 1, TradeFee(0.0, [(quote, 0)])),
         Trade(trading_pair, TradeType.SELL, 120, 15, None, trading_pair, 1, TradeFee(0.0, [(quote, 0)]))
     ]
     cur_bals = {base: 100, quote: 10000}
     metrics = asyncio.get_event_loop().run_until_complete(
         PerformanceMetrics.create("hbot_exchange", trading_pair, trades, cur_bals))
     self.assertEqual(Decimal("200"), metrics.trade_pnl)
     print(metrics)
Beispiel #10
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)
Beispiel #11
0
    def test_position_order_returns_open_and_close_pair(self):
        trades_for_open = [self.mock_trade(id=f"order{i}", amount=100, price=10, position="INVALID")
                           for i in range(3)]
        trades_for_close = [self.mock_trade(id=f"order{i}", amount=100, price=10, position="INVALID")
                            for i in range(2)]

        trades_for_open[1].position = "OPEN"
        trades_for_close[-1].position = "CLOSE"

        selected_open, selected_close = PerformanceMetrics.position_order(trades_for_open.copy(),
                                                                          trades_for_close.copy())
        self.assertEqual(selected_open, trades_for_open[1])
        self.assertEqual(selected_close, trades_for_close[-1])
Beispiel #12
0
 def active_positions_df(self) -> pd.DataFrame:
     columns = [
         "Id", "Symbol", "Fee Tier", "Range", "Current Base",
         "Current Quote", ""
     ]
     data = []
     if len(self.active_positions) > 0:
         for position in self.active_positions:
             data.append([
                 position.token_id, position.trading_pair,
                 position.fee_tier,
                 f"{PerformanceMetrics.smart_round(Decimal(str(position.lower_price)), 8)} - "
                 f"{PerformanceMetrics.smart_round(Decimal(str(position.upper_price)), 8)}",
                 PerformanceMetrics.smart_round(
                     Decimal(str(position.current_base_amount)), 8),
                 PerformanceMetrics.smart_round(
                     Decimal(str(position.current_quote_amount)), 8),
                 "[In range]" if self._last_price >= position.lower_price
                 and self._last_price <= position.upper_price else
                 "[Out of range]"
             ])
     return pd.DataFrame(data=data, columns=columns)
Beispiel #13
0
    def test_performance_metrics_for_derivatives(self, is_trade_fill_mock):
        is_trade_fill_mock.return_value = True
        trades = []
        trades.append(self.mock_trade(id="order1",
                                      amount=100,
                                      price=10,
                                      position="OPEN",
                                      type="BUY",
                                      fee=TradeFee(0.0, [(quote, 0)])))
        trades.append(self.mock_trade(id="order2",
                                      amount=100,
                                      price=15,
                                      position="CLOSE",
                                      type="SELL",
                                      fee=TradeFee(0.0, [(quote, 0)])))
        trades.append(self.mock_trade(id="order3",
                                      amount=100,
                                      price=20,
                                      position="OPEN",
                                      type="SELL",
                                      fee=TradeFee(0.1, [("USD", 0)])))
        trades.append(self.mock_trade(id="order4",
                                      amount=100,
                                      price=15,
                                      position="CLOSE",
                                      type="BUY",
                                      fee=TradeFee(0.1, [("USD", 0)])))

        cur_bals = {base: 100, quote: 10000}
        metrics = asyncio.get_event_loop().run_until_complete(
            PerformanceMetrics.create("hbot_exchange", 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("15"))
        self.assertEqual(metrics.trade_pnl, Decimal("1000"))
        self.assertEqual(metrics.total_pnl, Decimal("650"))
Beispiel #14
0
    def test_smart_round(self):
        value = PerformanceMetrics.smart_round(None)
        self.assertIsNone(value)
        value = PerformanceMetrics.smart_round(Decimal("NaN"))
        self.assertTrue(value.is_nan())

        value = PerformanceMetrics.smart_round(Decimal("10000.123456789"))
        self.assertEqual(value, Decimal("10000"))
        value = PerformanceMetrics.smart_round(Decimal("100.123456789"))
        self.assertEqual(value, Decimal("100.1"))
        value = PerformanceMetrics.smart_round(Decimal("1.123456789"))
        self.assertEqual(value, Decimal("1.12"))
        value = PerformanceMetrics.smart_round(Decimal("0.123456789"))
        self.assertEqual(value, Decimal("0.1234"))
        value = PerformanceMetrics.smart_round(Decimal("0.000456789"))
        self.assertEqual(value, Decimal("0.00045"))
        value = PerformanceMetrics.smart_round(Decimal("0.000056789"))
        self.assertEqual(value, Decimal("0.00005678"))
        value = PerformanceMetrics.smart_round(Decimal("0"))
        self.assertEqual(value, Decimal("0"))

        value = PerformanceMetrics.smart_round(Decimal("0.123456"), 2)
        self.assertEqual(value, Decimal("0.12"))
Beispiel #15
0
 def active_positions_df(self) -> pd.DataFrame:
     columns = [
         "Symbol", "Type", "Fee Tier", "Amount", "Upper Price",
         "Lower Price", ""
     ]
     data = []
     if len(self.active_positions) > 0:
         for position in self.active_positions:
             amount = self._base_token_amount if position in self.active_buys else self._quote_token_amount
             data.append([
                 position.trading_pair,
                 "Buy" if position in self.active_buys else "Sell",
                 position.fee_tier,
                 f"{PerformanceMetrics.smart_round(Decimal(str(amount)), 8)}",
                 PerformanceMetrics.smart_round(
                     Decimal(str(position.upper_price)), 8),
                 PerformanceMetrics.smart_round(
                     Decimal(str(position.lower_price)), 8),
                 "[In range]" if self._last_price >= position.lower_price
                 and self._last_price <= position.upper_price else
                 "[Out of range]"
             ])
     return pd.DataFrame(data=data, columns=columns)
Beispiel #16
0
    async def exchange_balances_extra_df(
            self,  # type: HummingbotApplication
            exchange: str,
            ex_balances: Dict[str, Decimal],
            ex_avai_balances: Dict[str, Decimal]):
        conn_setting = AllConnectorSettings.get_connector_settings()[exchange]
        total_col_name = f"Total ({RateOracle.global_token_symbol})"
        allocated_total = Decimal("0")
        rows = []
        for token, bal in ex_balances.items():
            avai = Decimal(ex_avai_balances.get(
                token.upper(),
                0)) if ex_avai_balances is not None else Decimal(0)
            # show zero balances if it is a gateway connector (the user manually
            # chose to show those values with 'gateway connector-tokens')
            if conn_setting.uses_gateway_generic_connector():
                if bal == Decimal(0):
                    allocated = "0%"
                else:
                    allocated = f"{(bal - avai) / bal:.0%}"
            else:
                # the exchange is CEX. Only show balance if non-zero.
                if bal == Decimal(0):
                    continue
                allocated = f"{(bal - avai) / bal:.0%}"

            rate = await RateOracle.global_rate(token)
            rate = Decimal("0") if rate is None else rate
            global_value = rate * bal
            allocated_total += rate * (bal - avai)
            rows.append({
                "Asset":
                token.upper(),
                "Total":
                round(bal, 4),
                total_col_name:
                PerformanceMetrics.smart_round(global_value),
                "sum_not_for_show":
                global_value,
                "Allocated":
                allocated,
            })
        df = pd.DataFrame(data=rows,
                          columns=[
                              "Asset", "Total", total_col_name,
                              "sum_not_for_show", "Allocated"
                          ])
        df.sort_values(by=["Asset"], inplace=True)
        return df, allocated_total
Beispiel #17
0
    def test_performance_metrics(self):
        rate_oracle = RateOracle()
        rate_oracle._prices["USDT-HBOT"] = Decimal("5")
        RateOracle._shared_instance = rate_oracle

        trade_fee = AddedToCostTradeFee(
            flat_fees=[TokenAmount(quote, Decimal("0"))])
        trades = [
            TradeFill(
                config_file_path="some-strategy.yml",
                strategy="pure_market_making",
                market="binance",
                symbol=trading_pair,
                base_asset=base,
                quote_asset=quote,
                timestamp=int(time.time()),
                order_id="someId0",
                trade_type="BUY",
                order_type="LIMIT",
                price=100,
                amount=10,
                trade_fee=trade_fee.to_json(),
                exchange_trade_id="someExchangeId0",
                position=PositionAction.NIL.value,
            ),
            TradeFill(
                config_file_path="some-strategy.yml",
                strategy="pure_market_making",
                market="binance",
                symbol=trading_pair,
                base_asset=base,
                quote_asset=quote,
                timestamp=int(time.time()),
                order_id="someId1",
                trade_type="SELL",
                order_type="LIMIT",
                price=120,
                amount=15,
                trade_fee=trade_fee.to_json(),
                exchange_trade_id="someExchangeId1",
                position=PositionAction.NIL.value,
            )
        ]
        cur_bals = {base: 100, quote: 10000}
        metrics = asyncio.get_event_loop().run_until_complete(
            PerformanceMetrics.create(trading_pair, trades, cur_bals))
        self.assertEqual(Decimal("799"), metrics.trade_pnl)
        print(metrics)
Beispiel #18
0
    async def format_status(self) -> str:
        """
        Returns a status string formatted to display nicely on terminal. The strings composes of 4 parts: market,
        assets, spread and warnings(if any).
        """
        if not self._connector_ready:
            return "UniswapV3 connector not ready."

        columns = ["Exchange", "Market", "Current Price"]
        data = []
        market, trading_pair, base_asset, quote_asset = self._market_info
        data.append([
            market.display_name, trading_pair,
            PerformanceMetrics.smart_round(Decimal(str(self._last_price)), 8)
        ])
        markets_df = pd.DataFrame(data=data, columns=columns)
        lines = []
        lines.extend(["", "  Markets:"] + [
            "    " + line
            for line in markets_df.to_string(index=False).split("\n")
        ])

        # See if there're any active positions.
        if len(self.active_positions) > 0:
            pos_info_df = self.active_positions_df()
            lines.extend(["", "  Positions:"] + [
                "    " + line
                for line in pos_info_df.to_string(index=False).split("\n")
            ])

            pos_profitability_df = self.profitability_df()
            lines.extend(["", "  Positions Performance:"] + [
                "    " + line for line in pos_profitability_df.to_string(
                    index=False).split("\n")
            ])
        else:
            lines.extend(["", "  No active positions."])

        assets_df = self.wallet_balance_data_frame([self._market_info])
        lines.extend(["", "  Assets:"] +
                     ["    " + line for line in str(assets_df).split("\n")])

        warning_lines = self.network_warning([self._market_info])
        warning_lines.extend(self.balance_warning([self._market_info]))
        if len(warning_lines) > 0:
            lines.extend(["", "*** WARNINGS ***"] + warning_lines)

        return "\n".join(lines)
Beispiel #19
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"))
Beispiel #20
0
    def test_aggregated_position_with_no_trades(self):
        aggregated_buys, aggregated_sells = PerformanceMetrics.aggregate_position_order(
            [], [])

        self.assertEqual(len(aggregated_buys), 0)
        self.assertEqual(len(aggregated_sells), 0)
    def report_performance_by_market(
            self,  # type: HummingbotApplication
            market: str,
            trading_pair: str,
            perf: PerformanceMetrics,
            precision: int):
        lines = []
        base, quote = trading_pair.split("-")
        lines.extend([f"\n{market} / {trading_pair}"])

        trades_columns = ["", "buy", "sell", "total"]
        trades_data = [
            [
                f"{'Number of trades':<27}", perf.num_buys, perf.num_sells,
                perf.num_trades
            ],
            [
                f"{f'Total trade volume ({base})':<27}",
                PerformanceMetrics.smart_round(perf.b_vol_base, precision),
                PerformanceMetrics.smart_round(perf.s_vol_base, precision),
                PerformanceMetrics.smart_round(perf.tot_vol_base, precision)
            ],
            [
                f"{f'Total trade volume ({quote})':<27}",
                PerformanceMetrics.smart_round(perf.b_vol_quote, precision),
                PerformanceMetrics.smart_round(perf.s_vol_quote, precision),
                PerformanceMetrics.smart_round(perf.tot_vol_quote, precision)
            ],
            [
                f"{'Avg price':<27}",
                PerformanceMetrics.smart_round(perf.avg_b_price, precision),
                PerformanceMetrics.smart_round(perf.avg_s_price, precision),
                PerformanceMetrics.smart_round(perf.avg_tot_price, precision)
            ],
        ]
        trades_df: pd.DataFrame = pd.DataFrame(data=trades_data,
                                               columns=trades_columns)
        lines.extend(["", "  Trades:"] + [
            "    " + line
            for line in trades_df.to_string(index=False).split("\n")
        ])

        assets_columns = ["", "start", "current", "change"]
        assets_data = [
            [f"{base:<17}", "-", "-", "-"] if market in DERIVATIVES
            else  # No base asset for derivatives because they are margined
            [
                f"{base:<17}",
                PerformanceMetrics.smart_round(perf.start_base_bal, precision),
                PerformanceMetrics.smart_round(perf.cur_base_bal, precision),
                PerformanceMetrics.smart_round(perf.tot_vol_base, precision)
            ],
            [
                f"{quote:<17}",
                PerformanceMetrics.smart_round(perf.start_quote_bal,
                                               precision),
                PerformanceMetrics.smart_round(perf.cur_quote_bal, precision),
                PerformanceMetrics.smart_round(perf.tot_vol_quote, precision)
            ],
            [
                f"{trading_pair + ' price':<17}",
                PerformanceMetrics.smart_round(perf.start_price),
                PerformanceMetrics.smart_round(perf.cur_price),
                PerformanceMetrics.smart_round(perf.cur_price -
                                               perf.start_price)
            ],
            [f"{'Base asset %':<17}", "-", "-", "-"] if market in DERIVATIVES
            else  # No base asset for derivatives because they are margined
            [
                f"{'Base asset %':<17}", f"{perf.start_base_ratio_pct:.2%}",
                f"{perf.cur_base_ratio_pct:.2%}",
                f"{perf.cur_base_ratio_pct - perf.start_base_ratio_pct:.2%}"
            ],
        ]
        assets_df: pd.DataFrame = pd.DataFrame(data=assets_data,
                                               columns=assets_columns)
        lines.extend(["", "  Assets:"] + [
            "    " + line
            for line in assets_df.to_string(index=False).split("\n")
        ])

        perf_data = [
            [
                "Hold portfolio value    ",
                f"{PerformanceMetrics.smart_round(perf.hold_value, precision)} {quote}"
            ],
            [
                "Current portfolio value ",
                f"{PerformanceMetrics.smart_round(perf.cur_value, precision)} {quote}"
            ],
            [
                "Trade P&L               ",
                f"{PerformanceMetrics.smart_round(perf.trade_pnl, precision)} {quote}"
            ]
        ]
        perf_data.extend([
            "Fees paid               ",
            f"{PerformanceMetrics.smart_round(fee_amount, precision)} {fee_token}"
        ] for fee_token, fee_amount in perf.fees.items())
        perf_data.extend([[
            "Total P&L               ",
            f"{PerformanceMetrics.smart_round(perf.total_pnl, precision)} {quote}"
        ], ["Return %                ", f"{perf.return_pct:.2%}"]])
        perf_df: pd.DataFrame = pd.DataFrame(data=perf_data)
        lines.extend(["", "  Performance:"] + [
            "    " + line for line in perf_df.to_string(
                index=False, header=False).split("\n")
        ])

        self._notify("\n".join(lines))
Beispiel #22
0
    async def format_status(self) -> str:
        """
        Returns a status string formatted to display nicely on terminal. The strings composes of 4 parts: markets,
        assets, profitability and warnings(if any).
        """

        if self._all_arb_proposals is None:
            return "  The strategy is not ready, please try again later."
        columns = ["Exchange", "Market", "Sell Price", "Buy Price", "Mid Price"]
        data = []
        for market_info in [self._market_info_1, self._market_info_2]:
            market, trading_pair, base_asset, quote_asset = market_info
            buy_price = await market.get_quote_price(trading_pair, True, self._order_amount)
            sell_price = await market.get_quote_price(trading_pair, False, self._order_amount)

            # check for unavailable price data
            buy_price = PerformanceMetrics.smart_round(Decimal(str(buy_price)), 8) if buy_price is not None else '-'
            sell_price = PerformanceMetrics.smart_round(Decimal(str(sell_price)), 8) if sell_price is not None else '-'
            mid_price = PerformanceMetrics.smart_round(((buy_price + sell_price) / 2), 8) if '-' not in [buy_price, sell_price] else '-'

            data.append([
                market.display_name,
                trading_pair,
                sell_price,
                buy_price,
                mid_price
            ])
        markets_df = pd.DataFrame(data=data, columns=columns)
        lines = []
        lines.extend(["", "  Markets:"] + ["    " + line for line in markets_df.to_string(index=False).split("\n")])

        columns = ["Exchange", "Gas Fees"]
        data = []
        for market_info in [self._market_info_1, self._market_info_2]:
            if hasattr(market_info.market, "network_transaction_fee"):
                transaction_fee: TokenAmount = getattr(market_info.market, "network_transaction_fee")
                data.append([market_info.market.display_name, f"{transaction_fee.amount} {transaction_fee.token}"])
        network_fees_df = pd.DataFrame(data=data, columns=columns)
        if len(data) > 0:
            lines.extend(
                ["", "  Network Fees:"] +
                ["    " + line for line in network_fees_df.to_string(index=False).split("\n")]
            )

        assets_df = self.wallet_balance_data_frame([self._market_info_1, self._market_info_2])
        lines.extend(["", "  Assets:"] +
                     ["    " + line for line in str(assets_df).split("\n")])

        lines.extend(["", "  Profitability:"] + self.short_proposal_msg(self._all_arb_proposals))

        quotes_rates_df = self.quotes_rate_df()
        lines.extend(["", f"  Quotes Rates ({str(self._rate_source)})"] +
                     ["    " + line for line in str(quotes_rates_df).split("\n")])

        warning_lines = self.network_warning([self._market_info_1])
        warning_lines.extend(self.network_warning([self._market_info_2]))
        warning_lines.extend(self.balance_warning([self._market_info_1]))
        warning_lines.extend(self.balance_warning([self._market_info_2]))
        if len(warning_lines) > 0:
            lines.extend(["", "*** WARNINGS ***"] + warning_lines)

        return "\n".join(lines)