Strategy Basics
Every quantitative strategy follows a lifecycle. Understanding this lifecycle is the foundation of successful strategy development.
The Strategy Lifecycle
Research → Develop → Backtest → Optimize → Deploy → Monitor
↑ │
└──────────────────── Iterate ←─────────────────────────┘
1. Research
Before writing any code, answer:
- What market inefficiency am I trying to exploit?
- Why does it exist? (behavioral bias, structural friction, information asymmetry)
- Will it persist? (or will it be arbitraged away?)
The best strategies are built on understanding why something works, not just finding patterns in data.
2. Develop
Convert your hypothesis into code:
from vecalpha import Strategy
class MomentumStrategy(Strategy):
"""Buy when price breaks above 20-day high."""
def on_bar(self, bar):
if self.position == 0:
if bar.close > self.high(20):
self.buy(size=self.calculate_position_size())
else:
if bar.close < self.low(10):
self.sell(size=self.position)
3. Backtest
Test on historical data. Key questions:
- Would this have made money?
- What were the worst losses?
- How does it perform in different market conditions?
4. Optimize
Fine-tune parameters carefully:
- Avoid overfitting
- Use out-of-sample testing
- Focus on robustness, not peak performance
5. Deploy
Move to live trading:
- Start with paper trading
- Scale up gradually
- Set risk limits
6. Monitor
Track performance and iterate:
- Compare live vs. backtest
- Identify degradation early
- Adapt to changing markets
Strategy Types
Trend Following
Capture sustained price movements.
Logic: Prices that have been rising tend to continue rising.
Indicators: Moving averages, breakout systems, ADX
Works best: Strong trending markets
Struggles: Sideways, choppy markets
Mean Reversion
Profit from prices returning to average.
Logic: Extreme moves tend to reverse.
Indicators: Bollinger Bands, RSI, z-scores
Works best: Range-bound, choppy markets
Struggles: Strong trends
Statistical Arbitrage
Exploit statistical relationships.
Logic: Related securities move together; divergences revert.
Indicators: Cointegration, correlation, pairs
Works best: Markets with related instruments
Struggles: Structural breaks, regime changes
Market Making
Provide liquidity for spread capture.
Logic: Buy at bid, sell at ask, capture spread.
Indicators: Order book analysis, volatility
Works best: High-volume, liquid markets
Struggles: Fast markets, adverse selection
Strategy Components
Every strategy needs these core components:
1. Data Input
What data does your strategy consume?
# Example data requirements
data_config = {
'symbols': ['AAPL', 'MSFT'],
'timeframe': '1h',
'fields': ['open', 'high', 'low', 'close', 'volume'],
'start_date': '2020-01-01',
}
2. Signal Generation
The logic that produces buy/sell signals.
def generate_signal(self, data):
"""Return: 1 (buy), -1 (sell), 0 (hold)"""
if condition_buy:
return 1
elif condition_sell:
return -1
return 0
3. Position Sizing
How much to trade.
def calculate_position_size(self):
"""Risk-based position sizing."""
risk_per_trade = 0.02 # 2% of portfolio
stop_loss_distance = self.entry_price * 0.05 # 5% stop
position_size = (self.portfolio_value * risk_per_trade) / stop_loss_distance
return position_size
4. Risk Management
Protect against losses.
risk_config = {
'max_position_size': 0.10, # Max 10% in one position
'max_drawdown': 0.15, # Stop if drawdown exceeds 15%
'max_correlation': 0.70, # Limit correlated positions
}
5. Execution
How orders are placed.
execution_config = {
'order_type': 'limit', # or 'market'
'time_in_force': 'GTC', # Good Till Cancel
'slippage_tolerance': 0.001, # Max slippage
}
Common Pitfalls
1. Overfitting
Problem: Strategy works perfectly on historical data but fails in live trading.
Solution:
- Use out-of-sample testing
- Limit parameters
- Focus on economic rationale, not just data mining
2. Look-Ahead Bias
Problem: Using future information in signals.
Solution:
- Always shift data properly
- Test: "Could I have known this at trade time?"
3. Survivorship Bias
Problem: Testing only on currently traded securities.
Solution:
- Use survivorship-free data
- Include delisted/bankrupt securities
4. Ignoring Costs
Problem: Not accounting for commissions and slippage.
Solution:
- Include realistic costs in backtests
- Consider impact on high-frequency strategies
Your First Strategy
Ready to build? Here's a template:
from vecalpha import Strategy, Backtest
class SimpleMovingAverageStrategy(Strategy):
"""Simple MA crossover strategy."""
def __init__(self):
self.short_window = 20
self.long_window = 50
def on_bar(self, bar):
# Calculate indicators
short_ma = self.sma(self.short_window)
long_ma = self.sma(self.long_window)
# Generate signals
if self.position == 0: # No position
if short_ma > long_ma:
self.buy(size=self.calculate_position_size())
else: # Have position
if short_ma < long_ma:
self.sell(size=self.position)
# Run backtest
results = Backtest(
SimpleMovingAverageStrategy,
symbol='AAPL',
start='2020-01-01',
end='2024-01-01',
initial_capital=100000
).run()
print(results.summary())
Next Steps
- Indicators & Signals - Learn technical indicators
- Backtesting Guide - Test your strategies properly