Algorithmic Testing Stock Portfolios to Optimize the Risk/Reward Ratio

Investors can optimize their stock portfolio by invoking backtesting within the realm of algorithmic trading. The goal is to optimize the specific portfolio by maximizing returns and the Sharpe ratio.

Stock monitor
Photo by Behnam Norouzi on Unsplash
Photo by Behnam Norouzi on Unsplash

The workflow in Python consists of the following steps:

  • Import/install relevant libraries
  • Define the stock portfolio & time interval
  • Download historical data from Yfinance
  • Apply the MVA strategy function to stock data
  • Calculate cumulative returns and the annual Sharpe ratio
  • Plot cumulative returns of individual stocks and the portfolio return

Let’s set the working directory YOURPATH

import os

os.chdir(‘YOURPATH’)

and import/install the following libraries

!pip install nsepy

import warnings
warnings.filterwarnings(‘ignore’)

import numpy as np
import pandas as pd
from datetime import datetime as dt
import yfinance as yf
import nsepy
from statistics import mean

Let’s define the following functions

Get daily data from Yfinance

def get_daily_data(symbol, start, end):
data = yf.download(tickers=symbol, start=start, end=end)
return data

MVA strategy on close price data

def ma(data,ma1,ma2):
# calculating moving averages
data[‘ma_short’] = data[‘Close’].ewm(span=ma1).mean().shift()
data[‘ma_long’] = data[‘Close’].ewm(span=ma2).mean().shift()

# creating positions
# data["position"] = [0]*len(data)
data['position'] = np.where(data["ma_short"] > data["ma_long"], 1, 0)    
data["strategy_returns"] = data["bnh_returns"] * data["position"]

# returning strategy returns
return data["strategy_returns"]
Cumulative returns function

def get_cumulative_return(df):
return list(df.cumsum())[-1]

Annual Sharpe ratio function

def get_annualized_sharpe_ratio(df):
return 252**(1/2) * (df.mean() / df.std())

Let’s define the backtesting parameters
days = 2000
end = dt.today()
start = end – pd.Timedelta(days=days)

and the stock portfolio
portfolio_stocks = [“OXY”,”LMT”,”SNOW”,”KBH”,”ENPH”,”PDD”,”SPY”,”QQQ”,”IWM”,”KWEB”,]

based on the available market research studies, technical analysis, updates, market trends, and stock screeners.

Let’s define a data frame to store portfolio returns
portfolio_strategy_returns = pd.DataFrame()
portfolio_bnh_returns = pd.DataFrame()

Buy and hold returns for individual stocs
bnh_stock_returns = []
bnh_stock_sharpe = []

iterating over stocks in the portfolio
for stock in portfolio_stocks:
data = get_daily_data(stock, start, end)

# Calculating daily returns
data["bnh_returns"] = np.log(data["Close"]/data["Close"].shift())

portfolio_strategy_returns[stock] = ma(data,ma1 = 3, ma2 = 8)

bnh_stock_returns.append(get_cumulative_return(data["strategy_returns"]))
bnh_stock_sharpe.append(get_annualized_sharpe_ratio(data["strategy_returns"]))
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed

print(“\nSTRATEGY RETURNS ON PORTFOLIO”)
portfolio_strategy_returns[“Portfolio_rets”] = portfolio_strategy_returns.mean(axis=1)
portfolio_strategy_returns.round(decimals = 4).tail(10)

portfolio and stock returns

perf = pd.DataFrame(index=portfolio_stocks,columns=[“Cumulative returns”,”Annualized Sharpe Ratio”])

for i,stock in enumerate(portfolio_stocks):
cum_ret = bnh_stock_returns[i]
anu_shp = bnh_stock_sharpe[i]
perf.loc[stock] = [cum_ret,anu_shp]

perf

Cumulative returns and annual Sharpe ratio table
Cumulative returns
Annualized sharpe ratio

perf.mean()

Cumulative returns         0.722194
Annualized Sharpe Ratio    0.369893
dtype: float64

print(“Cumulative returns MA Strategy :”,get_cumulative_return(portfolio_strategy_returns[“Portfolio_rets”]))
print(“Annualized sharpe ratio MA Strategy :”,get_annualized_sharpe_ratio(portfolio_strategy_returns[“Portfolio_rets”]))
print(“\n”)

Cumulative returns MA Strategy                  : 0.832610455484652
Annualized sharpe ratio MA Strategy             : 1.0024619067938556

import matplotlib.pyplot as plt
colors = [‘tab:cyan’,’tab:purple’,’tab:pink’,’tab:orange’,’tab:blue’,’tab:green’,’tab:gray’,’tab:olive’,’tab:brown’,’tab:red’,”k”]
portfolio_strategy_returns.cumsum().plot(figsize=(20,10), title=”MOVING AVERAGES STRATEGY CUMULATIVE RETURNS”, color=colors)
plt.savefig(‘portfolioreturnspup.png’)

PLot MVA strategy cumulative returns of individual stocks and the entire portfolio

The plot shows that the portfolio has almost no variation as compared to the individual stock performance. The Sharpe ratio of 1.0024 indicates the acceptable risk exposure.

Infographic

Infographic 2000 days MA backtesting of a selected stock portfolio indicates the acceptable risk exposure

Go back

Your message has been sent

Warning


Discover more from Our Blogs

Subscribe to get the latest posts sent to your email.

Leave a comment

Discover more from Our Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading