Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias
8269384077 fix: missing space in template
closes #12539
2025-11-22 19:51:16 +01:00
Matthias
b364153d02 feat: add nr_of_successful_* to api 2025-11-22 15:46:34 +01:00
Matthias
916d8324cb test: update tests with new fields 2025-11-22 13:48:57 +01:00
Matthias
60309b9e57 chore: move num_entries/exits to trade model 2025-11-22 13:40:57 +01:00
Matthias
a05d142af6 chore: remove unneeded formatting 2025-11-22 13:38:33 +01:00
8 changed files with 21 additions and 15 deletions

View File

@@ -755,6 +755,8 @@ class LocalTrade:
"precision_mode": self.precision_mode,
"precision_mode_price": self.precision_mode_price,
"contract_size": self.contract_size,
"nr_of_successful_entries": self.nr_of_successful_entries,
"nr_of_successful_exits": self.nr_of_successful_exits,
"has_open_orders": self.has_open_orders,
"orders": orders_json,
}

View File

@@ -340,6 +340,8 @@ class TradeSchema(BaseModel):
min_rate: float | None = None
max_rate: float | None = None
nr_of_successful_entries: int
nr_of_successful_exits: int
has_open_orders: bool
orders: list[OrderSchema]

View File

@@ -757,15 +757,7 @@ class Telegram(RPCHandler):
max_entries = self._config.get("max_entry_position_adjustment", -1)
for r in results:
r["open_date_hum"] = dt_humanize_delta(r["open_date"])
r["num_entries"] = len([o for o in r["orders"] if o["ft_is_entry"]])
r["num_exits"] = len(
[
o
for o in r["orders"]
if not o["ft_is_entry"] and not o["ft_order_side"] == "stoploss"
]
)
r["exit_reason"] = r.get("exit_reason", "")
r["stake_amount_r"] = fmt_coin(r["stake_amount"], r["quote_currency"])
r["max_stake_amount_r"] = fmt_coin(
r["max_stake_amount"] or r["stake_amount"], r["quote_currency"]
@@ -784,15 +776,15 @@ class Telegram(RPCHandler):
f"*Amount:* `{r['amount']} ({r['stake_amount_r']})`",
f"*Total invested:* `{r['max_stake_amount_r']}`" if position_adjust else "",
f"*Enter Tag:* `{r['enter_tag']}`" if r["enter_tag"] else "",
f"*Exit Reason:* `{r['exit_reason']}`" if r["exit_reason"] else "",
f"*Exit Reason:* `{r['exit_reason']}`" if r.get("exit_reason") else "",
]
if position_adjust:
max_buy_str = f"/{max_entries + 1}" if (max_entries > 0) else ""
lines.extend(
[
f"*Number of Entries:* `{r['num_entries']}{max_buy_str}`",
f"*Number of Exits:* `{r['num_exits']}`",
f"*Number of Entries:* `{r['nr_of_successful_entries']}{max_buy_str}`",
f"*Number of Exits:* `{r['nr_of_successful_exits']}`",
]
)
@@ -820,7 +812,7 @@ class Telegram(RPCHandler):
f"({r['realized_profit_r']})`",
(
f"*Total Profit:* `{r['total_profit_ratio']:.2%} "
f"({r['total_profit_abs_r']})`",
f"({r['total_profit_abs_r']})`"
),
]
)

View File

@@ -34,7 +34,7 @@
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"exit_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1

View File

@@ -1479,6 +1479,8 @@ def test_to_json(fee):
"contract_size": 1,
"orders": [],
"has_open_orders": False,
"nr_of_successful_entries": 0,
"nr_of_successful_exits": 0,
}
# Simulate dry_run entries
@@ -1570,6 +1572,8 @@ def test_to_json(fee):
"contract_size": 1,
"orders": [],
"has_open_orders": False,
"nr_of_successful_entries": 0,
"nr_of_successful_exits": 0,
}

View File

@@ -99,6 +99,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
"contract_size": 1,
"has_open_orders": False,
"nr_of_successful_entries": ANY,
"nr_of_successful_exits": ANY,
"orders": [
{
"amount": 91.07468123,

View File

@@ -1605,6 +1605,8 @@ def test_api_status(
"precision_mode": None,
"orders": [ANY],
"has_open_orders": True,
"nr_of_successful_entries": ANY,
"nr_of_successful_exits": ANY,
}
mocker.patch(
@@ -1817,6 +1819,8 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
"price_precision": None,
"precision_mode": None,
"has_open_orders": False,
"nr_of_successful_entries": ANY,
"nr_of_successful_exits": ANY,
"orders": [],
}

View File

@@ -421,7 +421,8 @@ async def test_telegram_status_multi_entry(default_conf, update, mocker, fee) ->
assert msg_mock.call_count == 4
msg = msg_mock.call_args_list[3][0][0]
assert re.search(r"Number of Entries.*2", msg)
assert re.search(r"Number of Exits.*1", msg)
# Exit order is still open, hence not a successful exit
assert re.search(r"Number of Exits.*0", msg)
assert re.search(r"Close Date:", msg) is None
assert re.search(r"Close Profit:", msg) is None