feat: show max-hold session countdown
This commit is contained in:
@@ -53,3 +53,7 @@ class PaperTradeResponse(BaseModel):
|
||||
# when the trailing exit policy is active.
|
||||
trailing_stop: float | None = None
|
||||
trailing_distance_pct: float | None = None
|
||||
# Trading sessions represented by post-entry OHLCV bars. These are populated
|
||||
# only while the active exit policy has a max-hold rule.
|
||||
sessions_held: int | None = None
|
||||
sessions_remaining: int | None = None
|
||||
|
||||
@@ -352,6 +352,7 @@ def _to_dict(
|
||||
current_price: float | None,
|
||||
benchmark_closes: dict[date, float] | None = None,
|
||||
trailing: tuple[float, float | None] | None = None,
|
||||
holding_sessions: tuple[int, int] | None = None,
|
||||
) -> dict:
|
||||
# For open trades, mark to market; for closed, the realized exit price.
|
||||
ref = current_price if trade.status == "open" else trade.close_price
|
||||
@@ -395,6 +396,8 @@ def _to_dict(
|
||||
"fill_mode": trade.fill_mode,
|
||||
"trailing_stop": trailing[0] if trailing else None,
|
||||
"trailing_distance_pct": trailing[1] if trailing else None,
|
||||
"sessions_held": holding_sessions[0] if holding_sessions else None,
|
||||
"sessions_remaining": holding_sessions[1] if holding_sessions else None,
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +438,26 @@ async def list_trades(
|
||||
# Current trailing-stop level + distance for open trades (when a trailing
|
||||
# policy is active).
|
||||
policy = await get_exit_policy(db)
|
||||
holding_sessions: dict[int, tuple[int, int]] = {}
|
||||
if policy["mode"] in ("time", "atr_trailing"):
|
||||
hold_days = int(policy["hold_days"])
|
||||
for t, _ in rows:
|
||||
if t.status != "open":
|
||||
continue
|
||||
held = int(
|
||||
(
|
||||
await db.execute(
|
||||
select(func.count())
|
||||
.select_from(OHLCVRecord)
|
||||
.where(
|
||||
OHLCVRecord.ticker_id == t.ticker_id,
|
||||
OHLCVRecord.date > t.opened_at.date(),
|
||||
)
|
||||
)
|
||||
).scalar_one()
|
||||
)
|
||||
holding_sessions[t.id] = (held, max(0, hold_days - held))
|
||||
|
||||
trailing_info: dict[int, tuple[float, float | None]] = {}
|
||||
if policy["mode"] == "trailing":
|
||||
trail_frac = policy["trailing_pct"] / 100.0
|
||||
@@ -483,7 +506,14 @@ async def list_trades(
|
||||
trailing_info[t.id] = (level, dist)
|
||||
|
||||
return [
|
||||
_to_dict(t, sym, prices.get(t.ticker_id), benchmark_closes, trailing_info.get(t.id))
|
||||
_to_dict(
|
||||
t,
|
||||
sym,
|
||||
prices.get(t.ticker_id),
|
||||
benchmark_closes,
|
||||
trailing_info.get(t.id),
|
||||
holding_sessions.get(t.id),
|
||||
)
|
||||
for t, sym in rows
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user