Complete, production-ready backtesting system for EMA crossover strategy with StochRSI confirmation, €500 max loss/€2000 max gain risk management, and comprehensive trade analysis.
- 50EMA crosses above 200EMA (uptrend confirmation)
- Wait 3 candles (filter false breakouts)
- StochRSI crosses above 0.20 (momentum confirmation)
- Execute LONG position when all conditions align
- Max Loss: -€500 per trade (hard stop loss)
- Max Gain: +€2000 per trade (profit target)
- StochRSI falls below 0.20 (momentum reversal)
- 50EMA crosses below 200EMA (trend reversal)
- Daily ATR exhausted (volatility spike >20% above 20-bar MA)
- Initial Capital: €1,000,000
- Position Size: 2% risk per trade
- Commission: 0.1%
- Margin: 10% (90:1 leverage)
- Stocks: 30 major stocks from NYSE, NASDAQ, Euronext
backtest_system/
├── ema_strategy_complete.py # Main backtest engine
├── backtest_analyzer.py # Results analysis & visualization
├── run_backtest.py # Automated pipeline (use this!)
├── QUICKSTART.py # Setup guide & examples
├── requirements.txt # Python dependencies
├── README.md # This file
└── results/ # Output directory (created after run)
├── all_trades_summary.csv
├── trades_SYMBOL_*.csv
├── backtest_detailed_report.xlsx
├── backtest_charts/
│ ├── 01_pnl_distribution.png
│ ├── 02_cumulative_pnl.png
│ ├── 03_win_loss_distribution.png
│ ├── 04_exit_reason_pnl.png
│ ├── 05_symbol_performance.png
│ ├── 06_duration_vs_pnl.png
│ └── 07_monthly_pnl.png
└── backtest_report.html
# Clone or download this directory
cd backtest_system
# Create virtual environment (recommended)
python -m venv backtest_env
source backtest_env/bin/activate # On Windows: backtest_env\Scripts\activate
# Install dependencies
pip install -r requirements.txt# Run complete pipeline: backtest → analysis → report
python run_backtest.pyThis single command:
- ✅ Installs dependencies
- ✅ Runs backtest on 15 stocks
- ✅ Analyzes all trades
- ✅ Creates visualizations
- ✅ Generates HTML report
results/
├── all_trades_summary.csv ← Open in Excel/Sheets
├── backtest_detailed_report.xlsx ← Multi-sheet analysis
└── backtest_charts/ ← 7 visualization charts
Edit the beginning of ema_strategy_complete.py:
# Line ~330 - STRATEGY PARAMETERS
stocks = [
'JPM', 'AAPL', 'MSFT', # Your stocks here
# Add or remove as needed
]
# Change date range
results_df = run_backtest(stocks,
start_date='2023-01-01',
end_date='2025-12-20')In ema_strategy_complete.py, modify the EMACrossoverStrategy.params:
class EMACrossoverStrategy(bt.Strategy):
params = (
('short_ema', 50), # Change 50EMA period
('long_ema', 200), # Change 200EMA period
('max_loss_eur', 500), # Change max loss
('max_gain_eur', 2000), # Change max gain
('account_size', 1000000.0), # Change starting capital
)Change yfinance interval:
# Line ~163 - Change from:
data = yf.download(symbol, start=start_date, end=end_date,
auto_adjust=False, progress=False)
# To:
data = yf.download(symbol, start=start_date, end=end_date,
interval='1h', # Hourly (limited to 730 bars)
auto_adjust=False, progress=False)
# Or for 15-min:
data = yf.download(symbol, start=start_date, end=end_date,
interval='15m', # 15-minute (limited to 60 days)
auto_adjust=False, progress=False)| Column | Meaning |
|---|---|
| Symbol | Stock ticker |
| Entry_Time | When position was entered |
| Exit_Time | When position was exited |
| Entry_Price | Price at entry |
| Exit_Price | Price at exit |
| Shares | Number of shares traded |
| Position_Value_EUR | Market value (shares × entry_price) |
| PnL_EUR | Profit/loss in EUR |
| Max_Win | Maximum profit if positive |
| Max_Loss | Maximum loss if negative |
| Margin_Used_EUR | Capital tied up (10% of position) |
| At_Risk_Capital_EUR | €500 per trade |
| Candles_In_Trade | How many bars held |
| Exit_Reason | Why trade exited |
| Return_Percent | Return on position |
Sheet 1: All Trades
- Complete trade log with all columns from CSV
Sheet 2: Summary
- Win rate, average PnL, best/worst trades
- Total stats across all symbols
Sheet 3: By Symbol
- Performance breakdown for each stock
- Win rates and total P&L per symbol
Sheet 4: By Exit Reason
- Analysis of each exit condition
- Which exits are most profitable
| Chart | Purpose |
|---|---|
| 01_pnl_distribution.png | Histogram showing profit distribution |
| 02_cumulative_pnl.png | Running total profit over time |
| 03_win_loss_distribution.png | Wins vs losses comparison |
| 04_exit_reason_pnl.png | Profit by exit reason |
| 05_symbol_performance.png | Performance per stock |
| 06_duration_vs_pnl.png | Trade length impact on profit |
| 07_monthly_pnl.png | Monthly aggregated results |
Problem: Backtest runs but generates 0 trades
Solutions:
- Lower
STOCHRSI_THRESHOLDfrom 0.20 to 0.15 - Reduce
BARS_AFTER_CROSSOVERfrom 3 to 1 - Use older date range (earlier start date)
- Check stock symbols are correct
Problem: Python runs out of memory on all 30 stocks
Solution:
# Test on smaller list first
results_df = run_backtest(stocks[:10]) # Just first 10
# Or:
results_df = run_backtest(['AAPL', 'MSFT', 'GOOGL']) # Specific stocksProblem: "Unable to download data" error
Solutions:
- Check internet connection
- Verify ticker symbols (e.g., 'ASML.AS' not just 'ASML')
- Use simpler symbols first: AAPL, MSFT, GOOGL
- Try again later (rate limits)
Problem: Backtest takes hours
Solutions:
- Reduce stock list (start with 5-10 symbols)
- Use shorter date range
- Run on hourly/daily instead of 15-min (less data)
- Close other applications
- Definition: % of trades that were profitable
- Good: >50%
- Excellent: >60%
- Formula: (Winning Trades / Total Trades) × 100
- Definition: Total wins divided by total losses
- Good: >1.0 (profitable)
- Excellent: >2.0 (very profitable)
- Formula: (Sum of Wins) / Abs(Sum of Losses)
- Definition: Risk-adjusted return
- Good: >1.0
- Excellent: >2.0
- Higher is better
- Definition: Largest peak-to-trough decline
- Meaning: Worst case scenario
- Lower is better
- Definition: Mean profit per trade
- Formula: Total PnL / Number of Trades
- Raise max loss threshold (€500 → €300)
- Lower entry threshold (StochRSI 0.20 → 0.30)
- Add more entry filters
- Raise max gain target (€2000 → €3000)
- Trail stops instead of hard exits
- Add profit-taking at partial targets
- Lower position size (2% → 1%)
- Add volatility filter (ATR-based)
- Skip trades in low-liquidity stocks
- Add market regime filter (VIX, trend)
- Optimize EMA periods (test 40/180, 45/200, etc.)
- Add position sizing by volatility
This backtest system can be extended to live trading:
# Switch from yfinance (historical) to broker API:
import ccxt # For crypto
from alpaca_trade_api import REST # For stocks
# Modify data feed in ema_strategy_complete.py
# Add broker integration
# Use IB (Interactive Brokers) or other broker APIsSee Backtrader documentation for live trading integration.
- Backtrader Docs: https://www.backtrader.com/
- yfinance: https://github.com/ranaroussi/yfinance
- Pandas Docs: https://pandas.pydata.org/docs/
- Matplotlib: https://matplotlib.org/
This is for educational and backtesting purposes only.
- Past performance does not guarantee future results
- Backtesting results may not reflect real trading conditions
- Commission, slippage, and gaps are simplified
- Always test thoroughly before live trading
- Risk management is YOUR responsibility
Free to use and modify for personal use.
- Run backtest:
python run_backtest.py - Review results: Open
results/all_trades_summary.csv - Analyze charts: View
results/backtest_charts/ - Optimize parameters: Edit
ema_strategy_complete.py - Test different stocks: Modify stock list
- Try new timeframes: Change interval parameter
Questions? Refer to QUICKSTART.py for more examples and configuration details.