QuantSigma Documentation

Section 1 - Basic Functions

Lets review the basic ordering functions available to you in QuantSigma:

1.1 - Ordering Functions

The first function is the order function. This function takes two inputs: the ticker symbol and how many stocks you wish to buy. A positive number would trigger buying the stock and a negative number would trigger selling the stock. Let's use the order function to buy 5 shares of SPY:

order('SPY',5)

The next function is order_target. This function works like the order function but the quantity signifies the number of shares to have in our portfolio instead of a quantity. In the example below, we are going to purchase 100 worth of SPY:

order_target('SPY',100)

Another similar function is the order_target_percent function. This function is also similar to the order function except that the input is a percentage of your current equity that you wish to invest. Just like in the order function, a negative number would trigger the function to sell shares. Note that values greater than 1 will mean that we are using leverage to purchase more shares. In the example below, we are going to use 50% of our equity to purchase shares of SPY:

order_target_percent('SPY',0.5)

1.2 - Portfolio Information

Now that we know how to buy and sell stocks, let's talk about some basic functions for checking your portfolio. In the example below, we are going to have a starting balance of $10,000 and then we will use the order function to purchase 5 shares of SPY. Then we will use context.account.cash and context.account.portfolio to check how much equity we have left and what our current portfolio is.

# starting balance is $10000
print(context.account.cash)
output: 10000.00

# verify that we do not have anything in our portfolio yet
print(context.account.portfolio)
output: {}

# purchase 5 shares of SPY
order('SPY',5)

# remaining balance after purchase
print(context.account.cash)
output: 8345.00

# check to see that we have 5 shares of SPY
print(context.account.portfolio)
output: {'SPY': 5}

1.3 - Stock Information

The last function we are going to cover in this section is data.history. This function allows you to view information about a particular stock. The inputs are (stock,category,lookback) where stock is the ticker symbol, category is 'open', 'close', 'high', 'low' or 'volume' and lookback is how many days of data you want to pull. Lookback always starts with the current price data and goes backwards from there. If we wanted to pull the last 15 days of price data, we would make the lookback 15. In the example below, we are going to use data.history to look at stock data for SPY for 01/02/2019:

print(data.history('SPY','close',1))
print(data.history('SPY','open',1))
print(data.history('SPY','high',1))
print(data.history('SPY','low',1))
print(data.history('SPY','volume',1))

output:

2019-01-02 PRINT - _id
2019-01-02    245.534673
Name: Close, dtype: float64
2019-01-02 PRINT - _id
2019-01-02    241.412658
Name: Open, dtype: float64
2019-01-02 PRINT - _id
2019-01-02    246.545548
Name: High, dtype: float64
2019-01-02 PRINT - _id
2019-01-02    241.383215
Name: Low, dtype: float64
2019-01-02 PRINT - _id
2019-01-02    126743877.0
Name: Volume, dtype: float64

This information will be used to feed data into our algorithms later on.

Section 2 - Preparing an Algorithm

Next, let's review some of the prerequisites to running a backtest in QuantSigma:

2.1 - Imports

In order to create more advanced trading strategies, you will most likely need to import other python modules. The list of supported python modules at this time are:

import pandas
import numpy
import talib

If there is another import that would be useful for creating algorithms, please contact us at support@quantsigma.com and we can look into adding it.

2.2 - Prerequisites

After our imports, we need to define a few more items before we are ready to start creating algorithms:

By default, QuantSigma will assume that we are going to trade securities in the US stock market. If the algorithm is for trading crypto, we need to set MARKET equal to CRYPTO. The two options for MARKET are below:

MARKET = 'CRYPTO' or MARKET = 'US-EQUITIES'

Another option is DOLLAR_MODE. This allows us to change charts to show dollars instead of percentages. By default, DOLLAR_MODE is set to False unless otherwise specified. the two options are:

DOLLAR_MODE = True or DOLLAR_MODE = False

2.3 - Initialize Function

Now that we have covered all of the basics, we need to create a method for initializing the trading functions set out by your algorithms output:

The first thing we need to do is initialize our algorithm. An example of how to do this is:

def initialize(context):
   schedule_function('trade', daterule='every_day', timerule='market_open', time_offset=45)
   context.TLT = 'TLT'
   context.SPY = 'SPY'

In this example, we have initialized our schedule_function and defined 'TLT' and 'SPY' ticker symbols. Let's break down the schedule function:

schedule_function('function','daterule','date_offset','timerule','time_offset')

In the above example, 'function' is your trading logic that is going to be defined after our initialize function.'daterule' is the logic behind when you want your trade to execute.'date_offset' is an offset you can use based off of 'daterule'. 'timerule' is what time of the day you want the trade to execute. 'time_offset' is an offset you can apply on 'timerule'. The defaults for each of the above if left blank in the function are: 'daterule' = 'None', 'date_offset' = 0, 'timerule' = 'None', and 'time_offset' = 0.

The options for 'daterule' are 'every_day', 'month_start', 'month_end','week_start' and 'week_end'. The options for 'timerule' are 'market_open' and 'market_close'. 'date_offset' and 'time_offset' are adjustments that can be applied to 'daterule' and 'timerule' respectively. Looking back at our initialization function, we can now see that this 'trade' function will be executed every day 45 minutes after the market opens.

If we are trading in an environment where friction is important, we also want to declare a friction value with the set_commission function. To set a friction value, declare set_commission in the initialize function and input the desired value:

set_commission(0.005)

Another option that can be declared in the initialize function is the benchmark stock. By default, this is set to SPY for the US-EQUITIES market and BTC for the CRYPTO market. In order to change this, call the set_benchmark function and input the desired benchmark ticker symbol:

set_benchmark('TLT')

Section 3 - Creating an Algorithm

Now that we have covered the initialize function, lets go over a sample trading algorithm. In the example below, we use a simple moving average function to determine whether to buy or sell SPY stock. In this case, if the 15 day moving average was less than the 5 day moving average, the algorithm would purchase as much SPY as possible with the available capital.

def trade(context):
    price_5 = data.history(context.SPY,'close',5)
    price_15 = data.history(context.SPY,'close',15)

    price_5_average = price_5.mean()
    price_15_average = price_15.mean()

    if price_15_average < price_5_average:
        order_target_percent(context.SPY,1)
    else:
        order_target_percent(context.SPY,0)

Section 4 - Backtesting

In this section, we are going to walk through an entire algorithm and show what the outputs look like.

The first step is to load our imports and specify our market:

import pandas
import numpy
import talib

MARKET = 'US-EQUITIES'
DOLLAR_MODE = False

From here, we need to initialize our schedule function. For this example, we are going to disable dollar mode and set our commission and benchmark values. After, we declare a ‘trade’ function that will run every day 45 minutes after the market opens. We are also going to define ‘SPY’ as context. SPY to be used later in our ‘trade’ function:

def initialize(context):
    set_commission(0.005)
    set_benchmark('SPY')
    schedule_function('trade', daterule='every_day', timerule='market_open', time_offset=45)
    context.SPY = 'SPY'

The next thing we need to do is build our algorithm. In this case we are going to use our example from section 3 but this time we are going to use 2 times leverage by using order_target_percent:

def trade(context):
    price_5 = data.history(context.SPY,'close',5)
    price_15 = data.history(context.SPY,'close',15)
    price_5_average = price_5.mean()
    price_15_average = price_15.mean()

    if price_5_average > price_15_average:
        order_target_percent(context.SPY,2)
    else:
        order_target_percent(context.SPY,0)

The last thing we need to do is set our date ranges. For this example, i am going to test this strategy from 01/01/2019 to 12/31/2019:

Screenshot

Now that we have set our backtest dates and completed our algorithm, this is what it should look like:

import pandas
import numpy
import talib

MARKET = 'US-EQUITIES'
DOLLAR_MODE = False


def initialize(context):
    set_commission(0.005)
    set_benchmark('SPY')
    schedule_function('trade', daterule='every_day', timerule='market_open', time_offset=45)
    context.SPY = 'SPY'

def trade(context):
    price_5 = data.history(context.SPY,'close',5)
    price_15 = data.history(context.SPY,'close',15)
    price_5_average = price_5.mean()
    price_15_average = price_15.mean()

    if price_5_average > price_15_average:
        order_target_percent(context.SPY,2)
    else:
        order_target_percent(context.SPY,0)

The last thing we need to do is click “Backtest” to evaluate how our algorithm performed:

Screenshot

In the chart above we can see how this strategy compares to the benchmark stock as well as some other key metrics like Alpha, Beta, Sharpe, Max Drawdown and Return.