mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge pull request #8537 from freqtrade/feat/balance_improve
Improve balance output
This commit is contained in:
commit
4690810d5d
|
@ -191,7 +191,8 @@ official commands. You can ask at any moment for help with `/help`.
|
|||
| **Metrics** |
|
||||
| `/profit [<n>]` | Display a summary of your profit/loss from close trades and some stats about your performance, over the last n days (all trades by default)
|
||||
| `/performance` | Show performance of each finished trade grouped by pair
|
||||
| `/balance` | Show account balance per currency
|
||||
| `/balance` | Show bot managed balance per currency
|
||||
| `/balance full` | Show account balance per currency
|
||||
| `/daily <n>` | Shows profit or loss per day, over the last n days (n defaults to 7)
|
||||
| `/weekly <n>` | Shows profit or loss per week, over the last n weeks (n defaults to 8)
|
||||
| `/monthly <n>` | Shows profit or loss per month, over the last n months (n defaults to 6)
|
||||
|
@ -202,7 +203,6 @@ official commands. You can ask at any moment for help with `/help`.
|
|||
| `/blacklist [pair]` | Show the current blacklist, or adds a pair to the blacklist.
|
||||
| `/edge` | Show validated pairs by Edge if it is enabled.
|
||||
|
||||
|
||||
## Telegram commands in action
|
||||
|
||||
Below, example of Telegram message you will receive for each command.
|
||||
|
|
|
@ -36,20 +36,25 @@ class Balance(BaseModel):
|
|||
free: float
|
||||
balance: float
|
||||
used: float
|
||||
bot_owned: Optional[float]
|
||||
est_stake: float
|
||||
est_stake_bot: Optional[float]
|
||||
stake: str
|
||||
# Starting with 2.x
|
||||
side: str
|
||||
leverage: float
|
||||
is_position: bool
|
||||
position: float
|
||||
is_bot_managed: bool
|
||||
|
||||
|
||||
class Balances(BaseModel):
|
||||
currencies: List[Balance]
|
||||
total: float
|
||||
total_bot: float
|
||||
symbol: str
|
||||
value: float
|
||||
value_bot: float
|
||||
stake: str
|
||||
note: str
|
||||
starting_capital: float
|
||||
|
|
|
@ -43,7 +43,8 @@ logger = logging.getLogger(__name__)
|
|||
# 2.23: Allow plot config request in webserver mode
|
||||
# 2.24: Add cancel_open_order endpoint
|
||||
# 2.25: Add several profit values to /status endpoint
|
||||
API_VERSION = 2.25
|
||||
# 2.26: increase /balance output
|
||||
API_VERSION = 2.26
|
||||
|
||||
# Public API, requires no auth.
|
||||
router_public = APIRouter()
|
||||
|
|
|
@ -583,13 +583,16 @@ class RPC:
|
|||
}
|
||||
|
||||
def __balance_get_est_stake(
|
||||
self, coin: str, stake_currency: str, balance: Wallet, tickers) -> float:
|
||||
self, coin: str, stake_currency: str, amount: float,
|
||||
balance: Wallet, tickers) -> Tuple[float, float]:
|
||||
est_stake = 0.0
|
||||
est_bot_stake = 0.0
|
||||
if coin == stake_currency:
|
||||
est_stake = balance.total
|
||||
if self._config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
|
||||
# in Futures, "total" includes the locked stake, and therefore all positions
|
||||
est_stake = balance.free
|
||||
est_bot_stake = amount
|
||||
else:
|
||||
try:
|
||||
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
|
||||
|
@ -598,11 +601,12 @@ class RPC:
|
|||
if pair.startswith(stake_currency) and not pair.endswith(stake_currency):
|
||||
rate = 1.0 / rate
|
||||
est_stake = rate * balance.total
|
||||
est_bot_stake = rate * amount
|
||||
except (ExchangeError):
|
||||
logger.warning(f"Could not get rate for pair {coin}.")
|
||||
raise ValueError()
|
||||
|
||||
return est_stake
|
||||
return est_stake, est_bot_stake
|
||||
|
||||
def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict:
|
||||
""" Returns current account balance per crypto """
|
||||
|
@ -615,7 +619,7 @@ class RPC:
|
|||
raise RPCException('Error getting current tickers.')
|
||||
|
||||
open_trades: List[Trade] = Trade.get_open_trades()
|
||||
open_assets = [t.base_currency for t in open_trades]
|
||||
open_assets: Dict[str, Trade] = {t.safe_base_currency: t for t in open_trades}
|
||||
self._freqtrade.wallets.update(require_update=False)
|
||||
starting_capital = self._freqtrade.wallets.get_starting_balance()
|
||||
starting_cap_fiat = self._fiat_converter.convert_amount(
|
||||
|
@ -625,30 +629,43 @@ class RPC:
|
|||
for coin, balance in self._freqtrade.wallets.get_all_balances().items():
|
||||
if not balance.total:
|
||||
continue
|
||||
|
||||
trade = open_assets.get(coin, None)
|
||||
is_bot_managed = coin == stake_currency or trade is not None
|
||||
trade_amount = trade.amount if trade else 0
|
||||
if coin == stake_currency:
|
||||
trade_amount = self._freqtrade.wallets.get_available_stake_amount()
|
||||
|
||||
try:
|
||||
est_stake = self.__balance_get_est_stake(coin, stake_currency, balance, tickers)
|
||||
est_stake, est_stake_bot = self.__balance_get_est_stake(
|
||||
coin, stake_currency, trade_amount, balance, tickers)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
total += est_stake
|
||||
if coin == stake_currency or coin in open_assets:
|
||||
total_bot += est_stake
|
||||
|
||||
if is_bot_managed:
|
||||
total_bot += est_stake_bot
|
||||
currencies.append({
|
||||
'currency': coin,
|
||||
'free': balance.free,
|
||||
'balance': balance.total,
|
||||
'used': balance.used,
|
||||
'bot_owned': trade_amount,
|
||||
'est_stake': est_stake or 0,
|
||||
'est_stake_bot': est_stake_bot if is_bot_managed else 0,
|
||||
'stake': stake_currency,
|
||||
'side': 'long',
|
||||
'leverage': 1,
|
||||
'position': 0,
|
||||
'is_bot_managed': is_bot_managed,
|
||||
'is_position': False,
|
||||
})
|
||||
symbol: str
|
||||
position: PositionWallet
|
||||
for symbol, position in self._freqtrade.wallets.get_all_positions().items():
|
||||
total += position.collateral
|
||||
total_bot += position.collateral
|
||||
|
||||
currencies.append({
|
||||
'currency': symbol,
|
||||
|
@ -657,9 +674,11 @@ class RPC:
|
|||
'used': 0,
|
||||
'position': position.position,
|
||||
'est_stake': position.collateral,
|
||||
'est_stake_bot': position.collateral,
|
||||
'stake': stake_currency,
|
||||
'leverage': position.leverage,
|
||||
'side': position.side,
|
||||
'is_bot_managed': True,
|
||||
'is_position': True
|
||||
})
|
||||
|
||||
|
@ -675,8 +694,10 @@ class RPC:
|
|||
return {
|
||||
'currencies': currencies,
|
||||
'total': total,
|
||||
'total_bot': total_bot,
|
||||
'symbol': fiat_display_currency,
|
||||
'value': value,
|
||||
'value_bot': value_bot,
|
||||
'stake': stake_currency,
|
||||
'starting_capital': starting_capital,
|
||||
'starting_capital_ratio': starting_capital_ratio,
|
||||
|
|
|
@ -905,6 +905,7 @@ class Telegram(RPCHandler):
|
|||
@authorized_only
|
||||
def _balance(self, update: Update, context: CallbackContext) -> None:
|
||||
""" Handler for /balance """
|
||||
full_result = context.args and 'full' in context.args
|
||||
result = self._rpc._rpc_balance(self._config['stake_currency'],
|
||||
self._config.get('fiat_display_currency', ''))
|
||||
|
||||
|
@ -915,8 +916,7 @@ class Telegram(RPCHandler):
|
|||
output = ''
|
||||
if self._config['dry_run']:
|
||||
output += "*Warning:* Simulated balances in Dry Mode.\n"
|
||||
starting_cap = round_coin_value(
|
||||
result['starting_capital'], self._config['stake_currency'])
|
||||
starting_cap = round_coin_value(result['starting_capital'], self._config['stake_currency'])
|
||||
output += f"Starting capital: `{starting_cap}`"
|
||||
starting_cap_fiat = round_coin_value(
|
||||
result['starting_capital_fiat'], self._config['fiat_display_currency']
|
||||
|
@ -928,7 +928,10 @@ class Telegram(RPCHandler):
|
|||
total_dust_currencies = 0
|
||||
for curr in result['currencies']:
|
||||
curr_output = ''
|
||||
if curr['est_stake'] > balance_dust_level:
|
||||
if (
|
||||
(curr['is_position'] or curr['est_stake'] > balance_dust_level)
|
||||
and (full_result or curr['is_bot_managed'])
|
||||
):
|
||||
if curr['is_position']:
|
||||
curr_output = (
|
||||
f"*{curr['currency']}:*\n"
|
||||
|
@ -937,13 +940,17 @@ class Telegram(RPCHandler):
|
|||
f"\t`Est. {curr['stake']}: "
|
||||
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
|
||||
else:
|
||||
est_stake = round_coin_value(
|
||||
curr['est_stake' if full_result else 'est_stake_bot'], curr['stake'], False)
|
||||
|
||||
curr_output = (
|
||||
f"*{curr['currency']}:*\n"
|
||||
f"\t`Available: {curr['free']:.8f}`\n"
|
||||
f"\t`Balance: {curr['balance']:.8f}`\n"
|
||||
f"\t`Pending: {curr['used']:.8f}`\n"
|
||||
f"\t`Est. {curr['stake']}: "
|
||||
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
|
||||
f"\t`Bot Owned: {curr['bot_owned']:.8f}`\n"
|
||||
f"\t`Est. {curr['stake']}: {est_stake}`\n")
|
||||
|
||||
elif curr['est_stake'] <= balance_dust_level:
|
||||
total_dust_balance += curr['est_stake']
|
||||
total_dust_currencies += 1
|
||||
|
@ -965,14 +972,15 @@ class Telegram(RPCHandler):
|
|||
tc = result['trade_count'] > 0
|
||||
stake_improve = f" `({result['starting_capital_ratio']:.2%})`" if tc else ''
|
||||
fiat_val = f" `({result['starting_capital_fiat_ratio']:.2%})`" if tc else ''
|
||||
|
||||
output += ("\n*Estimated Value*:\n"
|
||||
f"\t`{result['stake']}: "
|
||||
f"{round_coin_value(result['total'], result['stake'], False)}`"
|
||||
f"{stake_improve}\n"
|
||||
f"\t`{result['symbol']}: "
|
||||
f"{round_coin_value(result['value'], result['symbol'], False)}`"
|
||||
f"{fiat_val}\n")
|
||||
value = round_coin_value(
|
||||
result['value' if full_result else 'value_bot'], result['symbol'], False)
|
||||
total_stake = round_coin_value(
|
||||
result['total' if full_result else 'total_bot'], result['stake'], False)
|
||||
output += (
|
||||
f"\n*Estimated Value{' (Bot managed assets only)' if not full_result else ''}*:\n"
|
||||
f"\t`{result['stake']}: {total_stake}`{stake_improve}\n"
|
||||
f"\t`{result['symbol']}: {value}`{fiat_val}\n"
|
||||
)
|
||||
self._send_msg(output, reload_able=True, callback_path="update_balance",
|
||||
query=update.callback_query)
|
||||
|
||||
|
@ -1528,7 +1536,8 @@ class Telegram(RPCHandler):
|
|||
"------------\n"
|
||||
"*/show_config:* `Show running configuration` \n"
|
||||
"*/locks:* `Show currently locked pairs`\n"
|
||||
"*/balance:* `Show account balance per currency`\n"
|
||||
"*/balance:* `Show bot managed balance per currency`\n"
|
||||
"*/balance total:* `Show account balance per currency`\n"
|
||||
"*/logs [limit]:* `Show latest logs - defaults to 10` \n"
|
||||
"*/count:* `Show number of active trades compared to allowed number of trades`\n"
|
||||
"*/edge:* `Shows validated pairs by Edge if it is enabled` \n"
|
||||
|
|
|
@ -546,53 +546,67 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
|||
'free': 10.0,
|
||||
'balance': 12.0,
|
||||
'used': 2.0,
|
||||
'bot_owned': 9.9, # available stake - reducing by reserved amount
|
||||
'est_stake': 10.0, # In futures mode, "free" is used here.
|
||||
'est_stake_bot': 9.9,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': True,
|
||||
},
|
||||
{
|
||||
'free': 1.0,
|
||||
'balance': 5.0,
|
||||
'currency': 'ETH',
|
||||
'bot_owned': 0,
|
||||
'est_stake': 0.30794,
|
||||
'est_stake_bot': 0,
|
||||
'used': 4.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
|
||||
'is_bot_managed': False,
|
||||
},
|
||||
{
|
||||
'free': 5.0,
|
||||
'balance': 10.0,
|
||||
'currency': 'USDT',
|
||||
'bot_owned': 0,
|
||||
'est_stake': 0.0011562404610161968,
|
||||
'est_stake_bot': 0,
|
||||
'used': 5.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': False,
|
||||
},
|
||||
{
|
||||
'free': 0.0,
|
||||
'balance': 0.0,
|
||||
'currency': 'ETH/USDT:USDT',
|
||||
'est_stake': 20,
|
||||
'est_stake_bot': 20,
|
||||
'used': 0,
|
||||
'stake': 'BTC',
|
||||
'is_position': True,
|
||||
'leverage': 5.0,
|
||||
'position': 1000.0,
|
||||
'side': 'short',
|
||||
'is_bot_managed': True,
|
||||
}
|
||||
]
|
||||
assert pytest.approx(result['total_bot']) == 29.9
|
||||
assert pytest.approx(result['total']) == 30.309096
|
||||
assert result['starting_capital'] == 10
|
||||
assert result['starting_capital_ratio'] == 0.0
|
||||
# Very high starting capital ratio, because the futures position really has the wrong unit.
|
||||
# TODO: improve this test (see comment above)
|
||||
assert result['starting_capital_ratio'] == pytest.approx(1.98999999)
|
||||
|
||||
|
||||
def test_rpc_start(mocker, default_conf) -> None:
|
||||
|
|
|
@ -480,13 +480,18 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
|||
'free': 12.0,
|
||||
'balance': 12.0,
|
||||
'used': 0.0,
|
||||
'bot_owned': pytest.approx(11.879999),
|
||||
'est_stake': 12.0,
|
||||
'est_stake_bot': pytest.approx(11.879999),
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': True,
|
||||
}
|
||||
assert response['total'] == 12.159513094
|
||||
assert response['total_bot'] == pytest.approx(11.879999)
|
||||
assert 'starting_capital' in response
|
||||
assert 'starting_capital_fiat' in response
|
||||
assert 'starting_capital_pct' in response
|
||||
|
|
|
@ -783,19 +783,28 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick
|
|||
patch_get_signal(freqtradebot)
|
||||
|
||||
telegram._balance(update=update, context=MagicMock())
|
||||
context = MagicMock()
|
||||
context.args = ["full"]
|
||||
telegram._balance(update=update, context=context)
|
||||
result = msg_mock.call_args_list[0][0][0]
|
||||
assert msg_mock.call_count == 1
|
||||
result_full = msg_mock.call_args_list[1][0][0]
|
||||
assert msg_mock.call_count == 2
|
||||
assert '*BTC:*' in result
|
||||
assert '*ETH:*' not in result
|
||||
assert '*USDT:*' not in result
|
||||
assert '*EUR:*' not in result
|
||||
assert '*LTC:*' in result
|
||||
assert '*LTC:*' not in result
|
||||
|
||||
assert '*LTC:*' in result_full
|
||||
assert '*XRP:*' not in result
|
||||
assert 'Balance:' in result
|
||||
assert 'Est. BTC:' in result
|
||||
assert 'BTC: 12' in result
|
||||
assert 'BTC: 11' in result
|
||||
assert 'BTC: 12' in result_full
|
||||
assert "*3 Other Currencies (< 0.0001 BTC):*" in result
|
||||
assert 'BTC: 0.00000309' in result
|
||||
assert '*Estimated Value*:' in result_full
|
||||
assert '*Estimated Value (Bot managed assets only)*:' in result
|
||||
|
||||
|
||||
def test_balance_handle_empty_response(default_conf, update, mocker) -> None:
|
||||
|
@ -834,18 +843,23 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None
|
|||
'free': 1.0,
|
||||
'used': 0.5,
|
||||
'balance': i,
|
||||
'bot_owned': 0.5,
|
||||
'est_stake': 1,
|
||||
'est_stake_bot': 1,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': True,
|
||||
})
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={
|
||||
'currencies': balances,
|
||||
'total': 100.0,
|
||||
'total_bot': 100.0,
|
||||
'symbol': 100.0,
|
||||
'value': 1000.0,
|
||||
'value_bot': 1000.0,
|
||||
'starting_capital': 1000,
|
||||
'starting_capital_fiat': 1000,
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue
Block a user