| name | realised-return-reflection |
| description | Closes Atlas's learning loop. Every pick saves to an append-only markdown decision log with a pending tag. On the next run for the same ticker (or via `python main.py reflect <TICKER>`), Atlas fetches realised return + alpha vs benchmark via yfinance, generates a 2-4 sentence reflection, and rewrites the tag. The reflection plus last N same-ticker + M cross-ticker lessons inject into the next Portfolio Manager prompt so Atlas stops repeating its own mistakes. Pattern lifted from TauricResearch/TradingAgents. |
| triggers | ["reflection","realised return","post-mortem","alpha","learn from picks","what worked","pick performance","decision log","lessons learned","what did my pick do","how did my pick perform","last pick outcome","pick return","pick alpha"] |
| tier | research |
| dependencies | [] |
| origin | TauricResearch/TradingAgents (patterns) |
Realised-Return Reflection Loop
Purpose
Atlas was producing picks and saving them, but had no feedback loop — the next pick on the same ticker didn't know how the previous one performed. This skill closes that loop.
Two-phase design (lifted from TauricResearch/TradingAgents memory.py):
Phase A — Store (zero LLM call, runs at pick time)
When StockPickerAgent.pick(reflect=True) finishes, every pick appends a pending entry to data/reflections/decision_log.md:
[2026-05-15 | NVDA | Buy | pending]
DECISION:
Rating: Buy
Ticker: NVDA
Conviction: 8/10
Account: TFSA
Entry: 950 | Exit: 1180 | Stop: 850
<full PM recommendation prose>
<!-- ENTRY_END -->
Idempotent — re-running on the same (date, ticker) won't duplicate.
Phase B — Resolve (one quick_think LLM call, runs later)
On the next pick run for the same ticker, OR explicitly via python main.py reflect <TICKER>, Atlas:
- Pulls the ticker's prices + benchmark prices via yfinance (default benchmark: SPY)
- Computes realised return + alpha vs benchmark over the holding window
- Generates a 2-4 sentence reflection covering (a) was the directional call correct, citing the alpha figure, (b) what part of the thesis held or failed, (c) one concrete lesson
- Rewrites the tag in place with atomic file replace:
[2026-05-15 | NVDA | Buy | +5.2% | +1.8% | 5d]
DECISION:
<unchanged>
REFLECTION:
<2-4 sentence prose>
<!-- ENTRY_END -->
Data Integrity
If yfinance returns no data, research/reflection.py raises DataFeedError — the silent-fallback hallucination CC caught on 2026-04-25 is prohibited here too. The reflection refuses to resolve rather than fabricating a return.
Past Context Injection
Before the picker drafts a new pick, DecisionLog.get_past_context(ticker) returns the last N=5 same-ticker decisions (full decision + reflection) and last M=3 cross-ticker lessons (reflection only). This block injects into the Portfolio Manager prompt so the new pick reads what Atlas has already learned.
Storage
- Path:
data/reflections/decision_log.md (one global log, filtered by ticker on read)
- Format: markdown with
<!-- ENTRY_END --> separator
- Tag format:
[date | ticker | rating | pending] → [date | ticker | rating | raw% | alpha% | days]
- Rotation: optional
max_entries cap drops oldest resolved blocks (pending always kept)
- Atomic writes: temp file + os.replace so a crash mid-write never corrupts the log
Triggers
python main.py reflect <TICKER> — resolve all pending entries for that ticker
python main.py reflect <TICKER> --date 2026-05-01 — resolve a specific date
python main.py picks "<query>" --debate 1 --reflect — auto-write pending after each pick
- Internal:
from research.reflection import DecisionLog
Cost Note
Reflection generation uses the quick_think tier (Claude Haiku 4.5 by default). One LLM call per resolved entry. Past-context injection is free (file read + format).
Internal References
- [[reflection]] — Python module implementing the state machine
- The 5-tier rating scale lifted from
tradingagents/agents/utils/rating.py (Apache-2.0)
- The reflection prompt paraphrased from
tradingagents/graph/reflection.py:189-246