Merge pull request #5273 from freqtrade/fix/profit_calc

fix calculation logic for /profit
This commit is contained in:
Matthias 2021-07-16 19:19:28 +02:00 committed by GitHub
commit 4899b06b31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 73 additions and 9 deletions

View File

@ -245,10 +245,10 @@ current max
Return a summary of your profit/loss and performance. Return a summary of your profit/loss and performance.
> **ROI:** Close trades > **ROI:** Close trades
> ∙ `0.00485701 BTC (258.45%)` > ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)`
> ∙ `62.968 USD` > ∙ `62.968 USD`
> **ROI:** All trades > **ROI:** All trades
> ∙ `0.00255280 BTC (143.43%)` > ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)`
> ∙ `33.095 EUR` > ∙ `33.095 EUR`
> >
> **Total Trade Count:** `138` > **Total Trade Count:** `138`
@ -257,6 +257,10 @@ Return a summary of your profit/loss and performance.
> **Avg. Duration:** `2:33:45` > **Avg. Duration:** `2:33:45`
> **Best Performing:** `PAY/BTC: 50.23%` > **Best Performing:** `PAY/BTC: 50.23%`
The relative profit of `1.2%` is the average profit per trade.
The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`.
Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits.
### /forcesell <trade_id> ### /forcesell <trade_id>
> **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)` > **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)`

View File

@ -67,12 +67,16 @@ class Profit(BaseModel):
profit_closed_ratio_mean: float profit_closed_ratio_mean: float
profit_closed_percent_sum: float profit_closed_percent_sum: float
profit_closed_ratio_sum: float profit_closed_ratio_sum: float
profit_closed_percent: float
profit_closed_ratio: float
profit_closed_fiat: float profit_closed_fiat: float
profit_all_coin: float profit_all_coin: float
profit_all_percent_mean: float profit_all_percent_mean: float
profit_all_ratio_mean: float profit_all_ratio_mean: float
profit_all_percent_sum: float profit_all_percent_sum: float
profit_all_ratio_sum: float profit_all_ratio_sum: float
profit_all_percent: float
profit_all_ratio: float
profit_all_fiat: float profit_all_fiat: float
trade_count: int trade_count: int
closed_trade_count: int closed_trade_count: int
@ -115,6 +119,7 @@ class ShowConfig(BaseModel):
dry_run: bool dry_run: bool
stake_currency: str stake_currency: str
stake_amount: Union[float, str] stake_amount: Union[float, str]
available_capital: Optional[float]
stake_currency_decimals: int stake_currency_decimals: int
max_open_trades: int max_open_trades: int
minimal_roi: Dict[str, Any] minimal_roi: Dict[str, Any]

View File

@ -106,6 +106,7 @@ class RPC:
'stake_currency': config['stake_currency'], 'stake_currency': config['stake_currency'],
'stake_currency_decimals': decimals_per_coin(config['stake_currency']), 'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
'stake_amount': config['stake_amount'], 'stake_amount': config['stake_amount'],
'available_capital': config.get('available_capital'),
'max_open_trades': (config['max_open_trades'] 'max_open_trades': (config['max_open_trades']
if config['max_open_trades'] != float('inf') else -1), if config['max_open_trades'] != float('inf') else -1),
'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {}, 'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
@ -396,7 +397,12 @@ class RPC:
profit_all_coin_sum = round(sum(profit_all_coin), 8) profit_all_coin_sum = round(sum(profit_all_coin), 8)
profit_all_ratio_mean = float(mean(profit_all_ratio) if profit_all_ratio else 0.0) profit_all_ratio_mean = float(mean(profit_all_ratio) if profit_all_ratio else 0.0)
# Doing the sum is not right - overall profit needs to be based on initial capital
profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0 profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0
starting_balance = self._freqtrade.wallets.get_starting_balance()
profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance
profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance
profit_all_fiat = self._fiat_converter.convert_amount( profit_all_fiat = self._fiat_converter.convert_amount(
profit_all_coin_sum, profit_all_coin_sum,
stake_currency, stake_currency,
@ -412,12 +418,16 @@ class RPC:
'profit_closed_ratio_mean': profit_closed_ratio_mean, 'profit_closed_ratio_mean': profit_closed_ratio_mean,
'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2), 'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2),
'profit_closed_ratio_sum': profit_closed_ratio_sum, 'profit_closed_ratio_sum': profit_closed_ratio_sum,
'profit_closed_ratio': profit_closed_ratio_fromstart,
'profit_closed_percent': round(profit_closed_ratio_fromstart * 100, 2),
'profit_closed_fiat': profit_closed_fiat, 'profit_closed_fiat': profit_closed_fiat,
'profit_all_coin': profit_all_coin_sum, 'profit_all_coin': profit_all_coin_sum,
'profit_all_percent_mean': round(profit_all_ratio_mean * 100, 2), 'profit_all_percent_mean': round(profit_all_ratio_mean * 100, 2),
'profit_all_ratio_mean': profit_all_ratio_mean, 'profit_all_ratio_mean': profit_all_ratio_mean,
'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2), 'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2),
'profit_all_ratio_sum': profit_all_ratio_sum, 'profit_all_ratio_sum': profit_all_ratio_sum,
'profit_all_ratio': profit_all_ratio_fromstart,
'profit_all_percent': round(profit_all_ratio_fromstart * 100, 2),
'profit_all_fiat': profit_all_fiat, 'profit_all_fiat': profit_all_fiat,
'trade_count': len(trades), 'trade_count': len(trades),
'closed_trade_count': len([t for t in trades if not t.is_open]), 'closed_trade_count': len([t for t in trades if not t.is_open]),

View File

@ -494,11 +494,11 @@ class Telegram(RPCHandler):
start_date) start_date)
profit_closed_coin = stats['profit_closed_coin'] profit_closed_coin = stats['profit_closed_coin']
profit_closed_percent_mean = stats['profit_closed_percent_mean'] profit_closed_percent_mean = stats['profit_closed_percent_mean']
profit_closed_percent_sum = stats['profit_closed_percent_sum'] profit_closed_percent = stats['profit_closed_percent']
profit_closed_fiat = stats['profit_closed_fiat'] profit_closed_fiat = stats['profit_closed_fiat']
profit_all_coin = stats['profit_all_coin'] profit_all_coin = stats['profit_all_coin']
profit_all_percent_mean = stats['profit_all_percent_mean'] profit_all_percent_mean = stats['profit_all_percent_mean']
profit_all_percent_sum = stats['profit_all_percent_sum'] profit_all_percent = stats['profit_all_percent']
profit_all_fiat = stats['profit_all_fiat'] profit_all_fiat = stats['profit_all_fiat']
trade_count = stats['trade_count'] trade_count = stats['trade_count']
first_trade_date = stats['first_trade_date'] first_trade_date = stats['first_trade_date']
@ -514,7 +514,7 @@ class Telegram(RPCHandler):
markdown_msg = ("*ROI:* Closed trades\n" markdown_msg = ("*ROI:* Closed trades\n"
f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} " f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} "
f"({profit_closed_percent_mean:.2f}%) " f"({profit_closed_percent_mean:.2f}%) "
f"({profit_closed_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"({profit_closed_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n") f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n")
else: else:
markdown_msg = "`No closed trade` \n" markdown_msg = "`No closed trade` \n"
@ -523,7 +523,7 @@ class Telegram(RPCHandler):
f"*ROI:* All trades\n" f"*ROI:* All trades\n"
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} " f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
f"({profit_all_percent_mean:.2f}%) " f"({profit_all_percent_mean:.2f}%) "
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"({profit_all_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n" f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
f"*Total Trade Count:* `{trade_count}`\n" f"*Total Trade Count:* `{trade_count}`\n"
f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* " f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* "

View File

@ -129,6 +129,19 @@ class Wallets:
def get_all_balances(self) -> Dict[str, Any]: def get_all_balances(self) -> Dict[str, Any]:
return self._wallets return self._wallets
def get_starting_balance(self) -> float:
"""
Retrieves starting balance - based on either available capital,
or by using current balance subtracting
"""
if "available_capital" in self._config:
return self._config['available_capital']
else:
tot_profit = Trade.get_total_closed_profit()
open_stakes = Trade.total_open_trades_stakes()
available_balance = self.get_free(self._config['stake_currency'])
return available_balance - tot_profit + open_stakes
def get_total_stake_amount(self): def get_total_stake_amount(self):
""" """
Return the total currently available balance in stake currency, including tied up stake and Return the total currently available balance in stake currency, including tied up stake and

View File

@ -677,12 +677,16 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
'profit_all_ratio_mean': -0.6641100666666667, 'profit_all_ratio_mean': -0.6641100666666667,
'profit_all_percent_sum': -398.47, 'profit_all_percent_sum': -398.47,
'profit_all_ratio_sum': -3.9846604, 'profit_all_ratio_sum': -3.9846604,
'profit_all_percent': -4.41,
'profit_all_ratio': -0.044063014216106644,
'profit_closed_coin': 0.00073913, 'profit_closed_coin': 0.00073913,
'profit_closed_fiat': 9.124559849999999, 'profit_closed_fiat': 9.124559849999999,
'profit_closed_ratio_mean': 0.0075, 'profit_closed_ratio_mean': 0.0075,
'profit_closed_percent_mean': 0.75, 'profit_closed_percent_mean': 0.75,
'profit_closed_ratio_sum': 0.015, 'profit_closed_ratio_sum': 0.015,
'profit_closed_percent_sum': 1.5, 'profit_closed_percent_sum': 1.5,
'profit_closed_ratio': 7.391275897987988e-07,
'profit_closed_percent': 0.0,
'trade_count': 6, 'trade_count': 6,
'closed_trade_count': 2, 'closed_trade_count': 2,
'winning_trades': 2, 'winning_trades': 2,

View File

@ -452,7 +452,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'No closed trade' in msg_mock.call_args_list[-1][0][0] assert 'No closed trade' in msg_mock.call_args_list[-1][0][0]
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.5 \N{GREEK CAPITAL LETTER SIGMA}%)`' mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01)
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0]) in msg_mock.call_args_list[-1][0][0])
msg_mock.reset_mock() msg_mock.reset_mock()
@ -466,11 +467,11 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
telegram._profit(update=update, context=MagicMock()) telegram._profit(update=update, context=MagicMock())
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`' assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0]) in msg_mock.call_args_list[-1][0][0])
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`' assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0]) in msg_mock.call_args_list[-1][0][0])
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]

View File

@ -197,3 +197,30 @@ def test__validate_stake_amount(mocker, default_conf,
return_value=max_stake_amount) return_value=max_stake_amount)
res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount) res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
assert res == expected assert res == expected
@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [
(None, 10, 100, 910, 1000),
(None, 0, 0, 2500, 2500),
(None, 500, 0, 2500, 2000),
(None, 500, 0, 2500, 2000),
(None, -70, 0, 1930, 2000),
# Only available balance matters when it's set.
(100, 0, 0, 0, 100),
(1000, 0, 2, 5, 1000),
(1235, 2250, 2, 5, 1235),
(1235, -2250, 2, 5, 1235),
])
def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit,
open_stakes, free, expected):
if available_capital:
default_conf['available_capital'] = available_capital
mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit",
return_value=closed_profit)
mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes",
return_value=open_stakes)
mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert freqtrade.wallets.get_starting_balance() == expected