Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Alpha Factor based off StockTwits Trader Mood (Long/Short)

This is an algorithm that was inspired by a study documenting the non-linear evidence of social media on stock prices. In the study, Thársis T.P. Souza finds that social media has an effect on stock prices that cannot be explained away by other fundamental factors. My main takeaway from the study wasn't the researcher's findings, but rather the universe he examined (DJIA). In previous algorithms, I had used a blind factor ranking regardless of message volume. In this algorithm, I constrain my universe to purely securities with the highest levels of message volume and get more consistent and stable results through 2015 than my previous work.

Before cloning the algorithm, please read through these strategy notes:

  • This algorithm is basically a culmination of parts 3~4 of the Trader Mood Research Series: Hypothesis testing and crafting a signal to begin strategy creation.
  • This algorithm provides a simple framework for working with Trader Mood by only looking at the previous day's mood. In order to evaluate that signal independent of other factors, slippage and commissions have been set to zero. The algorithm itself is not geared for practical trading and will perform poorly with slippage & commissions on.
  • It uses the StockTwits Trader Mood dataset by PsychSignal between January of 2014 ~ December 2015. The full sample data feed is available from 10 Jul 2009 - two months delay from today.
  • The algorithm uses a weighting of message volume and bullish/bearish intensity to determine positions. The message volume is used as a filter (the top 500 securities with the highest volume of messages are looked at) and the previous day's bullish/bearish intensity is used as the ranking methodology.
  • Basic liquidity floors are used: ADV > 10,000,000 and only looking at the top 1,000 stocks by liquidity ranking

I'm excited to see what progressions we can make with this, please post back with your own variations if you want suggestions/feedback.

Finally, join us for our recorded webinar as we walk through this series with James Crane-Baker from PsychSignal.

This algorithm is for education - the algorithm is not intended to provide investment advice.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

4 responses

For those who missed the webinar, you can view it here: http://bit.ly/1SMv1lP

Also attached is the tearsheet for this algorithm and a few notes:

  • Turnover seems pretty stable (approx 30% per month 1.5% per day)
  • Returns are higher in 2015 than in 2014
  • Overall performance is stable

Hello Seong,
Hoping you are doing well, I wanted to ask you if you could send me the code that you explained in the webinar. I tried cloning the notebook but somehow it's not the same as the one you showed. thanks in advance!

The code throws an error as update_universe has been deprecated. I tried to rewrite it in the new style, but it is not compiling. Wondering if anyone can update the code? Besides , how can I enhance the code so that the weights of each stock is proportional to its sentiment score?
""" This sample algorithm will give you a brief overview on using the
StockTwits Trader Mood PsychSignal dataset.

This dataset measures the mood of traders posting messages on
StockTwits

Key metrics:

bull_scored_messages - total count of bullish sentiment messages
scored by PsychSignal's algorithm
bear_scored_messages - total count of bearish sentiment messages
scored by PsychSignal's algorithm
bullish_intensity - score for each message's language for the stength
of the bullishness present in the messages on a 0-4
scale. 0 indicates no bullish sentiment measured, 4
indicates strongest bullish sentiment measured. 4 is rare
bearish_intensity - score for each message's language for the stength
of the bearish present in the messages on a 0-4 scale.
0 indicates no bearish sentiment measured, 4 indicates
strongest bearish sentiment measured. 4 is rare
total_scanned_messages - number of messages coming through PsychSignal's
feeds and attributable to a symbol regardless of
whether the PsychSignal sentiment engine can score
them for bullish or bearish intensity
""" from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor, AverageDollarVolume
from quantopian.pipeline.filters import QTradableStocksUS
import pandas as pd
import numpy as np

This is the Twitter & StockTwits (All Fields) with Retweets dataset

Full information can be found at

https://www.quantopian.com/data/psychsignal/stocktwits

from quantopian.pipeline.data.psychsignal import stocktwits as psychsignal

Using the free sample in your pipeline algo

Free dataset availability is 24 Aug 2009 - 31 Dec 2015

from quantopian.pipeline.data.psychsignal import stocktwits_free as psychsignal

class PsychSignal(CustomFactor):
"""
Baseline PsychSignal Factor
"""
inputs = [psychsignal.bull_minus_bear]
window_length = 1

def compute(self, today, assets, out, bull_minus_bear):  
    out[:] = bull_minus_bear  

class PsychSignalMessages(CustomFactor):
"""
Created to rank each security by message coverage
"""
inputs = [psychsignal.bull_scored_messages, psychsignal.bear_scored_messages]
window_length = 30

def compute(self, today, assets, out, bull_msgs, bear_msgs):  
    np.mean(bull_msgs + bear_msgs, axis=0, out=out)  

Compute final rank and assign long and short baskets.

def before_trading_start(context, data):
results = pipeline_output('factors').dropna()
lower, upper = results['psychsignal_sentiment'].quantile([.05, .95])
context.shorts = results[results['psychsignal_sentiment'] <= lower]
context.longs = results[results['psychsignal_sentiment'] >= upper]
#update_universe(context.longs.index | context.shorts.index)
#context.Universo = context.longs.index | context.shorts.index

#context = context[(context.longs.index | context.shorts.index)]  

Put any initialization logic here. The context object will be passed to

the other methods in your algorithm.

def initialize(context):
# Create our pipeline
# Base universe set to the QTradableStocksUS
universe = QTradableStocksUS()
pipe = Pipeline()
pipe = attach_pipeline(pipe, name='factors')
pipe.add(PsychSignal(), "psychsignal_sentiment")

# Screen out penny stocks and low liquidity securities.  
dollar_volume = AverageDollarVolume(window_length = 20)  

# Only looks at securities within the top 1000 most liquid  
# and the 500 securities within those with the highest message  
# coverage  
liquidity_rank = dollar_volume.rank(ascending=False) < 1000  
message_rank = PsychSignalMessages().rank(ascending=False,  
                                          mask=liquidity_rank)  
pipe.set_screen(universe & (dollar_volume > 10**7) & (500 > message_rank))  

# Set our shorts and longs and define our benchmark  
context.spy = sid(8554)  
context.shorts = None  
context.longs = None  

# Create our scheduled functions  
schedule_function(rebalance, date_rules.every_day())  
schedule_function(cancel_open_orders, date_rules.every_day(),  
                  time_rules.market_close())  
set_commission(commission.PerShare(cost=0, min_trade_cost=0))  
set_slippage(slippage.FixedSlippage(spread=0))

Will be called on every trade event for the securities you specify.

def handle_data(context, data):
record(lever=context.account.leverage,
exposure=context.account.net_leverage,
num_pos=len(context.portfolio.positions),
oo=len(get_open_orders()))

def cancel_open_orders(context, data):
# Cancel any open orders at the end of each day
for security in get_open_orders():
for order in get_open_orders(security):
cancel_order(order)

def rebalance(context, data):
# Order our shorts
for security in context.shorts.index:
if get_open_orders(security):
continue
if security in data:
order_target_percent(security, -1.0/len(context.shorts.index))

# Order our longs  
for security in context.longs.index:  
    if get_open_orders(security):  
        continue  
    if security in data:  
        order_target_percent(security, 1.0/len(context.longs.index))  

# Order securities not in the portfolio  
for security in context.portfolio.positions:  
    if get_open_orders(security):  
        continue  
    if security in data:  
        if security not in (context.longs.index):# | context.shorts.index):  
            order_target_percent(security, 0)