Documentation

PyAlgosim, the simulator

PyAlgosim is the module that takes care of running the algorithm that you wish to backtest. Using it is very simple and intuitive. First, you must import the module into your program - note that your program should be created in the same directory as the PyAlgosim.py file for the program to properly communicate with the generated database and ticker file.

import PyAlgosim

The PyAlgosim API

class PyAlgosim.PyAlgosim(account, [db_path, ticker_list_path, variables])

This method initializes the PyAlgosim simulator. It returns an object that will function as the backtester for your algorithm.

account - a PyBank object. The trading account that you wish to use during the simulator. dbpath (optional) - the path to the database file you are using. Should be a string. If you used the initialize.py script and are running the program from PyAlgosim.py 's directory, then you shouldn't have to set this explicitly. Defaults to ./utils/stocks.db. tickerlist_path (optional) - the path to the ticker list file that should have been generated during the initialize.py script. Should be a string. Again, if you followed the initial setup instructions, you shouldn't have to explicitly set this. Defaults to ./utils/tickers.json. variables (optional) - an dictionary "template" (dictionary with empty fields) with any optional variables that you want your algorithm to have access to during the simulation. For example, the dictionary could store recurring averages, statistics, record highs and lows, etc. Defaults to None.

For example:

import PyAlgosim
import PyBank

trade_account = PyBank.Account()

# this will use all of the default configuration, assuming all files have been kept in their original place
backtester = PyAlgosim.PyAlgosim(trade_account)

# this uses custom configuration for the stock database and the ticker list json file
backtester = PyAlgosim.PyAlgosim(trade_account, db_path="/some/other/path/mydata.db", ticker_list_path="/some/other/path/mytickers.json")

# this uses a variables dictionary, for potentially calculating a simple moving average for 50 days and the highest and lowest prices of the stock. Note that it is a "template" in the sense that it is empty - it will be provided exactly like that at the start of backtesting each stock.
my_variables = {
    highest_price = 0
    lowest_price = 0
    past_50_prices = []
}
backtester = PyAlgosim.PyAlgosim(trade_account, variables=my_variables)

classmethod PyAlgosim.simulate(algorithm[, time_start, time_end])

This method performs the backtesting on the algorithm provided, using the data specified in time_start and time_end.

algorithm - the name of the algorithm (function) that you wish to backtest. The function provided as an algorithm must take in the following variables in the order specified:

  • A stock data dictionary that contains information about the stock being processed. The object will contain the following properties:

    • date - an integer of the form YYYYMMDD. The historical date of the data.
    • price - a float. The current price of the stock.
    • ticker - a string containing the ticker of the stock.
  • A PyBank.Account() object, which will be the account you pass as an argument to PyAlgosim. The algorithm can access the account using this parameter.

  • A variables dictionary, which will be the variables template dictionary that you provide PyAlgosim. The template will be used at the start of backtesting for each ticker (it will not remain the same across all tickers, just the ticker that is being simulated at the time. Keep this in mind).

timestart, timeend - optional parameters to specify the time to start and end the backtesting. They should be datetime objects within the range of the data being used. If they exceed the ranges, or the date doesn't exist (e.g the market was closed on the date), then PyAlgosim will just use the closest date to the one specified. The range of data in the database is from January 2nd, 1998 to August 9th, 2013.

Example uses:

import PyAlgosim
import PyBank
import datetime

trade_acc = PyBank.Account()
backtester = PyAlgosim.PyAlgosim(trade_acc)

# this algorithm checks to see if you currently own the stock, and if you have enough money to buy one share of the stock. It then buys one share if the conditions are met.
def algorithm(stock, account, variables):
    if stock["ticker"] not in account.stocks_owned and account.funds - (stock["price"] + account.TRANSACTION_FEE) > 0:
        account.buy_stock(stock["ticker"], 1)

# Here, the algorithm is passed to the simulate() method. Only a start date is provided.
backtester.simulate(algorithm, datetime.date(2008, 1, 1) )

PyBank, the trading account

The PyBank module allows you to simulate a trading account, working together with the PyAlgosim module. It is what you will use to buy stock, sell stock, and other things you might to in a real-life trading account. The API is simple and intuitive to use.

It should be noted that the PyBank module cannot be used on its own. It should be used in conjunction with the PyAlgosim module for the backtesting to work properly.

The PyBank API

class PyBank.Account([funds=100000, transaction_fee=6.99])

Returns an Account object, which you can perform various actions on, like in a trading account. Takes in two optional arguments:

funds - float that defaults to 100,000. This is the initial funds that you wish the account to start out with. transaction_fee - float that defaults to 6.99. This is the value of the transaction fees charged by the trading account (charged on every trade you perform).

import PyBank

# creating an account with different initial funds and transaction fee
account = PyBank.Account(funds=10000, transaction_fee=9.99)

classmethod Account.buy_stock(ticker, quantity)

Method to buy a stock. An error will be thrown if you attempt to buy more shares than what your funds allow you to.

ticker - a string specifying the ticker symbol, e.g "AAPL". quantity - an int specifying the number of shares to buy.

classmethod Account.sell_stock(ticker, quantity)

Method to sell a stock. An error will be thrown if you attempt to sell a stock that you do not own, or attempt to sell too much of a stock.

ticker - a string specifying the ticker symbol, e.g "AAPL". quantity - an int specifying the number of shares to buy. Alternatively, you can pass the string "all" to sell all the stock you own of that ticker.

classmethod Account.trailing_stop(ticker, quantity, points[, percentage=False])

Method to set a trailing stop order on a stock, selling it when it goes below a certain point. A more detailed explanation of how a trailing stop works can be found here.

ticker - a string specifying the stock to place the order on. E.g "AAPL". quantity - an int specifying the amount of stock to sell. points - a float sepcifying the number of points that the stock can fall before the sell order executing. For example, setting this to 1 would mean that a stock priced at 101 would sell if it dropped to 100. percentage - optional, defaults to False. Indicates whether the points parameter should be treated as a percentage or not i.e the stock would need to fall by the percentage specified for it to sell.

Note: If you attempt to place two trailing stop orders on the same stock, they will be merged into one - the latest one placed.

Examples using the previous methods:

# Note: all the operations that the account can do only work inside the algorithm inside PyAlgosim (it does not have the relevant data if used outside of PyAlgosim). However, they are shown outside of PyAlgosim here for illustration purposes.

import PyBank

account = PyBank.Account()

# buy 100 shares of Apple
account.buy_stock("AAPL", 100)

# sell those 100 shares - way #1
account.sell_stock("AAPL", 100)
# sell those 100 shares - way #2
account.sell_stock("AAPL", "all")


account.buy_stock("AAPL", 100)
# this indicates the account to sell the stock if it drops 5% in price.
account.trailing_stop("AAPL", 100, 5, percentage=True)

classmethod Account.sell_all()

Sells all the stocks owned.

classmethod Account.report([verbose=False])

Print out a report to the console on the trading activities of the account at that moment in time. It presents information such as the total return (based on the value of stocks held and funds in the account), average market return during that same time period, and number of transactions. If verbose is set to True, each stock currently owned by the account is printed to the console, with the average profit made reported on each one.

PyAlgosim and PyBank: putting it together

The two modules are used in conjunction to perform backtesting on algorithms. The following examples will illustrate how to do this.

Creating a trading algorithm

When creating your trading algorithm, you must keep a few things in mind for everything to work properly. Specifically,

  • Always checking whether you have enough money to buy the stock.
  • Correctly using the data provided.
  • Understand how the variables dictionary works

If you make a purchase which you don't have enough money for, the program will raise an error and stop. For your trading strategy to work, you do not want this. Furthermore, you should keep in mind how the backtesting works. The trading algorithm runs separately for each stock (it will only have access to one ticker at a time). When the trading algorithm begins running on any one of the tickers, the template dictionary variable (if provided) will be available to the algorithm. This variable is separate for each stock - you will not have access to the variable dictionary of other stocks.

import PyBank
import PyAlgosim

# template dictionary for variables
myvariables = {
    "high" : float("-inf"),
    "low" : float("inf")
}

account = PyBank.Account()
backtester = PyAlgosim.PyAlgosim(account, variables=myvariables)

# this is an example of keeping track of highs and lows in the variables dictionary
def myalgorithm(stock, account, variables):
    # keep track of highs and lows
    if stock["price"] > variables["high"]:
        variables["high"] = stock["price"]

    if stock["price"] < variables["low"]:
        variables["low"] = stock["price"]

    # buy stock if not owned
    if (stock["ticker"] not in account.stocks_owned) and (account.funds - (stock["price"] + account.TRANSACTION_FEE) >= 0):
        account.buy_stock(stock["ticker"], 1)

# backtest algorithm
backtester.simulate(myalgorithm)

# print report of account
account.report()

Contributing

PyAlgosim is completely opensource, licensed under the MIT License. If you find any bugs, or have new ideas to extend the software, please do! Create a new issue or pull request and it will be reviewed.