Skip to content

Jiian/trading-backtest-portfolio

Repository files navigation

trading-backtest-portfolio

This repository stores the codes to backtest several momentum-based trading strategies.

Trading Setup

From the point of view of an asset-management entity, the goal is to maximise the returns through holding positive positions of stocks while adhering to the agreed-upon trading strategy and universe of assets.

Scope and Assumptions

  1. The stocks under consideration are what are famously-known as the "Magnificent 7" companies today: AAPL, MSFT, META, TSLA, NVDA, AMZN and GOOG.
  2. Only long positions are allowed.
  3. Rebalancing is allowed only when a trading signal is triggered.
  4. Backtest for the years 1981 to 2023 inclusive.
  5. Daily level price data.
  6. Evaluate signal post-market, react on market-open.

Financial Parameters

All parameters are contained within the ./config.py file.

Parameter Default Value Description
CAPITAL_0 1,000,000 Initial capital for investment.
COMMISSION_RATE 0.0010 Commission rate per transaction.
SLIPPAGE_RATE 0.0002 Estimated slippage per transaction.
MIN_TRANSC 10 Minimum transaction size (in number of stocks).
MAX_PROP 0.30 Maximum proportion of asset under management (AUM) to be risked per instrument.
RISK_FREE_RATE 0.02 Annual risk-free interest rate.
BUSINESS_DAYS 252 Number of business days in a year.

Strategies Tested

No. Strategy Name Notebook Name Description
1 RSI 01 rsi-only.ipynb The idea is to buy an instrument when it is "oversold" i.e. having a low RSI value.
2 MACD+RSI 02 macd-and-rsi.ipynb Consider buying an instrument when its MACD Difference crosses zero from below. RSI must be relatively oversold to enter this position.
3 HRP 03 hrp-alloc.ipynb An extension of the MACD+RSI strategy. Portfolio allocation is done through Hierarchical Risk Parity.
4 VolAdj 04 voladj-alloc.ipynb An extension of the MACD+RSI strategy. Portfolio allocation is done through proportionally allocating funds based on each instrument's rolling returns per standard deviation.

Technical Setup

Files and Directories

File Name Function Description
backtest.py run_backtest() Given the positions signal and relevant price data, the function in this module runs the backtest to produce two tables - portfolio (in number of shares) and portfolio_value (in $) for each trading day.
calculate_macd.py calculate_macd_signal() Computes the MACD (Moving Average Convergence Divergence) difference indicator signal.
calculate_rsi.py calculate_rsi() Computes the RSI (Relative Strength Index) indicator signal.
generate_positions_macd.py generate_positions_macd() Generates trading positions based on MACD signals. "1" represents a long position is active while "0" represents no active positions.
generate_positions_rsi.py generate_positions_rsi() Generates trading positions based on RSI signals. "1" represents a long position is active while "0" represents no active positions.
hrp.py calculate_hrp_weights() Calculates the Hierarchical Risk Parity (HRP) portfolio allocation weights.
metrics.py calculate_portfolio_metrics() Defines performance metrics for evaluating strategies.
read_data.py read_data() Handles reading and processing data from source.
voladj.py calculate_voladj_weights() Calculates the volatility-adjusted portfolio allocation weights.

Results and Discussion

Metric RSI MACD+RSI HRP VolAdj S&P500
Total Return 1.8662 56.8983 0.2976 2.2947 33.9848
Annualised Return 0.0486 0.1251 0.0062 0.0317 0.1039
Annualised Volatility 0.2143 0.2163 0.0142 0.0827 0.1797
Maximum Drawdown 0.6484 0.4885 0.0631 0.2757 0.5678
Sharpe Ratio 0.1280 0.4527 -0.9762 0.1354 0.4391
Sortino Ratio 0.2012 0.7331 -1.4788 0.2158 0.6841
Total Number of Trades 1,504 1,437 833 754
Average Return per Trade 0.0012 0.0396 0.0004 0.0030
Win Rate 0.6918 0.7519 0.7970 0.6305
Expectancy 6,009 211,358 2,237 9,215
  1. RSI is considered a leading indicator. Using RSI alone to indicate mean-reversion timings produced a return far lower than the S&P500 index.

  2. Adding MACD as a lagging indicator for momentum, the results significantly outperformed the first model. The model has also outperformed the S&P500 index in total returns over the long run.

  3. While it is tempting to minimise the drawdown and volatility of the trading strategy in step 2, both approaches that were experimented with resulted in significantly worse metrics.

    (a) Both HRP and Volatility-adjusted portfolio optimisation approaches aim to reduce volatility in the portfolio by setting a lower weight to the higher volatility instruments.

    (b) Given that the magnificent 7 companies are high-performing in hindsight, they are in a long-term uptrend. In such an uptrend, the returns are usually driven by the more volatile stocks. The de-prooritisation of these stocks resulted in the strategies missing out on the upside.

    (c) Volatility-adjusted portfolio optimisation is unsuitable to be used together with our mean-reverting RSI strategy. By definition, an instrument that is oversold has experienced a recent downtrend. Adjusting the portfolio by looking at its recent volatility-adjusted returns are not helpful, given that the returns are likely poor.

About

A RSI/MACD-based trading strategy backtesting.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published