Files
Yijia-Xiao 78d063dc5c feat(reflection): configurable alpha benchmark for non-US tickers
SPY was hardcoded as the alpha benchmark in both the return-fetch
path and the reflection label, which produced meaningless alpha for
.NS / .T / .HK / .L / .TO / .AX / .BO listings — FX drift between a
local-currency stock and a USD index dominates the spread.

DEFAULT_CONFIG now exposes benchmark_ticker (explicit override) and
benchmark_map (suffix → regional index, with SPY as the empty-suffix
default). TRADINGAGENTS_BENCHMARK_TICKER joins the env-overlay table.
Trading graph resolves the benchmark once per ticker and threads it
through to both _fetch_returns and reflect_on_final_decision, so the
alpha label reads "Alpha vs ^N225" for Tokyo listings, "Alpha vs ^HSI"
for Hong Kong, etc., instead of the misleading "Alpha vs SPY".
2026-05-11 09:14:28 +00:00

58 lines
2.4 KiB
Python

# TradingAgents/graph/reflection.py
from typing import Any
class Reflector:
"""Handles reflection on trading decisions."""
def __init__(self, quick_thinking_llm: Any):
"""Initialize the reflector with an LLM."""
self.quick_thinking_llm = quick_thinking_llm
self.log_reflection_prompt = self._get_log_reflection_prompt()
def _get_log_reflection_prompt(self) -> str:
"""Concise prompt for reflect_on_final_decision (Phase B log entries).
Produces 2-4 sentences of plain prose — compact enough to be re-injected
into future agent prompts without bloating the context window.
"""
return (
"You are a trading analyst reviewing your own past decision now that the outcome is known.\n"
"Write exactly 2-4 sentences of plain prose (no bullets, no headers, no markdown).\n\n"
"Cover in order:\n"
"1. Was the directional call correct? (cite the alpha figure)\n"
"2. Which part of the investment thesis held or failed?\n"
"3. One concrete lesson to apply to the next similar analysis.\n\n"
"Be specific and terse. Your output will be stored verbatim in a decision log "
"and re-read by future analysts, so every word must earn its place."
)
def reflect_on_final_decision(
self,
final_decision: str,
raw_return: float,
alpha_return: float,
benchmark_name: str = "SPY",
) -> str:
"""Single reflection call on the final trade decision with outcome context.
Used by Phase B deferred reflection. The final_trade_decision already
synthesises all analyst insights, so no separate market context is needed.
``benchmark_name`` is the label used for the alpha line (e.g. ``"SPY"``
for US tickers, ``"^N225"`` for ``.T`` listings); defaults to SPY for
callers that haven't been updated to thread the benchmark through.
"""
messages = [
("system", self.log_reflection_prompt),
(
"human",
(
f"Raw return: {raw_return:+.1%}\n"
f"Alpha vs {benchmark_name}: {alpha_return:+.1%}\n\n"
f"Final Decision:\n{final_decision}"
),
),
]
return self.quick_thinking_llm.invoke(messages).content