Monday 6 April 2015

Quantopian Algorithmic Trading Strategy: Basic, Daily

We have been recently been using Quantopian, a crowd-sourced hedge fund which allows individuals access to a browser-based algorithmic trading platform. Below is a basic investment strategy. Algorithms are coded in Python, and backtested against US stock price and fundamental data.

def initialize(context):
    context.security = symbol('K')

def handle_data(context, data):

    average_price = data[context.security].mavg(5)
    current_price = data[context.security].price
    cash = context.portfolio.cash

if current_price > 1.01*average_price and cash > current_price:
    number_of_shares = int(cash/current_price)
    order(context.security, +number_of_shares)
    log.info("Buying %s" % (context.security.symbol))

elif current_price < average_price:
    order_target(context.security, 0)
    log.info("Selling %s" % (context.security.symbol))
    
record(stock_price=data[context.security].price)

Function meanings:


  • To start an algorithm, we need to use the initialise function. This will set any data or variables that are used in the algorithm, such as the security, or parameters
  • data is a dictionary that represents a snapshot of the algorithm's universe including market information about the security and transforms
  • context stores any state defined by us and stores portfolio object
  • In the first example, we are using Kellogg (K)
  • The handle_data function is run based on user specified frequency- either every minute (live trading and minute backtesting) or daily i.e. handle_data is called whenever a market event occurs for Kellogg
  • We calculate the average price of Kellogg stock based on the moving average for the last 5 days
  • The portfolio object tracks our position, cash, cost basis of holdings, etc. In our basic strategy, we are interested in the current amount of cash in our portfolio
  • We have programmed the algorithm to BUY if current price is 1% above Kellogg's 5 day average price and there is enough cash in the portfolio
  • This is why we also need to calculate how many shares to buy
  • We have programmed the algorithm to SELL if current price is below Kellogg's 5 day average price, that is, close position to holding zero shares
  • The record function can track signals, up to five different variables. In our case, we are recording Kellogg's stock price.
The screenshot demonstrates this strategy generates 8.3% return from the start of 2014 to April 2015. 


However, we identified a potential problem which played a role in lowering returns. We replaced the code to sell with a code instructing sell only if the current price is higher than purchase price/cost basis. Firstly we define cost basis as the volume weighted average price paid (including commission) per share in this position. 

costBasis = context.portfolio.positions[context.security].cost_basis
elif current_price > costBasis:
    order_target(context.security, 0)
    log.info("Selling %s" % (context.security.symbol))

This gave us a higher return of 19%, outperforming the benchmark. 


Update (7/4/15): I updated the buying strategy to 

if current_price > 1.005*average_price and cash > current_price:
        number_of_shares = int(cash/current_price)
        order(context.security, +number_of_shares)
        log.info("Buying %s" % (context.security.symbol))

All else remains unchanged. I believe that buying the stock when it increases its price by 0.5% as opposed to 1% would improve chances of making the most of the upswing in price before it fell again. This was confirmed by the 28.59% returns. 



If the same buy strategy (buy if 0.5% increase) had used a sell strategy where we sell if current price is below average price, ignoring the fact the current price could be below purchase price, 

elif current_price < average_price:
        order_target(context.security, 0)
        log.info("Selling %s" % (context.security.symbol))



The returns are 13.1%. This is higher than our original strategy (which generated 8.3% return)- the 0.5% buy strategy boosted returns by approximately 5%. Adding in the improved sell strategy where sell only if current price is above purchase price more than doubles this return. 

Using the same strategy on Facebook stock though with a moving average of 7 days, we generate a 22.4% return. We have added the function set_symbol_lookup_date, which globally sets the date to help disambiguate cases where a symbol historically referred to different securities (FB used to refer to FBR Asset Investment Corp, last traded in 2003).



No comments:

Post a Comment