<jupyter_start><jupyter_text>Lab 04 - Trend-Following Trading StrategiesMachine Learning, University of St. Gallen, Spring Term 2022 In this introductory lab, we create our first **financial data science process**. The main objective of this lab is to walk you through the general process of implementing and evaluating a simple **trend-following** trading strategy. To achieve this, we will follow the distinct process steps as outlined below:  As always, pls. don't hesitate to ask all your questions either during the lab, post them in our CANVAS (StudyNet) forum (https://learning.unisg.ch), or send us an email (using the course email).  1. Lab Objectives: After today's lab you should be able to:    > 1. Implement a **trend-following trading strategy** and apply it to distinct financial instruments.> 2. Convert the trading strategy results into **trade signals** to be used in backtest.> 3. Understand how to use the **python backtesting bt** library to backtest the implemented strategy.> 4. Interpret the backtests results using the distinct **backtest performance** measures. Before we start let's watch a motivational video:<jupyter_code>from IPython.display import YouTubeVideo
# Nvidia GTC 2017: "I Am AI" Opening in Keynote"
# YouTubeVideo('SUNPrR4o5ZA', width=800, height=600)<jupyter_output><empty_output><jupyter_text>2. Setup of the Jupyter Notebook Environment Suppress potential warnings due to recent library enhancements:<jupyter_code>import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>We need to import a couple of Python libraries that allow for data analysis and data visualization. In this lab will use the `Pandas`, `NumPy`, `BT` and the `Matplotlib` library. Let's import the libraries by the execution of the statements below:<jupyter_code># import python utility libraries
import os as os
import datetime as dt
import itertools as it

# import python data science libraries
import pandas as pd
import numpy as np

# import the pandas financial data reader library
import pandas_datareader as dr

# import the matplotlib and seaborn visualization library
import matplotlib.pyplot as plt
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Install the Python `BT` backtesting library and upgrade the `Pandas` datareader (a restart of the Colab runtime environment is potentially required):<jupyter_code>!pip install bt
!pip install --upgrade pandas-datareader<jupyter_output><empty_output><jupyter_text>Upon successful installation let's import the Python `BT` backtesting library:<jupyter_code>import bt as bt # library to backtest trading signals<jupyter_output><empty_output><jupyter_text>Let's also set a couple of general plot parameters:<jupyter_code># set general plot parameters
plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [10, 5]
plt.rcParams['figure.dpi']= 150<jupyter_output><empty_output><jupyter_text>Enable inline Jupyter notebook plotting:<jupyter_code>%matplotlib inline<jupyter_output><empty_output><jupyter_text>Import Google's GDrive connector and mount your GDrive directories:<jupyter_code># import the Google Colab GDrive connector
from google.colab import drive

# mount GDrive inside the Colab notebook
drive.mount('/content/drive')<jupyter_output><empty_output><jupyter_text>Create a structure of Colab Notebook sub-directories inside of GDrive to store (1) the data as well as (2) the trained neural network models:<jupyter_code># create Colab Notebooks directory
notebook_directory = '/content/drive/MyDrive/Colab Notebooks'
if not os.path.exists(notebook_directory): os.makedirs(notebook_directory)

# create the data sub-directory
data_directory = './data_mav'
if not os.path.exists(data_directory): os.makedirs(data_directory)<jupyter_output><empty_output><jupyter_text>3. Acquire the Financial Data In this section of the lab notebook, we will aquire historic daily stock market data of the **"International Business Machines" (IBM)** corporation (ticker symbol: "IBM"). Thereby, we will utilize the `datareader` of the `Pandas` library that provides the ability to interface the `Yahoo` finance API. Let's first specify the start date and end date of the data download. We aim to download market price data starting from the **31.12.1990** until the **31.12.2017** to develop and evaluate a simple momentum trading strategy:<jupyter_code># set to start and end date of the data download
start_date = dt.datetime(1990, 12, 31)
end_date = dt.datetime(2017, 12, 31)<jupyter_output><empty_output><jupyter_text>Download the **daily** "International Business Machines" (IBM) market data of the defined timeframe using the `datareader`'s `Yahoo` finance API:<jupyter_code># download ibm market data
ibm_data = dr.data.DataReader('IBM', data_source='yahoo', start=start_date, end=end_date)<jupyter_output><empty_output><jupyter_text>4. Pre-Process the Financial Data Inspect the top 10 records of the `IBM` data downloaded:<jupyter_code>ibm_data.head(10)<jupyter_output><empty_output><jupyter_text>Visually inspect the **adjusted closing price** of the downloaded `IBM` data:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 5]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot ibm stock daily adjusted closing prices
ax.plot(ibm_data.index, ibm_data['Adj Close'], color='#9b59b6')

# rotate x-ticks
for tick in ax.get_xticklabels():
    tick.set_rotation(45)

# set axis labels
ax.set_xlabel('[time]', fontsize=10)
ax.set_xlim([start_date, end_date])
ax.set_ylabel('[adjusted closing price]', fontsize=10)

# set plot title
plt.title('International Business Machines Corporation (IBM) - Historical Stock Prices', fontsize=10);<jupyter_output><empty_output><jupyter_text>Save the downloaded `IBM` data to the local directory:<jupyter_code># save retrieved data to local data directory
ibm_data.to_csv(os.path.join(data_directory, 'ibm_data_2000_2017_daily.csv'), sep=';', encoding='utf-8')<jupyter_output><empty_output><jupyter_text>5. Data Analysis - Moving Average Crossover Strategy Implementation Let's implement a simple **Moving Average Crossover** trading strategy. In general, **crossover trading** refers to the idea that changes of market situations can be determined based on price “breakouts”. A crossover can be interpreted as another measure of a financial instruments momentum. In the past crossover signals have been extensively used to determine that it’s time to either buy or sell the underlying asset.The price crossover signals of a simple **Moving Average Crossover** trading strategy are triggered by the following events:>- Generate a **short** trading signal once the price of a financial instrument drops below the general price trend, e.g., 100-days moving average band ("Sell Sign Crossover", left image below). >- Generate a **long** trading signal once the price of a financial instrument exceeds the general price trend, e.g., 100-days moving average band ("Buy Sign Crossover", right image below).  An enhancement of the **Moving Average Crossover** is to apply two moving averages to a chart: one long running moving average (e.g., a 200-days SMAV) and one short running moving average (e.g., 20-days SMAV). Once the short running moving average crosses above the long running moving average a **Buy** or **Long** signal is triggered, as it indicates that the trend is shifting up (this is known as a "golden cross"). On the other hand, when the short running moving average crosses below the long running moving average, a **Sell** or **Short** signal is triggered, as it indicates that the trend is shifting down (his is known as a "dead/death cross") Let's start implementing this enhanced trading strategy by setting the distinct moving average window sizes that specify the number of historical daily adjusted closing prices of the IBM stock to be considered in the calculation of the rolling moving average:<jupyter_code>cross_mav_days_15 = 15  # set "fast" short-running moving average indicator lookback, days = 15
cross_mav_days_60 = 60  # set "slow" short-running moving average indicator lookback, days = 60
cross_mav_days_200 = 200  # set "trend" long-running moving average indicator lookback, days = 200<jupyter_output><empty_output><jupyter_text>Calculate the rolling moving averages of window sizes: 15 days, 50 days and 200 days. In general the **Simple Moving Average (SMAV)** of a financial instrument $i$ (e.g., a stock, commodity, fx-rate) is defined as the mean of the previous $n$ prices, formally denoted by:  $$SMA_{i}(t)=\frac{1}{n} \sum_{k=0}^{n-1} p_{i}(t-k)$$ were $t$ denotes the current point in time and $n$ the lookback. We can calculate the SMAV by just using the Pandas `rolling()` and `mean()`function:<jupyter_code>cross_mav_15 = pd.Series(ibm_data['Adj Close'].rolling(window = cross_mav_days_15).mean(), name = 'SMAV_15')
cross_mav_60 = pd.Series(ibm_data['Adj Close'].rolling(window = cross_mav_days_60).mean(), name = 'SMAV_60')
cross_mav_200 = pd.Series(ibm_data['Adj Close'].rolling(window = cross_mav_days_200).mean(), name = 'SMAV_200')<jupyter_output><empty_output><jupyter_text>Merge the rolling moving average values with the original market data (adjusted closing prices):<jupyter_code>cross_mav_ibm_data = ibm_data.join(cross_mav_15)
cross_mav_ibm_data = cross_mav_ibm_data.join(cross_mav_60)
cross_mav_ibm_data = cross_mav_ibm_data.join(cross_mav_200)<jupyter_output><empty_output><jupyter_text>Inspect and validate the daily adjusted closing prices of the IBM stock as well as the derived moving average values starting from the first obtained 200-day moving average market price:<jupyter_code>cross_mav_ibm_data[['Adj Close', 'SMAV_15', 'SMAV_60', 'SMAV_200']].iloc[200:210]<jupyter_output><empty_output><jupyter_text>Plot the historical daily adjusted closing prices of the IBM stock (blue) as well as its 15 days (green), 60 days (red) as well as 200 days (yellow) rolling moving averages:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 5]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot adjusted closing prices and moving averages
ax.plot(cross_mav_ibm_data['Adj Close'], lw=1.0, color='#9b59b6', label='Closing Prices (purple)')
ax.plot(cross_mav_ibm_data['SMAV_15'], color='C1',lw=1.0, label='15-day MAV (green)')
ax.plot(cross_mav_ibm_data['SMAV_60'], color='C1',lw=1.0, label='60-day MAV (red)')
ax.plot(cross_mav_ibm_data['SMAV_200'], color='C4', lw=1.0, label='200-day MAV (yellow)')

# rotate x-tick labels
for tick in ax.get_xticklabels():
    tick.set_rotation(45)
    
# set axis labels
ax.set_xlabel('[time]', fontsize=10)
ax.set_xlim([start_date, end_date])
ax.set_ylabel('[market price]', fontsize=10)

# set plot legend
plt.legend(loc="upper left", numpoints=1, fancybox=True)

# set plot title
plt.title('International Business Machines Corporation (IBM) - Daily Historical Stock Closing Prices', fontsize=10);<jupyter_output><empty_output><jupyter_text>6. Moving Average Crossover Signal Generation Derive trading signals from of two distinct moving average crossover trading strategy configurations. We will generate a **long-signal** (+1.0) for the time intervals where the fast moving averages are above the 200-day moving average. In addition we generate a **short-signal** (-1.0) for the time intervals where the fast moving averages are below the 200-day moving average:<jupyter_code># create 'fast' trend-following signals
cross_mav_ibm_data['SIGNAL_15'] = 0.0
cross_mav_ibm_data.loc[cross_mav_ibm_data['SMAV_15'] > cross_mav_ibm_data['SMAV_200'], 'SIGNAL_15'] = 1.0
cross_mav_ibm_data.loc[cross_mav_ibm_data['SMAV_15'] < cross_mav_ibm_data['SMAV_200'], 'SIGNAL_15'] = -1.0

# create 'slow' trend-following signals
cross_mav_ibm_data['SIGNAL_60'] = 0.0
cross_mav_ibm_data.loc[cross_mav_ibm_data['SMAV_60'] > cross_mav_ibm_data['SMAV_200'], 'SIGNAL_60'] = 1.0
cross_mav_ibm_data.loc[cross_mav_ibm_data['SMAV_60'] < cross_mav_ibm_data['SMAV_200'], 'SIGNAL_60'] = -1.0<jupyter_output><empty_output><jupyter_text>In addition, let's also prepare a backtest of a **"baseline"** in terms of a simple **buy-and-hold** trading strategy for comparison purposes. Our buy-and-hold strategy sends a "long" (+1.0) signal for each time step:<jupyter_code>cross_mav_ibm_data['SIGNAL_BASE'] = 1.0<jupyter_output><empty_output><jupyter_text>Prepare the trading signal data to be utilized in backtesting the long-/short-term moving-average trading strategy:<jupyter_code># convert signals to Pandas DataFrame
cross_mav_ibm_signal_data = pd.DataFrame(cross_mav_ibm_data[['SIGNAL_15', 'SIGNAL_60', 'SIGNAL_BASE']], columns=['SIGNAL_15', 'SIGNAL_60', 'SIGNAL_BASE'])

# convert pandas DataFrame index to datatype: datetime
cross_mav_ibm_signal_data = cross_mav_ibm_signal_data.set_index(pd.to_datetime(ibm_data.index))<jupyter_output><empty_output><jupyter_text>Inspect top 10 rows of the prepared trading signals:<jupyter_code>cross_mav_ibm_signal_data.head(10)<jupyter_output><empty_output><jupyter_text>Inspect some of the exemplary signal deviations between the 15-days and 60-days crossover moving average trading strategies:<jupyter_code>cross_mav_ibm_signal_data[cross_mav_ibm_signal_data['SIGNAL_15'] != cross_mav_ibm_signal_data['SIGNAL_60']].head(10)<jupyter_output><empty_output><jupyter_text>Visualize the prepared trading signals:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 5]
fig, ax = plt.subplots(ncols=1, nrows=3)

ax[0].plot(cross_mav_ibm_signal_data['SIGNAL_15'], lw=1.0, color='C2', label='SMAV 16 (red)')
ax[1].plot(cross_mav_ibm_signal_data['SIGNAL_60'], lw=1.0, color='C1', label='SMAV 60 (green)')
ax[2].plot(cross_mav_ibm_signal_data['SIGNAL_BASE'], lw=1.0, color='C3', label='BASE (purple)')
    
# set axis labels
plt.xlabel('[time]', fontsize=10)
ax[0].set_xlim([start_date, end_date])
ax[0].set_ylabel('[smav 15 signal]', fontsize=10)
ax[1].set_xlim([start_date, end_date])
ax[1].set_ylabel('[smav 60 signal]', fontsize=10)
ax[2].set_xlim([start_date, end_date])
ax[2].set_ylabel('[base signal]', fontsize=10)

# rotate the x-axis labels
for tick in ax[0].get_xticklabels():
    tick.set_rotation(45)
    
for tick in ax[1].get_xticklabels():
    tick.set_rotation(45)
    
for tick in ax[2].get_xticklabels():
    tick.set_rotation(45)

# set plot title
ax[0].set_title('International Business Machines Corporation (IBM) - 15 days Crossover Moving Average Trading Signals', fontsize=10)
ax[1].set_title('International Business Machines Corporation (IBM) - 60 days Crossover Moving Average Trading Signals', fontsize=10)
ax[2].set_title('International Business Machines Corporation (IBM) - Baseline Buy and Hold Trading Signals', fontsize=10)

# reset plot layout
plt.tight_layout()<jupyter_output><empty_output><jupyter_text>Let's determine the total number of **long-short signal changes** of the distinct trading strategies:<jupyter_code># signal changes of the 15-200 days moving average crossover trading strategy
len(list(it.groupby(cross_mav_ibm_signal_data['SIGNAL_15'], lambda x: x > 0)))
# signal changes of the 60-200 days moving average crossover trading strategy
len(list(it.groupby(cross_mav_ibm_signal_data['SIGNAL_60'], lambda x: x > 0)))
# signal changes of the baseline buy and hold trading strategy
len(list(it.groupby(cross_mav_ibm_signal_data['SIGNAL_BASE'], lambda x: x > 0)))<jupyter_output><empty_output><jupyter_text>7. Moving Average Crossover Signal Backtest Prepare the market data to be utilized in backtesting the crossover moving average trading strategy configurations:<jupyter_code># extract the ibm stock closing prices
ibm_market_data = pd.DataFrame(ibm_data['Adj Close'], columns=['Adj Close'])

# rename the 'close' column to 'ibm' (since this is the column we want to allocate to in the backtest)
ibm_market_data = ibm_market_data.rename(columns={'Adj Close': 'IBM'})

# convert pandas DataFrame index to datatype: datetime
ibm_market_data = ibm_market_data.set_index(pd.to_datetime(ibm_data.index))<jupyter_output><empty_output><jupyter_text>Inspect top 10 rows of the prepared market data:<jupyter_code>ibm_market_data.head(10)<jupyter_output><empty_output><jupyter_text>Implementing a simple Moving Average Trading Strategy by interfacing the Python `bt`'s Algo class:<jupyter_code>class MovingAverageStrategy(bt.Algo):
    
    # inits the strategy
    def __init__(self, signals):
        
        # set class signals
        self.signals = signals
        
    # calss the trading strategy
    def __call__(self, target):
        
        # case: current timestep in signals
        if target.now in self.signals.index[1:]:
            
            # get actual signal
            signal = self.signals[target.now]
            
            # set target weights according to signal
            target.temp['weights'] = dict(IBM=signal)
            
        # return 'True' since we want to move on to the next timestep
        return True<jupyter_output><empty_output><jupyter_text>Define the moving average trading strategy backtest algorithm stack. **Note:** That in the Python `bt` library a trading strategy usually consists of a so-called **stack of algorithms**. For each timestep of our backtest timeframe, the `bt` library executes all algorithm of the stack in sequential order. Each moving average strategy we aim to design and backtest consists in total of three algorithms, briefly described in the following: > 1. `bt.algos.SelectAll()`: Selects all available stocks for trading except stock prices that correspond to NaN or 0.00.  > 2. `MovingAverageStrategy()`: Assigns the calculated signal in terms of a weight value to the IBM stock.> 3. `bt.algos.Rebalance()`: Rebalances the available capital based on the weights assigned to each stock. Define the long-/short-term moving average trading strategy backtest algorithm stack:<jupyter_code>cross_mav_ibm_strategy_15 = bt.Strategy(name='smav_15', algos=[bt.algos.SelectAll(), MovingAverageStrategy(cross_mav_ibm_signal_data['SIGNAL_15']), bt.algos.Rebalance()])
cross_mav_ibm_strategy_60 = bt.Strategy(name='smav_60', algos=[bt.algos.SelectAll(), MovingAverageStrategy(cross_mav_ibm_signal_data['SIGNAL_60']), bt.algos.Rebalance()])
cross_mav_ibm_strategy_base = bt.Strategy(name='base', algos=[bt.algos.SelectAll(), MovingAverageStrategy(cross_mav_ibm_signal_data['SIGNAL_BASE']), bt.algos.Rebalance()])<jupyter_output><empty_output><jupyter_text>Let's now define the trading ('fees') commissions used in each rebalancing time-step of a backtest. To achieve this, the `bt` library expects a callable function that expects the following two parameters as an input:> - the 'quantity', denoted by `q`, of rebalanced assets at a backtest time-step;> - the 'price', denoted by `p`, of rebalanced assets at a backtest time-step.Let's implement such a callable function defining a flat trading fee of **USD 1.00** per trade (or a fee of **1\% (0.01)** per quantity of rebalanced asset):<jupyter_code># init trading fees function
def trading_fees_function(q, p):
    
    # calcluate trading fees (rebalanced-quantity * trading-fee)
    # fees = q * 0.01 # non-flat fee of 1% per quantity of rebalanced asset
    fees = 1.00 # flat fee of USD 1.00 per trade
    
    # return the total trading fees
    return fees<jupyter_output><empty_output><jupyter_text>Upon completion of defining the long-/sort-term moving average strategies let's now init the corresponding backtests using (1) both strategies as well as (2) the market data that we aim to evaluate during the backtest:<jupyter_code>ibm_backtest_cross_mav_15 = bt.Backtest(strategy=cross_mav_ibm_strategy_15, data=ibm_market_data, name='ibm_backtest_smav_15', commissions=trading_fees_function, progress_bar=True)
ibm_backtest_cross_mav_60 = bt.Backtest(strategy=cross_mav_ibm_strategy_60, data=ibm_market_data, name='ibm_backtest_smav_60', commissions=trading_fees_function, progress_bar=True)
ibm_backtest_cross_mav_base = bt.Backtest(strategy=cross_mav_ibm_strategy_base, data=ibm_market_data, name='ibm_backtest_smav_base', commissions=trading_fees_function, progress_bar=True)<jupyter_output><empty_output><jupyter_text>Now, let's run the backtest of both configurations of the crossover moving average strategy as well as the defined baseline:<jupyter_code>backtest_results_ibm = bt.run(ibm_backtest_cross_mav_15, ibm_backtest_cross_mav_60, ibm_backtest_cross_mav_base)<jupyter_output><empty_output><jupyter_text>Inspect the individual backtest results and performance measures:<jupyter_code>backtest_results_ibm.display()<jupyter_output><empty_output><jupyter_text>Collect detailed backtest performance per timestep of the "fast" **15-200 days** crossover moving average strategy:<jupyter_code>backtest_sma_15_ibm_details = ibm_backtest_cross_mav_15.strategy.prices.to_frame(name='Rel. EQUITY')
backtest_sma_15_ibm_details['Abs. EQUITY'] = ibm_backtest_cross_mav_15.strategy.values # equity per timestep
backtest_sma_15_ibm_details['CASH'] = ibm_backtest_cross_mav_15.strategy.cash # cash per timestep
backtest_sma_15_ibm_details['POSITIONS'] = ibm_backtest_cross_mav_15.strategy.positions # positions per timestep
backtest_sma_15_ibm_details['FEES'] = ibm_backtest_cross_mav_15.strategy.fees # trading fees per timestep<jupyter_output><empty_output><jupyter_text>Inspect detailed backtest results per timestep:<jupyter_code>backtest_sma_15_ibm_details.head(10)<jupyter_output><empty_output><jupyter_text>Visualize the monthly returns obtained by the **15-200 days** crossover moving average strategy:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 10]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot heatmap of monthly returns generated by the strategy
ax = sns.heatmap(ibm_backtest_cross_mav_15.stats.return_table, annot=True, cbar=True, vmin=-0.5, vmax=0.5)

# set axis labels
ax.set_xlabel('[month]', fontsize=10)
ax.set_ylabel('[year]', fontsize=10)

# set plot title
ax.set_title('International Business Machines Corporation (IBM) - Monthly Returns SMAV 15-200 Strategy', fontsize=10);<jupyter_output><empty_output><jupyter_text>Collect detailed backtest performance per timestep of the "slow" **60-200 days** crossover moving average strategy:<jupyter_code>backtest_sma_60_ibm_details = ibm_backtest_cross_mav_60.strategy.prices.to_frame(name='Rel. EQUITY')
backtest_sma_60_ibm_details['Abs. EQUITY'] = ibm_backtest_cross_mav_60.strategy.values # equity per timestep
backtest_sma_60_ibm_details['CASH'] = ibm_backtest_cross_mav_60.strategy.cash # cash per timestep
backtest_sma_60_ibm_details['POSITIONS'] = ibm_backtest_cross_mav_60.strategy.positions # positions per timestep
backtest_sma_60_ibm_details['FEES'] = ibm_backtest_cross_mav_60.strategy.fees # fees per timestep<jupyter_output><empty_output><jupyter_text>Inspect detailed backtest results per timestep:<jupyter_code>backtest_sma_60_ibm_details.head(10)<jupyter_output><empty_output><jupyter_text>Visualize the monthly returns obtained by the **60-200 days** crossover moving average strategy:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 10]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot heatmap of monthly returns generated by the strategy
ax = sns.heatmap(ibm_backtest_cross_mav_60.stats.return_table, annot=True, cbar=True, vmin=-0.5, vmax=0.5)

# set axis labels
ax.set_xlabel('[month]', fontsize=10)
ax.set_ylabel('[year]', fontsize=10)

# set plot title
ax.set_title('International Business Machines Corporation (IBM) - Monthly Returns SMAV 60-200 Strategy', fontsize=10);<jupyter_output><empty_output><jupyter_text>Collect detailed backtest performance per timestep of the "long only" baseline strategy:<jupyter_code>backtest_sma_base_ibm_details = ibm_backtest_cross_mav_base.strategy.prices.to_frame(name='Rel. EQUITY')
backtest_sma_base_ibm_details['Abs. EQUITY'] = ibm_backtest_cross_mav_base.strategy.values # equity per timestep
backtest_sma_base_ibm_details['CASH'] = ibm_backtest_cross_mav_base.strategy.cash # cash per timestep
backtest_sma_base_ibm_details['POSITIONS'] = ibm_backtest_cross_mav_base.strategy.positions # positions per timestep
backtest_sma_base_ibm_details['FEES'] = ibm_backtest_cross_mav_base.strategy.fees # fees per timestep<jupyter_output><empty_output><jupyter_text>Inspect detailed backtest results per timestep:<jupyter_code>backtest_sma_base_ibm_details.head(10)
plt.rcParams['figure.figsize'] = [15, 10]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot heatmap of monthly returns generated by the strategy
ax = sns.heatmap(ibm_backtest_cross_mav_base.stats.return_table, annot=True, cbar=True, vmin=-0.5, vmax=0.5)

# set axis labels
ax.set_xlabel('[month]', fontsize=10)
ax.set_ylabel('[year]', fontsize=10)

# set plot title
ax.set_title('International Business Machines Corporation (IBM) - Monthly Returns SMAV Baseline', fontsize=10);<jupyter_output><empty_output><jupyter_text>Visualize each strategie's backtest equity progression over time:<jupyter_code>plt.rcParams['figure.figsize'] = [15, 5]
fig = plt.figure()
ax = fig.add_subplot(111)

# plot equity progression of the distinct trading strategies
ax.plot(backtest_sma_15_ibm_details['Rel. EQUITY'], color='C2',lw=1.0, label='15-day SMAV strategy (red)')
ax.plot(backtest_sma_60_ibm_details['Rel. EQUITY'], color='C1',lw=1.0, label='60-day SMAV strategy (green)')
ax.plot(backtest_sma_base_ibm_details['Rel. EQUITY'], color='C3',lw=1.0, label='Base SMAV strategy (purple)')

# rotate x-tick labels
for tick in ax.get_xticklabels():
    tick.set_rotation(45)
    
# set axis labels
ax.set_xlabel('[time]', fontsize=10)
ax.set_xlim([start_date, end_date])
ax.set_ylabel('[rel. equity]', fontsize=10)

# set plot legend
plt.legend(loc="upper left", numpoints=1, fancybox=True)

# set plot title
plt.title('International Business Machines Corporation (IBM) - Backtest % Equity Progression', fontsize=10);<jupyter_output><empty_output><jupyter_text>8. Exercises: We recommend you to try the following exercises as part of the lab: **1. Evaluation of distinct daily moving average parameters.**> Evaluate the simple crossover moving average trading strategy using distinct moving average look-backs, e.g., 10 days, 30 days, 50 days, 200 days and 300 days. Compare the performance of the lookback parametrizations in terms of total-return, equity progression and yearly sharpe-ratio. Gain an intuition about the years in which the strategy didn't perform well and the potential reason for the poor performance.<jupyter_code># ***************************************************
# INSERT YOUR CODE HERE
# ***************************************************<jupyter_output><empty_output><jupyter_text>**2. Evaluation of distinct trading frequencies.** >Decreasing the lookback in calculating the moving average may results in a significant increase of the corresponding trading frequency (referred to as 'signal changes', and defined as a change of the trading signal from -1 to 1 and vice versa). Calculate the number of signal changes for each of the distinct lookback parametrizations evaluated in the first exercise.<jupyter_code># ***************************************************
# INSERT YOUR CODE HERE
# ***************************************************<jupyter_output><empty_output><jupyter_text>**3. Consideration of trading commissions and impact on strategy performance.** >Run the backtest of the lookback parametrizations evaluated in exercise 1. but change the commission per trade. Set the trading commission of each backtest to (i) 10 USD per trade and (ii) 1% of the price per share. Determine the impact of such a trading fee on the performance of each strategy in terms of total-return, equity progression and yearly sharpe-ratio.<jupyter_code># ***************************************************
# INSERT YOUR CODE HERE
# ***************************************************<jupyter_output><empty_output><jupyter_text>**4. Optimization of the simple crossover moving average trading strategy parameters.**>The lab notebook backtest results obtained for the simple crossover moving average trading strategy reveals that we didn't identify a well-performing strategy parametrization yet. Grid search the parameter space of the strategy to determine a parametrization that results in a positive total-return within the time interval 30.12.1990 until 31.12.2017.<jupyter_code># ***************************************************
# INSERT YOUR CODE HERE
# ***************************************************<jupyter_output><empty_output><jupyter_text>**5. Implementation of a long-/short-term exponential moving average trading strategy.**>A potential further enhancement of the simple crossover moving average trading strategy lies in the application of exponentially weighted moving averages. Adapt the crossover trading strategy of this notebook using an exponential moving average instead of the equally weighted moving average (hint: you may want to use the `ewm()` of the Pandas library). Evaluate the performance of the exponential moving average trading strategy using distinct alpha parameters, e.g.: 0.2, 0.5 and 0.8.<jupyter_code># ***************************************************
# INSERT YOUR CODE HERE
# ***************************************************<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11167913264255816, 'fraction_numerical': 0.02298771701091059, 'mean_word_length': 6.741567065073041, 'pattern_counts': {'":': 0, '<': 195, '<?xml version=': 0, '>': 214, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '48', 'dir': 'jupyter-structured-clean-dedup', 'id': '96294', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 9784, 'n_tokens_neox': 9043, 'n_words': 2784, 'path': 'lab_4_5/colab_04-5.ipynb', 'repo_name': 'HSG-AIML-Teaching/ML2022-Lab', 'size': '29146'}