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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: