Monday, 8 August 2016

Quantopian Hackathon

Last Saturday we attended our first Hackathon held at the Tyro FinTech Hub. The event was hosted by Dr. Tom Starke. 

The hackathon was based on Quantopian, an online crowd sourced hedge fund platform that just received $250m in funding from hedge fund manager Steve Cohen of Point72. 

The aim was to construct an algorithm that had low beta (-0.3 to 0.3) and made use of sentiment data (StockTwits). This daily sentiment data included variables such as date, equity, number of bearish signals, number of bullish signals, the intensities of each signal scored, and total messages generated (including neutral). 

Ideally, when designing an algorithm, you firstly work in a research phase and then an implementation phase. In the research environment, we interact with data and quickly iterate on different ideas in a notebook. That way, you can make sure your designing your algorithm on the data you want, and make sure the data is reliable and valid to use in a trading strategy. I like to think of it as the data science part of algo trading. Here you can also construct your own method of filters and scoring, simple examples include making ratios, finding net differences, etc. More information on how to do this in Quantopian can be found in their pipeline tutorial.

Our strategy combined the sentiment data with Bollinger bands. 

Put simply, if the price falls below the recent lower band, then we long the market and short the bond market. However, if the sentiment signals were negative, we would short the market and long the bond market. 

If the price is above the recent upper band, then we short the market and go long the bond market. However, if the sentiment signals were positive, we would long the market and short the bond market. 

Basically we are trying to use mean reversion, but transitioning to momentum based on sentiment signals. 

Here's the detailed version.
  • Open positions at the start of the day and close positions 5 minutes before market day end. 
  • Default slippage and transaction costs were used
  • We had the algo go through the S&P100 and aggregate the sentiment score. 
  • The score itself was: the sum of log[(bullish intensity +1)/(bearish intensity + 1)]
  • In the bollinger band, number of non-biased standard deviations from the mean is 0.6 
  • If price is below recent lower band then we long the market BUT
    • If score is less than -0.5
      • Have 150% portfolio value shorting market
      • Have 50% portfolio value long 1-3 year treasury bonds
  • If price is above recent upper band then we short the market BUT
    • If score is more than 1.5
      • Have 150% portfolio value long market
      • Have 50% portfolio value short 1-3 year treasury bonds 
The Code

import talib 
import pandas as pd
import numpy as np
from quantopian.pipeline import Pipeline
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline.factors import AverageDollarVolume
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.data.psychsignal import stocktwits as psychsignal
from quantopian.pipeline.data.builtin import USEquityPricing


def initialize(context):
    
    context.spy = sid(8554) 
    context.shy = symbol('SHY')
   
    # Open positions at start of the day, one minute into the day.
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open())
    
    # Close positions five minutes before the end of the day.
    schedule_function(close_positions, date_rules.every_day(), time_rules.market_close(minutes=5))

    #Setting default slippage and commission
    set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.1))
    set_commission(commission.PerShare(cost=0.0075, min_trade_cost=1))
    
    context.stop_price = 0
    context.stop_pct = 0.98

    pipe = my_pipeline()
    attach_pipeline(pipe, "pipeline")


# Before Trading Start

def before_trading_start(context, data):
    # Store our pipeline output DataFrame in context.
    context.results = pipeline_output('pipeline')
    

# Create Pipeline

def my_pipeline():
    pipe = Pipeline()
    
    #Adding pipeline factors
    NYSE_screen = (morningstar.share_class_reference.exchange_id.latest.eq('NYSE'))
    SPY_screen = (morningstar.valuation.market_cap.latest.top(50, mask=NYSE_screen))
    
    pipe.add(psychsignal.bull_scored_messages.latest, 'bull_messages')
    pipe.add(psychsignal.bear_scored_messages.latest, 'bear_messages')
    pipe.add(psychsignal.bullish_intensity.latest, "bullish_intensity")
    pipe.add(psychsignal.bearish_intensity.latest, "bearish_intensity")
    pipe.add(psychsignal.total_scanned_messages.latest, "total_messages")
    
    pipe.set_screen(SPY_screen &  (psychsignal.total_scanned_messages.latest>10))
    
    return pipe

def calculate_score(results):
    score = np.sum(np.log((results['bullish_intensity']+1)/(results['bearish_intensity']+1)))
    # score.columns = ['score']
    return score

def rebalance(context, data):
   
    score = calculate_score(context.results)
        #current_position = context.portfolio.positions[context.spy].amount
    price=data.current(context.spy, 'price')
    
    # Load historical data for the stocks
    prices = data.history(context.spy, 'price', 15, '1d')
    
    upper, middle, lower = talib.BBANDS(
        prices, 
        timeperiod=5,
        # number of non-biased standard deviations from the mean
        nbdevup=0.6,
        nbdevdn=0.6,
        # Moving average type: simple moving average here
        matype=0)
    # If price is below the recent lower band 
    # portfolio value into SPY
    #if price <= lower[-1] and current_position <= 0 and data.can_trade(context.spy):
    if price <= lower[-1]:
       # print 'here'
        if score < -0.5:
            order_target_percent(context.spy, -1.5)
            order_target_percent(context.shy, 0.5)
            context.stop_price = (2-context.stop_pct) * data.current(context.spy, 'price')
        else:
            order_target_percent(context.spy, 2.0)
    # If price is above the recent upper band 
    # portfolio value to short SPY
    #elif price >= upper[-1] and current_position >= 0 and data.can_trade(context.spy):
    elif price >= upper[-1]:
        if score > 1.5:
            order_target_percent(context.spy, 1.5)
            order_target_percent(context.shy, -0.5)
            context.stop_price = context.stop_pct * data.current(context.spy, 'price')  
        else:
            order_target_percent(context.spy, -2.0)

    
#stop loss specifications:
def handle_data(context, data):
    
   
    set_trailing_stop(context, data)
    score = calculate_score(context.results)
    if score > 1.5:
        if data.current(context.spy, 'price') < context.stop_price:
            order_target(context.spy, 0)
    elif score < -0.5:
        if data.current(context.spy, 'price') > context.stop_price:
            order_target(context.spy, 0)


            
def set_trailing_stop(context, data):
    if context.portfolio.positions[context.spy].amount:
        price = data.current(context.spy, 'price')
        context.stop_price = max(context.stop_price, context.stop_pct * price)
    

def close_positions(context, data):
    order_target_percent(context.spy, 0)
    context.stop_price = 0
    









No comments:

Post a Comment