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:

  1. Temporal filtering – Ensure signals fire only at specific intervals (e.g., monthly)
  2. Strike selection – Calculate target strikes based on current underlying price
  3. 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:

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 and OnData() for real-time processing.
  • The static/strategies/volatility-risk-premium-effect.py file 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →