Skip to content

Feat/evening pnl equity backfill (daily-only P&L export) - #99

Merged
yebof merged 5 commits into
mainfrom
feat/evening-pnl-equity-backfill
Jun 10, 2026
Merged

Feat/evening pnl equity backfill (daily-only P&L export)#99
yebof merged 5 commits into
mainfrom
feat/evening-pnl-equity-backfill

Conversation

@diana-jydai

@diana-jydai diana-jydai commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Evening TG notification no longer includes the P&L history text table (replaced by daily CSV export)
  • db.backfill_equity_close(): automatically backfills NULL equity_close rows caused by Alpaca API lag, triggered each evening run
  • P&L CSV export is now daily-only (daily flow, Mon–Fri at 09:00 ET)
  • Removed legacy weekly compatibility paths (--mode weekly, run_weekly() alias, and weekly formatter/CSV aliases)

Deploy steps (run on different server after merge)

1. Find your existing weekly CSV timer/service (name may vary by deployment)

systemctl --user list-timers | grep -i weekly
# or
ls ~/.config/systemd/user/ | grep -i weekly

2. Migrate to daily timer/service and remove weekly mode invocations

  • Create/update timer/service to call --mode daily
  • Remove old weekly timer/service units after verifying daily runs successfully

diana-jydai and others added 2 commits May 30, 2026 13:26
- broker: get_full_portfolio_history() — full 1D equity history from
  Alpaca portfolio_history (5-year lookback, skips pre-funding rows)
- notifier: send_document() — sends CSV file via Telegram sendDocument API
- notifier: build_weekly_csv() — Date/NAV/Daily P&L/Return%/Drawdown%/SPY
  columns; SPY data from yfinance; degrades gracefully on failure
- pipeline: run_weekly() — orchestrates fetch → CSV → Telegram send
- main: --mode weekly added to choices

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fill; rename weekly→daily CSV

- Evening TG notification no longer includes the P&L history text table
- db.backfill_equity_close(): backfills NULL equity_close rows caused by
  Alpaca API lag; triggered each evening run for the prior N-day window
- Renamed weekly → daily: P&L CSV export now runs Mon-Fri at 09:00 ET
  (run_weekly/build_weekly_csv → run_daily/build_daily_csv; --mode daily)
- notifier: send_document() retained for daily CSV Telegram delivery

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@diana-jydai

Copy link
Copy Markdown
Collaborator Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved and pushed in commit 2e92640. Merge conflicts in main.py, src/notifier.py, and src/pipeline.py are fixed, and tests are passing.

@diana-jydai

Copy link
Copy Markdown
Collaborator Author

@copilot I only want daily mode, I don't want weekly mode anymore

Copilot AI commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@copilot I only want daily mode, I don't want weekly mode anymore

Done in 1111ac0. Weekly mode has been removed (CLI --mode weekly, run_weekly(), weekly formatter/CSV aliases), and the flow now supports daily mode only.

Copilot AI changed the title Feat/evening pnl equity backfill Feat/evening pnl equity backfill (daily-only P&L export) Jun 9, 2026
@diana-jydai
diana-jydai requested review from Copilot and yebof June 9, 2026 08:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR shifts the P&L export workflow from a weekly-compatible flow to a daily-only CSV export, removes the evening message’s embedded P&L history table, and adds an automatic DB “self-heal” to backfill missing equity_close values caused by Alpaca portfolio_history lag. It also improves operational visibility by ensuring startup/config failures can still trigger a Telegram notification.

Changes:

  • Replace legacy weekly export paths with a daily CSV export (--mode daily, run_daily(), build_daily_csv()), and update session-result formatting accordingly.
  • Add db.backfill_equity_close() and invoke it during the evening run to fill prior NULL equity_close rows once the API catches up.
  • Remove the evening Telegram P&L history text table and update tests to reflect the new daily-export behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_weekly_report.py Updates tests to target build_daily_csv() / run_daily() and daily-mode session formatting.
tests/test_notifier.py Removes coverage for the now-deleted evening P&L history text table helper.
tests/test_db.py Adds test coverage for the new backfill_equity_close() gap-filling behavior.
src/storage/db.py Introduces backfill_equity_close() to update only NULL equity_close rows.
src/pipeline.py Adds evening-run backfill loop for prior dates and renames weekly export orchestration to run_daily().
src/notifier.py Switches session formatting from weekly to daily, deletes the evening P&L history table code, and renames CSV builder to build_daily_csv().
main.py Replaces weekly CLI mode with daily and restructures startup to instantiate notifier/try-finally earlier for crash visibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_weekly_report.py Outdated
Comment on lines +1 to +6
"""Daily P&L CSV export (PR #98).

Covers: build_weekly_csv settlement math (close-to-close, drawdown, return),
Covers: build_daily_csv settlement math (close-to-close, drawdown, return),
SPY column population + graceful degradation, broker.get_full_portfolio_history
ET-date mapping + pre-funding skip, send_document, run_weekly orchestration,
and the format_session_result weekly body.
ET-date mapping + pre-funding skip, send_document, run_daily orchestration,
and the format_session_result daily body.
Comment thread src/notifier.py Outdated
Comment on lines 213 to 216
elif mode == "daily":
rows = result.get("rows", "?")
filename = result.get("filename", "")
lines.append(f"📊 {rows} rows → {filename}")
Review fixes on top of the daily-only P&L export:

- pipeline: guard the equity_close backfill against 0.0 / NaN / inf /
  negative portfolio_history points. A backfilled value targets
  NULL-only rows, so a corrupt write could never be corrected by a
  later run; NaN additionally binds as SQL NULL (fake success log
  every night). Mirrors the prev_close > 0 guard 16 lines above and
  the eq == 0.0 pre-funding skip in get_full_portfolio_history.
- pipeline: reword the backfill comment — it claimed today's bar
  'isn't settled' while the branch above trusts and stores it.
- scheduling: --mode daily previously had NO automated invocation
  path (run_if_et_window.sh rejects it by design, no timer existed)
  while this PR deletes the evening P&L table it replaces. Add
  scripts/run_daily_export.sh (.env sourcing + 300s timeout) and
  tracked units scripts/systemd/quant-agent-daily.{service,timer}
  (Mon-Fri 09:00 America/New_York, Persistent=true).
- notifier: daily 'sent' is now silent (the CSV document push IS the
  confirmation — was 2 pushes per weekday); 'error' now carries the
  reason (a bare 'status: error' is undebuggable from a phone);
  _status_emoji classifies 'sent' as success; docstring noise-policy
  list gains the daily entry; drop stale history-table comment.
- main: honest crash-visibility comment (when .env was never sourced
  the Telegram creds are missing too, so the FAILED push is dropped —
  that scenario still needs an external dead-man's switch); missing
  config file exits with the path in the message (str(SystemExit(1))
  is just '1'); graceful live-scheduler exit pushes a clear
  'scheduler_exited' status instead of 'returned non-dict result'.
- tests: rename test_weekly_report.py -> test_daily_report.py; +10
  tests pinning the backfill loop (today excluded in both branches,
  value guard, per-date error isolation), the early-crash FAILED
  push, and the daily noise policy. 1236 passed.
- docs: CLAUDE.md + README updated for the daily mode, its timer,
  and the removed evening P&L table.

Refs #99
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yebof

yebof commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Review summary (multi-agent review, 5 dimensions + adversarial verification)

Core design is sound and merged with fixes (commit d337242). What the review found and the fix shipped on this branch:

Data integrity (major): the backfill loop wrote any get_recent_daily_closes value into NULL equity_close rows with no validity guard. That source does not filter 0.0 points (unlike get_full_portfolio_history's pre-funding skip), and a NULL-only fill is permanent — a corrupt value could never be corrected. NaN was worse: sqlite binds it as NULL, so backfill would log success every night while storing nothing. → guarded with isfinite && > 0, mirroring the existing prev_close > 0 guard.

Operability (major): --mode daily had no automated invocation path — run_if_et_window.sh rejects it (exit 2), no timer existed on the prod host — while this PR deletes the evening P&L table it replaces, so scheduled P&L history would have dropped from nightly to zero. → added scripts/run_daily_export.sh (.env sourcing + timeout) and tracked units scripts/systemd/quant-agent-daily.{service,timer} (Mon-Fri 09:00 America/New_York).

Notifications: daily sent now silent (the CSV document push IS the confirmation — was 2 pushes/weekday); error now carries the reason; _status_emoji knows sent (Copilot's catch); missing-config exit now pushes the path instead of SystemExit: 1; graceful live-scheduler exit pushes scheduler_exited instead of "returned non-dict result".

Honesty fix: the main.py crash-visibility comment claimed to fix the missing-Saturday-report incident, but in that exact scenario (.env never sourced) the Telegram creds are missing too and the FAILED push is silently dropped. Comment now states the real coverage + the dead-man's-switch gap.

Tests/docs: test_weekly_report.pytest_daily_report.py (Copilot's catch); +10 tests (backfill today-exclusion in both branches, value guard, per-date error isolation, early-crash FAILED push, daily noise policy); CLAUDE.md/README updated. 1236 passed.

@yebof
yebof merged commit 2a812d1 into main Jun 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants