For-loop backtester

  1. As regards the implementation of this backtester, we will use the GOOG data by retrieving it with the same function we used previously, load_financial_data. We will follow the pseudo code that we proposed during the previous section:
for each price update:
create_metric_out_of_prices()
buy_sell_or_hold_something()
next_price();

We will create a ForLookBackTester class. This class will handle, line by line, all the prices of the data frame. We will need to have two lists capturing the prices to calculate the two moving averages. We will store the history of profit and loss, cash, and holdings to draw a chart to see how much money we will make.

The create_metrics_out_of_prices function calculates the long moving average (100 days) and the short moving average (50 days). When the short window moving average is higher than the long window moving average, we will generate a long signal. The buy_sell_or_hold_something function will place orders. The buy order will be placed when there is a short position or no position. The sell order will be placed when there is a long position or no position. This function will keep track of the position, the holdings, and the profit.

These two functions will be sufficient for this for-loop backtester.

  1. Now, let's import the following libraries as shown in this code:
#!/bin/python3
import pandas as pd
import numpy as np
from pandas_datareader import data
import matplotlib.pyplot as plt
import h5py
from collections import deque
  1. Next, as shown, we will call the load_financial_data function previously defined in this book:
 goog_data=load_financial_data(start_date='2001-01-01',
end_date = '2018-01-01',
output_file='goog_data.pkl')

# Python program to get average of a list
def average(lst):
return sum(lst) / len(lst)
  1. Let's now define the ForLoopBackTester class as shown. This class will have the data structure to support the strategy in the constructor. We will store the historic values for profit and loss, cash, positions, and holdings. We will also keep the real-time profit and loss, cash, position, and holding values:
 class ForLoopBackTester:
def __init__(self):
self.small_window=deque()
self.large_window=deque()
self.list_position=[]
self.list_cash=[]
self.list_holdings = []
self.list_total=[]

self.long_signal=False
self.position=0
self.cash=10000
self.total=0
self.holdings=0
  1. As shown in the code, we will write the create_metric_out_of_prices function to update the real-time metrics the trading strategy needs in order to make a decision:
      def create_metrics_out_of_prices(self,price_update):
self.small_window.append(price_update['price'])
self.large_window.append(price_update['price'])
if len(self.small_window)>50:
self.small_window.popleft()
if len(self.large_window)>100:
self.large_window.popleft()
if len(self.small_window) == 50:
if average(self.small_window) >
average(self.large_window):
self.long_signal=True
else
:
self.long_signal = False
return True
return False
  1. The buy_sell_or_hold_something function will take care of placing the orders based on the calculation from the prior function:
     def buy_sell_or_hold_something(self,price_update):
if self.long_signal and self.position<=0:
print(str(price_update['date']) +
" send buy order for 10 shares price=" + str(price_update['price']))
self.position += 10
self.cash -= 10 * price_update['price']
elif self.position>0 and not self.long_signal:
print(str(price_update['date'])+
" send sell order for 10 shares price=" + str(price_update['price']))
self.position -= 10
self.cash -= -10 * price_update['price']

self.holdings = self.position * price_update['price']
self.total = (self.holdings + self.cash)
print('%s total=%d, holding=%d, cash=%d' %
(str(price_update['date']),self.total, self.holdings, self.cash))

self.list_position.append(self.position)
self.list_cash.append(self.cash)
self.list_holdings.append(self.holdings)
self.list_total.append(self.holdings+self.cash)
  1. We will feed this class by using the goog_data data frame as shown:
naive_backtester=ForLoopBackTester()
for line in zip(goog_data.index,goog_data['Adj Close']):
date=line[0]
price=line[1]
price_information={'date' : date,
'price' : float(price)}
is_tradable = naive_backtester.create_metrics_out_of_prices(price_information)
if is_tradable:
naive_backtester.buy_sell_or_hold_something(price_information)

When we run the code, we will obtain the following curve. This curve shows that this strategy makes around a 50% return with the range of years we are using for the backtest. This result is obtained by assuming a perfect fill ratio. Additionally, we don't have any mechanism preventing drawdown, or large positions. This is the most optimistic approach when we study the performance of trading strategies:

Achieving improved confidence in the way the strategy will perform in the market implies having a backtester that considers the characteristics of the trading system (more generally, the specificities of the company trading strategy where you work) and market assumptions. To make things more akin to scenarios encountered in real life, we will need to backtest the trading strategy by using most of the trading system components. Additionally, we will include the market assumptions in a market simulator.

In the following section, we will implement an event-based backtester handling the same GOOG data and we will be able to appreciate the differences.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.220.114