Trading Signals
Platform
From Excel spreadsheets to automated quantitative signal generation — a Python-based pipeline managing six strategies across two Interactive Brokers accounts.
The Journey
From Excel workbooks to automated signal generation.
The original setup was a set of heavy Excel files that depended on a Bloomberg terminal connection to run. These files combined everything — backtests, research, and daily signal generation — into single workbooks. They crashed frequently, and any formula error could silently produce wrong signals.
This repo separates those purposes. Research and backtesting stay in Excel where they belong. Signal generation lives here — standalone Python code with no Bloomberg dependency, designed to do one thing well: tell me exactly what to trade today, for both accounts, across all six strategies.
The benefits are straightforward:
- Signal generation mixed with research and backtests
- Depended on Bloomberg connection
- Frequent crashes
- Formula errors could go unnoticed
- Couldn't run away from my desk
- Signal generation is its own codebase, separate from research
- Uses Polygon API and IQFeed — no Bloomberg needed
- Automated pipeline with validation at every step
- Reduces trading mistakes through systematic checks
- Can run remotely via Discord from my phone
The core principle behind this project: separate signal generation from research. Everything else follows from that.
The goal is simple: generate correct orders every day and avoid trading mistakes. Every signal is reproducible, every run is logged, and every order is reconciled against the broker before and after execution.
Architecture Overview
A modular, pipeline-driven design that separates configuration from state, strategies from I/O, and data providers from consumers.
The architecture follows several deliberate design principles. Configuration files — edited only by humans — are strictly separated from state files written only by automation. Strategies are pure functions: they receive data and return signals, with no file I/O or network calls permitted inside compute(). A DataRouter abstracts the mapping from symbols to data providers, so strategies never call APIs directly. This separation makes the system testable, reproducible, and safe to operate.
Repository Structure
1trading-signals/2├── pyproject.toml # Installable package — pip install -e . → signals run3├── config/ # STATIC — edited by humans only4│ ├── accounts.yaml # Account metadata + strategy assignments5│ ├── contracts.yaml # Futures specs, multipliers, asset classes6│ ├── tws_config.yaml # TWS connection settings7│ ├── notifications.yaml # Discord/Telegram config8│ └── strategies/ # Per-strategy YAML parameters9│ ├── rph.yaml # Risk Premia Harvesting10│ ├── sb_rebalance.yaml # Stocks & Bonds End-of-Month11│ ├── vxx_slayer.yaml # VXX Slayer12│ ├── uga_holiday.yaml # UGA Gasoline Holidays13│ ├── ung_holiday.yaml # UNG Natural Gas Holidays14│ └── ng_winter.yaml # Natural Gas Winter Short15├── data/ # Market data (not in git)16│ ├── raw/ # Static reference data17│ ├── market/ # Mutable market data files18│ └── cache/ # API response cache19├── state/ # DYNAMIC — only written by automation20│ ├── positions.yaml # Live NAV + positions (TWS sync only)21│ └── checkpoints/ # Post-trade position snapshots22├── src/trading_signals/ # The Python package23│ ├── cli.py # Unified CLI (Typer-based)24│ ├── pipeline.py # preflight → compute → postcheck → output25│ ├── run_context.py # Frozen RunContext per pipeline run26│ ├── models.py # Shared data models27│ ├── data/ # Data providers28│ │ ├── router.py # Symbol → provider resolution29│ │ ├── polygon_client.py # Equities/ETFs via Polygon API30│ │ ├── databento_client.py # Futures via Databento31│ │ └── iqfeed_client.py # VIX futures via IQFeed32│ ├── strategies/ # Strategy implementations33│ │ ├── base.py # Abstract base class (pure compute)34│ │ ├── registry.py # Strategy registry35│ │ ├── rph.py # Risk Premia Harvesting36│ │ ├── sb_rebalance.py # Stocks & Bonds End-of-Month37│ │ ├── vxx_slayer/ # VXX Slayer (sub-package)38│ │ ├── uga_holiday.py # UGA Gasoline Holidays39│ │ ├── ung_holiday.py # UNG Natural Gas Holidays40│ │ └── ng_winter.py # Natural Gas Winter Short41│ ├── commands/ # CLI command implementations42│ └── utils/ # Shared utilities43├── tests/ # 34 test files, 704+ tests44│ ├── golden/ # Golden regression tests45│ └── test_strategy_purity.py # Purity enforcement46└── discord_signal_bot.py # Discord bot for mobile accessKey Architectural Decisions
Config vs State Separation
Configuration files (config/) are edited only by humans and define strategy parameters, account assignments, and connection settings. State files (state/) are written only by automation — positions synced from TWS, checkpoints saved after runs. This prevents the dangerous failure mode where the system reads its own predictions as reality. The rule is enforced by convention and by agent rules in CLAUDE.md: "Never write to state/positions.yaml from signal generation."
Pure Strategy Functions
Every strategy extends BaseStrategy and implements a compute(as_of_date, data, config) method that is strictly pure — no file I/O, no network calls, no print() statements. This is enforced by purity tests that patch open, requests, and urllib during test execution.
1class BaseStrategy(ABC):2 required_data_ops: ClassVar[frozenset[str]]3 routed_symbols: ClassVar[tuple[str, ...]]45 @abstractmethod6 def compute(7 self,8 as_of_date: date,9 data: dict,10 config: dict,11 ) -> list[PositionSignal]:12 """Pure function: data in → signals out. No I/O."""13 ...DataRouter Abstraction
The DataRouter resolves symbols to the appropriate data provider using contracts.yaml. Equities and ETFs route to the Polygon API, futures route to Databento, and VIX term structure data routes to IQFeed. Strategies declare their data requirements via required_data_ops and routed_symbols class variables, and the pipeline fetches everything before calling compute().
Data Flow
Market data flows from multiple providers through the DataRouter into pure strategy computations, producing signals that are validated, formatted, and delivered through multiple channels. The interactive diagram below shows every connection in the system.
Hover over any node to explore connections and detailsTap any node to see details
| Provider | Asset Types | Operations | Use Case |
|---|---|---|---|
| Polygon | Equities, ETFs | daily_bars, close, snapshot | VTI, VGLT, GLD, IBIT, SPY, TLT, UGA, UNG, KOLD |
| Databento | Futures | daily_bars, last_trade | MES (micro E-mini S&P 500) |
| IQFeed | VIX Futures | term_structure | VX1–VX9 continuous contracts |
| TWS (IB) | All | positions, nav, orders | Account sync, order reconciliation |
Live Config Explorer
Browse the actual configuration directory tree. Click any file to view its contents — hover over lines to see inline annotations explaining what each parameter does.
The Six Strategies
Each strategy is a self-contained module with its own configuration, documentation, and test suite. Click any card to expand its parameters.

Strategy Registry
All strategies are registered in a central registry, enabling the pipeline to discover and execute them dynamically:
1STRATEGY_REGISTRY = {2 "rph": RiskPremiaStrategy,3 "sb_rebalance": SBRebalanceStrategy,4 "vxx_slayer": VXXSlayerStrategy,5 "uga_holiday": UGAHolidayStrategy,6 "ung_holiday": UNGHolidayStrategy,7 "ng_winter": NGWinterStrategy,8}Strategy Comparison
A side-by-side view of all six strategies — sortable by any column, filterable by asset class. Click a row to see its risk approach.
| Strategy | Asset Class | Instruments | Frequency | Data Sources | Accounts | Order |
|---|---|---|---|---|---|---|
| RPHRisk Premia Harvesting | Multi-Asset | VTI, VGLT, GLD, IBIT | Daily (drift-band) | Polygon | A + B | MOC |
| S&B RebalStocks & Bonds End-of-Month | Equity + Fixed Income | SPY, TLT | Monthly (LD-5 → T+5) | Polygon, Databento | A + B | MOC |
| VXX SlayerVXX Long/Short | Volatility | VXX, VIX Futures | Daily | Polygon, IQFeed | A only | OPG |
| UGA HolidayGasoline Holiday Seasonal | Commodities | UGA | ~10 periods/year | Polygon | A + B | MOC |
| UNG HolidayNatural Gas Holiday Seasonal | Commodities | UNG | ~10 periods/year | Polygon | A + B | MOC |
| NG WinterNatural Gas Winter Short | Commodities | KOLD (from UNG signal) | Daily (Oct–Mar) | Polygon | A + B | MOC |
Click any row to expand details · Click column headers to sort
Strategy Activity Calendar
A visual calendar showing when each strategy is active. RPH and VXX Slayer run daily, S&B Rebalance activates around month-end, holiday strategies fire near US holidays, and NG Winter covers October through March. Navigate between months to see the overlap.
Daily Pipeline
The operational heart of the system — a five-stage pipeline that runs every trading day, from data fetch through signal delivery.
Interactive Pipeline Walkthrough
Pipeline Execution Flow
Click each stage to see inputs, outputs, and timing
1. Fetch Market Data
~8sDataRouter queries Polygon.io, Yahoo Finance, and FRED for prices, volatility surfaces, and macro indicators. Each provider has automatic retry with exponential backoff.
Inputs
config/tickers.yamlAPI keys from .env
Outputs
state/market_data.jsonstate/vix_term_structure.json
Sample Output
Here is what the pipeline produces each morning — a signals CSV with every position target, and a styled HTML order worksheet for manual review before entering orders in TWS:
strategy,ticker,direction,target_pct,current_pct,delta_shares,price,notional vol_premium,SPY,SELL_PUT,0.15,0.12,+2,585.40,1170.80 gasoline_seasonal,UGA,LONG,0.08,0.08,0,57.20,0.00 mean_reversion,TLT,LONG,0.10,0.07,+5,92.15,460.75 sector_momentum,XLK,LONG,0.12,0.12,0,220.30,0.00 bond_rotation,SHY,LONG,0.05,0.05,0,82.40,0.00 tail_hedge,UVXY,LONG,0.02,0.02,0,28.90,0.00
Daily Automation Schedule
The pipeline runs on a fixed schedule every trading day, managed by Windows Task Scheduler with a Discord bot as the mobile fallback:
| Time (ET) | Event | Description |
|---|---|---|
| ~15:30 | Safety-net run | Full pipeline re-runs, reconciles against any pending orders |
| ~15:45 | Signal generation | MOC orders calculated based on latest market data |
| ~15:50 | Manual review | Operator reviews signals and enters MOC orders in TWS |
| ~16:00 | Market close | MOC orders execute at closing price |
| ~16:10 | Auto-verify | Compares actual fills against signal targets, posts to Discord |
Remote Access & Mobile Operations
The system supports full remote operation through multiple channels, enabling the operator to monitor and trigger the pipeline from any device:
Discord Bot
9 slash commands: /daily, /signals, /preview, /calendar, /reconcile, /verify, /update_accounts, /update_data, /bot_status. Scheduled safety-net at 15:30 ET and auto-verify at 16:10 ET.
Notifications
Discord webhooks and Telegram messages deliver signal summaries, verification results, and alerts. Configurable per-channel routing.
GitHub Gist Reports
Styled HTML reports uploaded to GitHub Gist for mobile-friendly viewing. Clickable links instead of file attachments.
CLI Interface
The entire system is operated through a unified Typer-based CLI, installed as the signals command:
1$ signals run # Full daily pipeline2$ signals run --only rph,ng_winter # Run specific strategies3$ signals run --date 2025-12-15 # Backtest mode4$ signals run --dry-run # No output files5$ signals run --strict # Abort on any warning6$ signals preview # Offline signal preview7$ signals reconcile # Compare TWS orders vs signals8$ signals verify # Post-close position check9$ signals update-accounts # Sync positions from TWS10$ signals update-data # Refresh VIX futures data11$ signals calendar # Show upcoming strategy actionsHow a Signal Is Born
An end-to-end walkthrough tracing a single RPH signal for VTI — from raw Polygon API data through every pipeline stage to the final MOC order on your phone.
The previous sections explain what each piece of the system does. This section shows how they work together by tracing one concrete signal through the entire pipeline. Every number below comes from the actual source code — the config values from rph.yaml, the formulas from rph.py, and the checks from postcheck.py.
The Scenario
It's Tuesday, February 10, 2026 at 4:35 PM ET. The market just closed. The RPH (Risk Premia Harvesting) strategy needs to check whether VTI has drifted outside its 5% rebalance band in Account A. The account holds 195 shares of VTI with a NAV of $150,000 and a 6% volatility target. Let's trace exactly what happens.
Stage 1: Preflight
~2sValidate everything before touching the market
It's 4:35 PM ET on a Tuesday. The pipeline starts by asking: is it safe to run? Preflight loads every YAML config through Pydantic with extra='forbid' — a single unexpected field kills the run. It checks the trading calendar, verifies positions.yaml was synced from TWS within the last 26 hours, and confirms the DataRouter can resolve all symbols. Only when every check passes does the pipeline advance.
Safety & Risk Management
Defense-in-depth for a solo trading operation — multiple independent layers that catch errors before they become costly mistakes.
The safety architecture follows a propose-and-confirm pattern: the system generates signals and proposes orders, but a human reviews and confirms before submission to the broker. This is a deliberate choice — full automation is on the roadmap, but the current priority is ensuring correctness over speed. Every layer below is designed to catch errors before they reach the confirmation step.
Pure Strategy Functions
No side effects in compute() means signals are deterministic and reproducible. Given the same data and config, the same signals are always produced. Enforced by purity tests that patch I/O primitives.
Config vs State Separation
Configuration (human-edited) and state (machine-written) are never mixed. The system cannot read its own predictions as ground truth. Agent rules explicitly forbid writing to state/positions.yaml from signal generation.
Large Trade Guardrails
Post-check flags any single order exceeding 30% of account NAV. This catches data errors, miscalculations, or configuration mistakes before they become outsized positions.
Strict Mode
The --strict flag blocks execution if any data staleness warning, validation issue, or preflight concern is detected. Used for production runs where zero tolerance is required.
Run Manifests
Every pipeline run records a manifest.json with the git SHA, config hash, file hashes, timing, and any warnings. Any signal can be traced back to the exact code and configuration that produced it.
Post-Close Verification
After market close (~16:10 ET), the auto-verify command compares actual TWS positions against the intended signal targets. Discrepancies are flagged and posted to Discord immediately.
Strategy-Aware Reconciliation
Order reconciliation uses strategy-specific tolerances: RPH uses drift-band tolerance (since it rebalances within bands), while other strategies use flat NAV percentage thresholds. Stale fills from prior TWS sessions are filtered by execution date.
Error Scenarios in Action
Select a scenario below to see exactly how the safety layers respond — step by step — when something goes wrong:
Polygon API returns data older than expected trading day
Preflight detects data timestamp mismatch
WARNWarning logged: "Market data is 2 days stale"
WARNIn --strict mode: pipeline ABORTS immediately
BLOCKIn normal mode: continues with stale-data warning in manifest
PASSOperator sees warning in Discord notification
Stale data is never silently used. Strict mode blocks execution; normal mode flags it prominently for human review.
Config Hash Reproducibility
Every pipeline run records a SHA-256 hash of the complete configuration. This ensures full auditability — any signal can be traced back to the exact parameters that produced it:
Config Hash Demo
Every pipeline run records a SHA-256 hash of the config files used. Edit the config below and watch the hash change — proving that any modification is detectable.
Config Hash
In production, this is a full SHA-256 hash. The manifest records it alongside the run timestamp, so any config drift between runs is immediately visible.
Why This Architecture?
Anticipating the questions someone reviewing this system might ask — here are the design decisions, explained.
Technology Stack
The tools and integrations that power the platform — chosen for reliability, testability, and operational simplicity.
Dependency Map
An interactive visualization of the Python package ecosystem powering the platform. Hover over any node to see its role and connections.
Primary runtime — type hints, match statements, tomllib
Config validation with extra=forbid — catches typos and schema violations at load time
DataFrame operations for market data manipulation, OHLCV processing, and signal computation
Numerical operations — EWMA calculations, volatility estimation, z-scores
YAML parsing for all configuration files — accounts, contracts, strategies
Official Polygon.io client — fetches equity/ETF OHLCV, snapshots, and reference data
Databento client — fetches MES futures data for S&B Rebalance futures exposure sizing
Discord bot framework — 9 slash commands, scheduled signal delivery, alert notifications
Telegram integration — signal delivery and operational alerts to mobile
Testing & Quality
A multi-layered testing approach that ensures correctness at every level — from individual strategy logic to full pipeline integration.
The test suite comprises 34 test files with 704+ individual tests, all running offline by default — no live API calls required. The testing philosophy emphasizes three pillars: strategy correctness (unit tests), behavioral stability (golden regression tests), and architectural integrity (purity enforcement tests).
| Category | Files | What It Tests |
|---|---|---|
| Strategy Unit Tests | 6 files | Each strategy's compute() logic with fixture data |
| Golden Regression | 1 file + fixtures | Exact signal output match against captured baselines |
| Purity Enforcement | 1 file | Verifies compute() makes no I/O calls (patches open, print, requests, urllib) |
| Data Client Tests | 3 files | Polygon, Databento, TWS client behavior |
| Utility Tests | 3 files | Volatility calculations, calendar math, price service |
| Integration Tests | 6 files | Checkpoint, reconciliation, preview, NAV estimation, output formatting |
Purity Enforcement
The purity test is one of the most important architectural guardrails. It patches Python's I/O primitives during strategy execution via pytest and fails if any strategy attempts to read files, make network calls, or print output:
1def _purity_patches():2 """Patch I/O primitives to detect impure strategy code."""3 return [4 patch("builtins.open", side_effect=AssertionError("I/O in compute()")),5 patch("builtins.print", side_effect=AssertionError("print in compute()")),6 patch("requests.get", side_effect=AssertionError("network in compute()")),7 patch("urllib.request.urlopen", side_effect=AssertionError("network")),8 ]910# For every registered strategy:11patches = _purity_patches()12for p in patches:13 p.start()14try:15 signals = strategy.compute(REF_DATE, data, config)16 assert len(signals) > 0 # Must produce output17finally:18 for p in patches:19 p.stop()Golden Regression Tests
Golden tests capture the exact output of each strategy against a fixed set of market data fixtures. When code changes, the test compares new output against the captured baseline. Any difference — even a rounding change — must be explicitly acknowledged and the fixture re-captured. This catches subtle regressions that unit tests might miss.
1# Capture new golden fixtures2$ python tests/golden/capture_fixtures.py --only rph34# Run golden regression tests5$ pytest tests/golden/test_golden.py -v67# Run full test suite8$ pytest # All 704+ tests9$ pytest tests/test_rph.py -v # Single strategy10$ pytest --tb=short # Brief tracebackManifest Inspector
Every pipeline run produces a manifest.json — a complete audit trail. Expand each section below to explore a sample manifest:
Every pipeline run produces a manifest.json — a complete audit trail of what ran, when, and with what configuration.
Glossary
A searchable reference of technical terms, architectural concepts, and domain-specific vocabulary used throughout this platform.
This glossary collects the key terms and concepts referenced throughout the site — from architectural patterns like the DataRouter and RunContext, to strategy-specific vocabulary like drift-band rebalancing and MAD z-scores, to operational concepts like config hashing and purity testing. Click any term to expand its full definition, and use the related terms to navigate between connected concepts.
Technical FAQ & Feature Guide
A comprehensive reference covering every subsystem — architecture decisions, pipeline internals, safety layers, strategy purity, daily operations, and testing infrastructure.
This section presents the full technical documentation in a browsable format. Each chapter covers a major subsystem with plain-English explanations of what it is, why it exists, and how it works — including code examples, configuration snippets, and operational details. Use the search bar to find specific topics, or expand chapters to read through sequentially.
This is a trading signals system. Every day, it: (1) pulls account data from Interactive Brokers, (2) fetches market prices from several data providers, (3) runs 6 independent strategy functions that each say "buy X shares of Y" or "hold", (4) checks the output for obvious mistakes, and (5) writes everything to files so you can enter orders and verify them later. The whole thing is wrapped in safety layers and can be operated from the terminal, a Discord bot on your phone, or a Jupyter notebook.
What's Next
The platform is fully operational with all six strategies running daily. The roadmap focuses on deeper automation, observability, and performance tracking.
Automated Order Submission
PlannedMove from propose-and-confirm to full automation via the TWS API. The human review step becomes optional, with configurable guardrails for when manual confirmation is required.
Signal Ledger
CompleteSQLite run ledger (state/ledger.db) with two tables: runs (one row per pipeline run) and signals (one row per PositionSignal). Queryable via signals history for historical analysis, strategy attribution, and audit trails.
Performance Analytics
PlannedProgrammatic tracking of strategy returns, attribution analysis, and risk metrics. Replace manual Excel-based performance tracking with automated dashboards.
Contract Roll Management
PlannedAutomated handling of futures expiry — detecting upcoming rolls, calculating roll dates, and adjusting positions across contract months.
Enhanced Observability
In ProgressRicher run manifests, operational dashboards, and proactive alerting. The goal is to know about problems before the market opens, not after it closes.