Trade journal: momentum journal command + nightly close capture

momentum journal prints the day's trades (time/symbol/qty/entry/exit/P&L) +
broker-accurate day P&L + win summary. journal.sh appends it to
data/trade_journal.log; cron 16:02 weekdays keeps a durable daily record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Opus 4.8 committed 2026-06-17 17:54:24 -04:00
1 parent c35224af94
commit 8a24226cfb
2 files changed
+45

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Capture the day's trade journal at the close into a durable daily record.
# crontab: 2 16 * * 1-5 /mnt/c/code/momentum/journal.sh
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_PY="/home/philip/.venvs/momentum/bin/python"
cd "$ROOT" || exit 1
mkdir -p "$ROOT/data"
{ echo; echo "=================================================="; date; } >> "$ROOT/data/trade_journal.log"
PYTHONPATH="$ROOT" TERM=dumb "$VENV_PY" momentum_cli.py journal >> "$ROOT/data/trade_journal.log" 2>&1
+36
View File
@@ -630,6 +630,42 @@ def _render_board(con):
return Group(header, tt, st, pt, rk) return Group(header, tt, st, pt, rk)
@app.command()
def journal():
"""Print the day's trade journal (today's trades + day P&L) — for the close
capture and the nightly record."""
from datetime import datetime
try:
from zoneinfo import ZoneInfo
today = datetime.now(ZoneInfo("America/New_York")).date().isoformat()
except Exception: # noqa: BLE001
today = datetime.now().date().isoformat()
con = _con()
trades, realized = _todays_trades(con)
day_pnl = _day_pnl(con)
console.print(f"[bold]=== trade journal {today} ===[/bold]")
if day_pnl is not None:
console.print(f"day P&L: [{'green' if day_pnl >= 0 else 'red'}]{day_pnl:+,.0f}[/]"
f" ([dim]matched {realized:+,.0f}[/])")
if not trades:
console.print("no trades today.")
return
t = Table(box=box.SIMPLE)
for c in ("time", "symbol", "qty", "entry", "exit", "P&L", "status"):
t.add_column(c, justify="left" if c in ("symbol", "status") else "right")
for tr in sorted(trades, key=lambda x: x["time"]):
pnl = tr["pnl"]
t.add_row(tr["time"], str(tr["symbol"]), f"{tr['qty']:.0f}", f"{tr['entry']:.2f}",
f"{tr['exit']:.2f}" if tr["exit"] else "—",
f"{pnl:+.0f}" if pnl is not None else "—", tr["status"])
console.print(t)
closed = [tr for tr in trades if tr["status"] == "closed"]
if closed:
wins = sum(1 for tr in closed if (tr["pnl"] or 0) > 0)
console.print(f"[dim]{len(closed)} closed · {wins} win ({wins / len(closed) * 100:.0f}%) · "
f"{len(trades) - len(closed)} still open[/dim]")
@app.command() @app.command()
def watch(interval: float = 3.0, once: bool = typer.Option(False, help="render one frame and exit")): def watch(interval: float = 3.0, once: bool = typer.Option(False, help="render one frame and exit")):
"""LIVE board: the symbols being evaluated + their determinations, refreshing.""" """LIVE board: the symbols being evaluated + their determinations, refreshing."""