Tradability Analysis: Entry, Hold, Exit
Market Synthesis is validated not just on detection accuracy but on tradability β can a fund manager actually profit from these signals?
This page documents the complete trading algorithm derived from backtesting 303 signals (2014-2026) against real market data via yfinance, using an adversarial LLM-based exit system.
Key Finding: Adversarial Exit Beats Mechanical Rules
| Metric | Adversarial Exit | Hold-to-time | Delta |
|---|---|---|---|
| Mean P&L per trade | +7.98% | +4.86% | +3.11% |
| Worst single trade | -19.3% | -57.3% | +38.0% |
| Estimated annual return (10% alloc) | ~22% p.a. | ~14% p.a. | +8% p.a. |
The adversarial exit system uses a weekly LLM check: βis the narrative-reality gap still open?β It holds winners longer (gap still exploitable) and cuts losers earlier (gap closed or reversed), producing better returns with dramatically lower tail risk.
Per-Category Results (303 Signals Backtested)
| Category | N | Exit P&L | Hold P&L | Alpha | Exits Triggered |
|---|---|---|---|---|---|
| Commodity | 45 | +32.8% | +21.8% | +11.0% | 0 |
| Currency Peg | 24 | +16.8% | +2.9% | +13.9% | 1 |
| Sovereign Debt | 111 | +3.2% | +2.0% | +1.2% | 11 |
| Geopolitical | 35 | +3.2% | +3.6% | -0.3% | 14 |
| Central Bank | 67 | +0.9% | +1.3% | -0.4% | 12 |
| Real Estate | 21 | +0.6% | -0.5% | +1.1% | 7 |
Commodity and currency peg are the strongest performers β classic Soros-style trades where structural supply/demand or reserve depletion creates clear mispricings.
Entry Timing Algorithm
Entry timing is category-specific. Fast-repricing events need immediate entry; slow-building events benefit from confirmation.
Score β₯ 70"] --> ROUTE{"Category?"} ROUTE -->|"central_bank
currency_peg
geopolitical
real_estate"| IMM["β‘ IMMEDIATE ENTRY
Next market open"] ROUTE -->|"sovereign_debt"| MOM["π MOMENTUM CONFIRM
Wait for 2 consecutive
favorable-direction days"] ROUTE -->|"commodity"| WAIT["β³ WAIT 3 DAYS
Let initial volatility
settle"] IMM --> SIZE["Position Size
Score 90+ β 100%
Score 80-89 β 75%
Score 70-79 β 50%"] MOM --> SIZE WAIT --> SIZE SIZE --> STOP["Set Stop-Loss
(category-specific)"] style DETECT fill:#1d4ed8,stroke:#153ca8,color:#ffffff style ROUTE fill:#f0f9ff,stroke:#0284c7,color:#0c4a6e style IMM fill:#dcfce7,stroke:#16a34a,color:#14532d style MOM fill:#fef3c7,stroke:#d97706,color:#78350f style WAIT fill:#fee2e2,stroke:#dc2626,color:#7f1d1d style SIZE fill:#e0e7ff,stroke:#4f46e5,color:#312e81 style STOP fill:#f8fafc,stroke:#94a3b8,color:#0f2b57
Category-specific entry timing β derived from backtest of 4 strategies on each category
Entry Rules by Category
| Category | Entry Trigger | Rationale | Backtested Win Rate |
|---|---|---|---|
| Sovereign Debt | 2 consecutive favorable days | Counter-move (disbelief rally) common in first 1-5 days; momentum confirmation filters false starts | 75% (vs 64% immediate) |
| Central Bank | Immediate (day 0) | Shock reprices markets in hours. Waiting 3 days = -3.7% avg (move already done) | 57% |
| Commodity | Wait 3 days | Extreme initial volatility (-10.7% DD). After 3 days, dust settles, DD drops to -5.3% | 40% |
| Geopolitical | Immediate (day 0) | Crisis escalates from detection. Small drawdown (-2.7%). Delay = worse | 50% |
| Real Estate | Immediate (day 0) | Slow grind; entry timing matters less than exit timing | 33% |
| Currency Peg | Immediate (day 0) | Peak at day 6. Any delay loses the entire move | 33% |
What Does NOT Help for Entry Timing
Tested and rejected:
- Volume spikes: Only 33% correlation with optimal entry. Volume peaks on day 1 (headline splash), not at the drawdown trough.
- VIX reversal: Marginal improvement for sovereign_debt only. Not worth the complexity.
- RSI / oversold signals: Insufficient data, unreliable on macro events.
Position Management Algorithm
Set initial stop-loss"] --> D5{"Day 5:
Profitable?"} D5 -->|"Yes"| TIGHT["Tighten stop
to breakeven"] D5 -->|"No"| HOLD1["Hold with
original stop"] TIGHT --> SIG1{"First exit
signal?"} HOLD1 --> SIG1 SIG1 -->|"Yes"| RED1["Reduce 30%
of position"] SIG1 -->|"No"| PEAK{"Peak day
reached?"} RED1 --> CLUST{"Signal
cluster?
(2+ signals
in 3 days)"} CLUST -->|"Yes"| RED2["Reduce another
30%"] CLUST -->|"No"| PEAK RED2 --> PEAK PEAK -->|"Yes"| CLOSE["CLOSE
remaining position"] PEAK -->|"No"| SIG1 style ENTRY fill:#1d4ed8,stroke:#153ca8,color:#ffffff style D5 fill:#fef3c7,stroke:#d97706,color:#78350f style TIGHT fill:#dcfce7,stroke:#16a34a,color:#14532d style HOLD1 fill:#f8fafc,stroke:#94a3b8,color:#0f2b57 style SIG1 fill:#fef3c7,stroke:#d97706,color:#78350f style RED1 fill:#fee2e2,stroke:#dc2626,color:#7f1d1d style CLUST fill:#fef3c7,stroke:#d97706,color:#78350f style RED2 fill:#fee2e2,stroke:#dc2626,color:#7f1d1d style PEAK fill:#e0e7ff,stroke:#4f46e5,color:#312e81 style CLOSE fill:#7f1d1d,stroke:#450a0a,color:#ffffff
Position management: time-based exit with headline-driven partial profit-taking
Category Parameters
| Category | Stop-Loss | Peak Day | Hold Period | Reward:Risk |
|---|---|---|---|---|
| Sovereign Debt | -6% | Day 25 | 3-4 weeks | 2.1:1 |
| Commodity | -7% | Day 29 | 3-4 weeks | 2.9:1 |
| Central Bank | -7% | Day 19 | 2-3 weeks | 1.7:1 |
| Geopolitical | -5% | Day 22 | 3 weeks | 2.5:1 |
| Real Estate | -8% | Day 18 | 2-3 weeks | 0.8:1 |
| Currency Peg | -6% | Day 6 | 1 week | 0.8:1 |
Adversarial Exit System
The exit system is an LLM-based weekly check that asks: βis the narrative-reality gap that justified this trade still open?β
Gap still open?"} WEEK -->|"gap_still_open"| HOLD["Hold Position
(check again next week)"] WEEK -->|"gap_closed / reversed"| EXIT["Exit Position"] HOLD --> WEEK STOP{"Stop-loss hit?
(per-category threshold)"} -->|"Yes"| FORCE["Force Exit"] ENTRY --> STOP style ENTRY fill:#1d4ed8,stroke:#153ca8,color:#ffffff style WEEK fill:#fef3c7,stroke:#d97706,color:#78350f style HOLD fill:#dcfce7,stroke:#16a34a,color:#14532d style EXIT fill:#fee2e2,stroke:#dc2626,color:#7f1d1d style STOP fill:#f8fafc,stroke:#94a3b8,color:#0f2b57 style FORCE fill:#7f1d1d,stroke:#450a0a,color:#ffffff
Adversarial exit: LLM weekly check replaces mechanical time-based exits
How it works
Each week, the exit detector receives:
- The original signal (thesis, category, direction, gap summary)
- Current news headlines from that week
- Current price data for the instrument
It then decides whether the gap is still exploitable. This produces two key behaviors:
- Holds winners longer β if a commodity supply disruption is still unresolved, the system keeps the position open past the mechanical hold period
- Cuts losers earlier β if a central bank successfully defends, the system exits before the stop-loss is hit
Exit verdict distribution (303 signals)
| Verdict | Count | Meaning |
|---|---|---|
| gap_still_open | 258 | Held to maturity β gap persisted |
| stop_loss | 45 | Exited early on stop-loss or gap closure |
Why adversarial exit beats mechanical rules
Mechanical exits (fixed hold period + stop-loss) fail in two ways:
- Exit too early on winners where the thesis is still playing out (commodity supply disruptions can last months)
- Exit too late on losers where the gap has already closed (central bank successfully defended)
The LLM exit check solves both: it understands whether the thesis is still valid, not just whether price moved.
Instrument Selection
The system uses a 3-layer instrument selector:
- Disk cache β previously validated ticker assignments (living map, updated as results come in)
- Static registry β category Γ region β default instruments (e.g., sovereign_debt + Europe β EUFN, TLT)
- LLM fallback β Sonnet adjudicates the best tradeable expression for novel signals, with regression testing before cache update
Category instruments (top performers)
| Category | Primary Instruments | Avg P&L |
|---|---|---|
| Commodity | CL=F (WTI), BZ=F (Brent), NG=F (Nat Gas) | +32.8% |
| Currency Peg | USDRUB=X, USDTRY=X, USDBRL=X, USDCNY=X | +16.8% |
| Sovereign Debt | TLT, EMB, IGLT.L, DX-Y.NYB | +3.2% |
| Geopolitical | GC=F (Gold), EWZ, country ETFs | +3.2% |
| Central Bank | FX pairs, TLT, IEF | +0.9% |
| Real Estate | VNQ, GBPUSD=X, country ETFs | +0.6% |
Implementation
The adversarial exit backtest is implemented in tests/backtest_adversarial_exit.py:
# Run the full backtest
python -u tests/backtest_adversarial_exit.py --workers 5 --label v10
# Key components:
# - Adversarial exit detector: detectors/adversarial_exit_detector.py
# - Instrument selector: shared/instrument_selector.py (3-layer with cache)
# - Signal store: shared/signal_store.py (635 signals, append-only)
The entry timing algorithm is implemented in shared/entry_timing.py:
from shared.entry_timing import get_entry_recommendation
rec = get_entry_recommendation(
category="sovereign_debt",
score=85,
direction="short",
pre_move_pct=0.0,
)
# Returns: EntryRecommendation(
# entry_type="momentum_confirm",
# momentum_days=2,
# stop_loss_pct=-6.0,
# position_size=0.75,
# hold_days=23,
# peak_day=25,
# )
All metrics derived from feed-driven backtest on real news data (2014-2026), validated against yfinance market prices. 303 signals backtested with adversarial LLM exit system. No hand-crafted test events.