TradingAgents/tradingagents/strategies/value.py

63 lines
1.8 KiB
Python

"""Value strategy signal (§3.3 — Value).
Composite value score from Book/Market, Earnings/Price, and CashFlow/Price.
Reference:
Kakushadze & Serur, "151 Trading Strategies", §3.3
"""
from __future__ import annotations
from typing import Any
from .base import BaseStrategy, StrategySignal
from ._data import get_info
class ValueStrategy(BaseStrategy):
name = "Value (§3.3)"
roles = ["fundamentals", "researcher"]
def compute(self, ticker: str, date: str, context: dict[str, Any] | None = None) -> StrategySignal | None:
info = get_info(ticker, context)
if not info:
return None
scores: list[float] = []
# Book/Market (inverse of P/B)
pb = info.get("priceToBook")
if pb and pb > 0:
bm = 1.0 / pb
scores.append(min(bm, 3.0) / 3.0) # normalize: BM=3 → 1.0
# Earnings/Price (inverse of trailing PE)
pe = info.get("trailingPE")
if pe and pe > 0:
ep = 1.0 / pe
scores.append(min(ep, 0.15) / 0.15)
# Free Cash Flow yield proxy
mcap = info.get("marketCap")
fcf = info.get("freeCashflow")
if mcap and fcf and mcap > 0:
cfy = fcf / mcap
scores.append(max(-1.0, min(cfy / 0.10, 1.0)))
if not scores:
return None
composite = sum(scores) / len(scores)
# Map [0,1] → [-1,1]: high value = bullish
strength = max(-1.0, min(1.0, composite * 2 - 1))
direction = "bullish" if strength > 0.1 else ("bearish" if strength < -0.1 else "neutral")
return StrategySignal(
name=self.name,
ticker=ticker,
date=date,
signal_strength=round(strength, 4),
direction=direction,
detail=f"Composite value score: {composite:.2f} from {len(scores)} factors",
)