A momentum-based ETF trading strategy implementation for Interactive Brokers paper trading.
This project implements a momentum-based trading strategy using Interactive Brokers. The strategy selects the top-performing ETFs based on their recent momentum (one-year price change) and rebalances the portfolio once per quarter. The strategy uses historical price data for analysis and backtesting, which is free to access, rather than real-time market data which typically requires additional subscriptions.
The strategy's performance is primarily benchmarked against ACWD (SPDR MSCI All Country World UCITS ETF Accumulation), which tracks the MSCI All Country World Index. ACWD was chosen as the main benchmark because it provides comprehensive exposure to both developed and emerging markets globally, aligning with the strategy's multi-asset, global allocation approach. Additionally, S&P 500 is used as a secondary benchmark since it represents the most widely followed equity index and offers a point of reference familiar to most investors. Comparing against both benchmarks demonstrates how the strategy performs relative to both global (ACWD) and US-centric (S&P 500) passive investment alternatives.
- Performance (2015-2025): 163.8% total return (10.19% CAGR) with a Sharpe ratio of 0.76
- Lower Risk Profile: Maximum drawdown of -27.3% vs. -49.4% for ACWD and -57.4% for S&P 500
- Alpha Generation: +2.67% annual alpha vs. ACWD with only 0.78 beta exposure
- Walk-Forward Validation: 12.64% average annual returns with only 5.48% average drawdown across 14 test periods
- Simple & Robust: Quarterly rebalancing of top-5 momentum ETFs with rank-weighted allocation
- UCITS Compliant: Designed for UK/EU investors with access to UCITS ETFs on Interactive Brokers
- Cost-Effective: Strategy modeled with realistic 1% annual trading costs, TER costs included by IBKR
Please, navigate to "docs" folder for more details: methodology, backtesting results, etc.
momentum-strategy/
├── config.py # Main configuration
├── run_ib_momentum_strategy.py # Main script to run the strategy with IB
├── buy_and_hold_backtest.py # Script for buy-and-hold comparison backtesting
├── requirements.txt # Project dependencies
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # License information
├── momentum_strategy/ # Main package
│ ├── data/ # Data handling modules
│ │ └── ib_data_fetcher.py # IB data fetching implementation
│ ├── strategy/ # Strategy implementation
│ │ └── momentum_strategy.py # Momentum strategy core logic
│ ├── trading/ # Trading execution
│ │ └── ib_executor.py # IB trade execution
│ ├── utils/ # Utilities
│ │ └── logger.py # Logging setup
│ └── backtesting/ # Backtesting modules
│ └── backtest_engine.py # Backtesting implementation
├── data/ # Data files
│ ├── etf_data_sample.csv # Sample ETF data
│ └── wfo_comparison.csv # Walk-forward optimization comparison data
├── docs/ # Documentation
│ ├── methodology.md # Methodology documentation
│ ├── results.md # Performance results
│ ├── experiments.md # Experimental findings
│ └── figures/ # Documentation images
├── notebooks/ # Jupyter notebooks
│ └── Momentum_Strategy_Classic.ipynb # Strategy analysis notebook
├── tests/ # Test suite
│ └── test_core_functionality.py # Core functionality tests
├── test_momentum_strategy.py # Strategy tests
├── test_tws_connection.py # IB connectivity tests
└── run_tests.py # Test runner script
- Python 3.8+
- Interactive Brokers Trader Workstation (TWS) or IB Gateway
- IB Paper Trading account
- Clone the repository:
git clone https://github.com/yourusername/momentum-strategy.git
cd momentum-strategy- Create and activate a virtual environment:
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate- Install dependencies:
pip install -r requirements.txtEdit the config.py file to customize:
- IB connection settings
- List of ETFs to trade
- Strategy parameters (lookback period, number of assets, etc.)
-
Start Interactive Brokers TWS or IB Gateway and ensure it's configured for paper trading.
-
Run the strategy:
python run_ib_momentum_strategy.py--mode: Trading mode (backtest,paper,live) - Default:paper--duration: Duration of historical data to fetch (e.g.,365 D,1 Y) - Default:365 D--bar-size: Bar size for data (e.g.,1 day,1 hour) - Default:1 day--top-n: Number of top assets to include in portfolio - Default:5--lookback: Lookback period for momentum calculation (trading days) - Default:252--no-trade: Run in simulation mode with no actual trading--verbose: Enable verbose logging
Example:
python run_ib_momentum_strategy.py --top-n 3 --lookback 126 --no-tradeThis strategy is designed to work with IB Paper Trading. To use it:
- Ensure TWS or IB Gateway is running and connected to paper trading
- Default paper trading port is 7497 (configured in
config.py) - Important: Make sure to enable API connections in TWS/IB Gateway:
- In TWS: Go to Edit > Global Configuration > API > Settings and enable "Enable ActiveX and Socket Clients"
- Set the port number (default 7497 for paper trading)
- Add your IP address to trusted IPs (use 127.0.0.1 for local connections)
- Check "Allow connections from localhost only" for security if running locally
- Consider unchecking "Read-Only API" to allow the strategy to place trades
- Run the strategy with desired parameters
- Monitor the logs for execution details
Note: Common errors like "Connection refused" typically occur when API connections are not properly enabled or when TWS/IB Gateway is not running.
CAUTION: Switch to live trading only after extensive paper trading testing!
To enable live trading, modify config.py to use port 7496 (live trading port) and run with:
python run_ib_momentum_strategy.py --mode liveThis project includes a comprehensive test suite to ensure the strategy functions correctly:
- Run all tests using the provided script:
python run_tests.py- Or run specific test modules:
python -m unittest tests/test_core_functionality.py- Test connection to Interactive Brokers:
python test_tws_connection.py- Test coverage includes:
- Core strategy functionality
- Data handling and processing
- Portfolio allocation logic
- Signal generation and verification
Tests use historical data to verify strategy performance and can be run without an active IB connection.
This implementation uses historical data rather than real-time market data for several reasons:
- Cost-effective: Historical data is available for free through the IB API, while real-time market data often requires paid subscriptions.
- Sufficient for strategy: For a momentum strategy that rebalances quarterly, daily closing prices provide adequate precision without the need for intraday data.
- Reduced complexity: Historical data processing is simpler and less resource-intensive than handling real-time streams.
- Accessibility: Makes the strategy more accessible to users without premium data subscriptions.
If you need real-time execution, you can modify the ib_data_fetcher.py module to use real-time data feeds, but be aware this may require market data subscriptions through your Interactive Brokers account.
To customize the strategy:
- Modify
config.pyto change the list of ETFs or other parameters - Update the momentum calculation logic in
momentum_strategy.py - Add other technical indicators in a new strategy class