How to Generate Trading Signals from Price Data Using Python: A QuantConnect Implementation Guide
Generating trading signals from price data using Python requires implementing academic strategies within the QuantConnect framework, filtering live market data for specific strike prices and expiries, and executing orders when statistical conditions like volatility risk premium or momentum thresholds are met.
The paperswithbacktest/awesome-systematic-trading repository serves as a curated knowledge base for generating trading signals from price data using Python. Rather than providing a monolithic backtesting engine, it supplies reference implementations in static/strategies/ that demonstrate how to convert raw price streams into systematic trading decisions using the QuantConnect API.
Repository Architecture for Signal Generation
The repository follows a deliberate catalog-and-code separation that makes signal generation strategies discoverable and reusable. The README.md acts as the central index, linking to fully functional Python algorithms stored in static/strategies/. Each file contains a self-contained QCAlgorithm subclass that defines the signal logic, allowing you to copy the implementation directly into QuantConnect's cloud environment or adapt it for other frameworks like Zipline or Backtrader.
This architecture ensures that signal generation logic remains decoupled from execution infrastructure. You browse the curated tables to find a strategy matching your asset class, then import the specific Python file containing the signal rules.
The Signal Generation Pipeline
Every strategy in the repository follows a consistent three-phase pipeline for transforming price data into portfolio actions.
Initialization and Security Configuration
Signal generation begins in the Initialize() method, where you define the backtest parameters and subscribe to data feeds. The VolatilityRiskPremiumEffect class in static/strategies/volatility-risk-premium-effect.py demonstrates this pattern by setting up both equity and option data sources:
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100_000)
# Underlying price data
equity = self.AddEquity("SPY", Resolution.Minute)
equity.SetLeverage(5)
self.symbol = equity.Symbol
# Options chain for delta-hedging and premium collection
opt = self.AddOption("SPY", Resolution.Minute)
opt.SetFilter(-20, 20, 25, 35) # strikes and expiries filter
The SetFilter(-20, 20, 25, 35) call restricts the option universe to contracts within 20 strikes of the current price and expiring between 25 and 35 days, ensuring your signal logic processes only liquid, relevant instruments.
Real-Time Signal Processing in OnData
Trading signals are generated inside the OnData(self, slice) method, which receives price updates every minute (or at your specified Resolution). The signal logic typically involves three steps:
- Temporal filtering – Ensure signals fire only at specific intervals (e.g., monthly)
- Strike selection – Calculate target strikes based on current underlying price
- Order execution – Submit buy/sell orders for the identified contracts
The volatility risk premium implementation uses daily guards to ensure monthly rebalancing:
def OnData(self, slice):
# Run only once per day to generate monthly signals
if self.Time.day == self.last_day:
return
self.last_day = self.Time.day
for chain in slice.OptionChains.values():
if not self.Portfolio.Invested:
calls = [c for c in chain if c.Right == OptionRight.Call]
puts = [p for p in chain if p.Right == OptionRight.Put]
price = self.Securities[self.symbol].Price
expiry = min(p.Expiry for p in puts,
key=lambda d: abs((d.date() - self.Time.date()).days - 30))
# Signal logic: find ATM and 15% OTM strikes
atm_strike = min(p.Strike for p in puts,
key=lambda s: abs(s - price))
otm_strike = min(p.Strike for p in puts,
key=lambda s: abs(s - 0.85 * price))
Portfolio Construction and Risk Management
After identifying the signal triggers, the algorithm constructs the position. In the volatility strategy, this involves selling an at-the-money straddle to collect premium while buying a 15% out-of-the-money put for tail risk protection:
# Build contracts
atm_call = next(c for c in calls if c.Expiry == expiry and c.Strike == atm_strike)
atm_put = next(p for p in puts if p.Expiry == expiry and p.Strike == atm_strike)
otm_put = next(p for p in puts if p.Expiry == expiry and p.Strike == otm_strike)
# Position sizing based on margin
qty = int(self.Portfolio.MarginRemaining / (price * 100))
# Execute signal: sell straddle, buy protection, hold underlying
self.Sell(atm_call.Symbol, qty)
self.Sell(atm_put.Symbol, qty)
self.Buy(otm_put.Symbol, qty)
self.SetHoldings(self.symbol, 1)
The BuyingPowerModel(5) configuration applied to each option contract enables leveraged exposure while maintaining risk constraints.
Alternative Signal Generation Patterns
The repository contains additional implementations that demonstrate different signal generation philosophies:
-
static/strategies/time-series-momentum-effect.py– Generates signals by ranking asset returns over lookback periods and taking long positions in the top-performing decile, implementing classic trend-following logic. -
static/strategies/pairs-trading-with-stocks.py– Creates mean-reversion signals by monitoring the price spread between cointegrated equities and rebalancing when the spread deviates from its historical mean.
Each file maintains the same structure—Initialize() for setup and OnData() for signal processing—making it straightforward to compare methodologies and adapt them to your specific data sources.
Summary
- Generating trading signals from price data using Python in this repository follows a standardized QuantConnect pattern using
Initialize()for setup andOnData()for real-time processing. - The
static/strategies/volatility-risk-premium-effect.pyfile demonstrates option-based signal generation by filtering chains for specific strikes and expiries, then executing a volatility arbitrage strategy. - Signal logic relies on precise price-derived calculations—such as ATM strikes (
min(p.Strike, key=lambda s: abs(s - price))) and OTM protection levels (15% below spot)—to determine position entries. - The repository's separation of documentation (
README.md) and implementation (static/strategies/) enables rapid discovery and deployment of academic trading strategies.
Frequently Asked Questions
What Python libraries are required for generating trading signals in these examples?
The reference implementations rely exclusively on the QuantConnect API imported via from AlgorithmImports import *. No external pandas, NumPy, or TA-Lib installations are required within the QuantConnect environment, though you may add these imports if running the code offline with the QuantConnect Lean engine.
How does the algorithm ensure it only generates signals once per month?
The VolatilityRiskPremiumEffect class tracks the last execution day using self.last_day and compares it against self.Time.day. When the day changes, the algorithm processes new data; otherwise, it returns immediately. This pattern prevents over-trading while maintaining responsiveness to end-of-day price movements.
Can these signal generation strategies run on data sources other than QuantConnect?
Yes, although the code uses QuantConnect-specific classes like QCAlgorithm and BuyingPowerModel, the core signal logic—calculating ATM/OTM strikes, filtering option chains by expiry, and sizing positions based on underlying price—can be adapted to other Python backtesting frameworks like Backtrader, Zipline, or custom pandas implementations by replacing the API-specific method calls.
What is the difference between time-series momentum and volatility risk premium signals?
Time-series momentum generates signals based on the persistence of price trends (past returns predict future returns), implemented in time-series-momentum-effect.py by ranking assets and going long winners. Volatility risk premium signals, as shown in volatility-risk-premium-effect.py, exploit the tendency of implied volatility to exceed realized volatility by selling expensive options and buying cheap protection, creating a signal derived from the volatility surface rather than price direction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →