Notebook
In [48]:
from quantopian.interactive.data.sentdex import sentiment
from quantopian.pipeline.filters.morningstar import Q500US, Q1500US, Q3000US
In [72]:
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import USEquityPricing
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.domain import US_EQUITIES
In [50]:
def make_pipeline():
    return Pipeline()
In [51]:
from quantopian.research import run_pipeline
In [52]:
result = run_pipeline(make_pipeline(), start_date = '2002-02-01', end_date = '2020-01-01')

Pipeline Execution Time: 1.90 Seconds
In [53]:
len(result)
Out[53]:
37581789
In [54]:
assets = result.index.levels[1].unique()
len(assets)
Out[54]:
23829
In [55]:
result.head()
Out[55]:
2002-02-01 00:00:00+00:00 Equity(2 [ARNC])
Equity(6 [AACE])
Equity(14 [AALA])
Equity(21 [AAME])
Equity(24 [AAPL])
In [56]:
from quantopian.pipeline.data.sentdex import sentiment

Sentiment trading strategy

A Sentiment trading strategy involves taking up positions in the market driven by such bulls or bears. The sentiment trading strategy can be momentum based i.e. going with the consensus opinion or market sentiment and if it's a bull we invest high and sell higher or vice versa.

Sentiment is from 6 to -3.

In [84]:
def make_pipeline():
    # Create a reference to our trading universe
    base_universe = USEquityPricing.specialize(US_EQUITIES)

    # Get latest closing price
    close_price = USEquityPricing.close.latest

    # Calculate 3 day average of bull_minus_bear scores
    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    # Return Pipeline containing close_price and
    # sentiment_score that has our trading universe as screen
    return Pipeline(
        columns={
            'close_price': close_price,
            'sentiment_score': sentiment_score,
        },
        screen=base_universe
    )
In [85]:
#Pipeline is where we filter down our companies. Figure out how to keep all 23829 without getting NaN's in sentiment

'''def make_pipeline():
    sentiment_factor = sentiment.sentiment_signal.latest
    
    universe = (Q3000US() & sentiment_factor.notnull())
    pipe = Pipeline(columns={'sentiment': sentiment_factor,
                             #Objective is to compare your factor(sentiment) over the entire choosen time span to the market returns of all companies considered.
                             #We are not really testing/checking our trading startegy using Alphalens, but rather the signal.
                             #Meaning that even though you might make money from a trading strategy we could be lucky, meaning no alpha, and vice versa.
                             'longs': (sentiment_factor >=4),
                             'shorts': (sentiment_factor <=-2)},
                    screen=universe)
    return pipe'''
Out[85]:
"def make_pipeline():\n    sentiment_factor = sentiment.sentiment_signal.latest\n    \n    universe = (Q3000US() & sentiment_factor.notnull())\n    pipe = Pipeline(columns={'sentiment': sentiment_factor,\n                             #Objective is to compare your factor(sentiment) over the entire choosen time span to the market returns of all companies considered.\n                             #We are not really testing/checking our trading startegy using Alphalens, but rather the signal.\n                             #Meaning that even though you might make money from a trading strategy we could be lucky, meaning no alpha, and vice versa.\n                             'longs': (sentiment_factor >=4),\n                             'shorts': (sentiment_factor <=-2)},\n                    screen=universe)\n    return pipe"
In [86]:
result = run_pipeline(make_pipeline(), start_date = '2003-01-01', end_date = '2019-12-31')

TypeErrorTraceback (most recent call last)
<ipython-input-86-928291fba369> in <module>()
----> 1 result = run_pipeline(make_pipeline(), start_date = '2003-01-01', end_date = '2019-12-31')

<ipython-input-84-ae967d1cbc00> in make_pipeline()
     19             'sentiment_score': sentiment_score,
     20         },
---> 21         screen=base_universe
     22     )

/build/src/qexec_repo/zipline_repo/zipline/pipeline/pipeline.py in __init__(self, columns, screen, domain)
     42         columns=optional(dict),
     43         screen=optional(Filter),
---> 44         domain=Domain
     45     )
     46     def __init__(self, columns=None, screen=None, domain=GENERIC):

/build/src/qexec_repo/zipline_repo/zipline/utils/input_validation.pyc in _check(func, argname, argvalue)
    451                     'funcname': get_funcname(func),
    452                     'argname': argname,
--> 453                     'actual': actual(argvalue),
    454                 },
    455             )

TypeError: zipline.pipeline.pipeline.__init__() expected a value of type zipline.pipeline.filters.filter.Filter or NoneType for argument 'screen', but got zipline.pipeline.data.dataset.DataSetMeta instead.
In [61]:
result.head()
Out[61]:
longs sentiment shorts
2012-10-16 00:00:00+00:00 Equity(24 [AAPL]) True 6.0 False
2012-10-17 00:00:00+00:00 Equity(24 [AAPL]) False 2.0 False
2012-10-18 00:00:00+00:00 Equity(24 [AAPL]) True 6.0 False
2012-10-19 00:00:00+00:00 Equity(24 [AAPL]) True 6.0 False
2012-10-22 00:00:00+00:00 Equity(24 [AAPL]) False 1.0 False
In [62]:
assets = result.index.levels[1].unique()
len(assets)
Out[62]:
576

570 different companies. Figure out how to check all US comanies not just the most liquid companies.

Have to change the universe in order to do that. universe = ((Q1500US()) & sentiment_factor.notnull())

Alpha and Beta

Alpha is a measure of your returns that are irrespective to the market gains.

Want Alpha to be as high as possible(positie at least).

Beta to be as neutral as possible quantopian likes it between 0.3 and -0.3.

Alphalens

Alphalens is a tool for analyzing a given alpha factor's effectiveness at predicting future returns. As a reminder, alpha factors express a predictive relationship between some given set of information and future returns

The objective is to compare your (sentiment)factor over the entire trading period to the market returns of all the assets you might be interested in.

In [63]:
pricing = get_pricing(assets, start_date = '2003-01-01', end_date = '2019-12-31', fields = 'open_price')
In [89]:
result.tail(10)
Out[89]:
longs sentiment shorts
date asset
2019-12-31 00:00:00+00:00 Equity(48892 [IGT]) False 1.0 False
Equity(50070 [HTZ]) True 5.0 False
Equity(50428 [AA]) True 6.0 False
Equity(50749 [BTU]) False -1.0 False
Equity(51157 [DD]) False -1.0 False
Equity(51367 [ALTR]) False -1.0 False
Equity(51494 [DLPH]) True 6.0 False
Equity(51649 [ADT]) False -1.0 False
Equity(52747 [DELL]) False 3.0 False
Equity(52991 [DOW]) True 4.0 False

Going to use Alphalent to test Alpha

In [66]:
import alphalens

factor_data = alphalens.utils.get_clean_factor_and_forward_returns(factor=result['sentiment'],
                                                                   prices = pricing,
                                                                   quantiles = 2,
#quantiles: a factor that is going to range from low to high(eg. so cannot use price to book ratio(a company might be worth less than its tangible assets))
#quantiles: have to use 2 quantiles because if we use more it will evenly distribute by sample number(meaning it will relicate numbers rather than use -3,-2,-1 etc to 6, it will eg. -3,-3,-3,-1,-1,1,1,2,4,6,6)
                                                                   periods = (20,40,60)
                                                                  )

factor_data.head()
Dropped 4.3% entries from factor data: 3.8% in forward returns computation and 0.5% in binning phase (set max_loss=0 to see potentially suppressed Exceptions).
max_loss is 35.0%, not exceeded: OK!
Out[66]:
20D 40D 60D factor factor_quantile
date asset
2012-10-23 00:00:00+00:00 Equity(24 [AAPL]) -0.097052 -0.184139 -0.189966 -1.0 1.0
Equity(5061 [MSFT]) -0.011217 -0.003254 -0.012330 6.0 2.0
2012-10-24 00:00:00+00:00 Equity(24 [AAPL]) -0.069052 -0.158845 -0.256410 -3.0 1.0
Equity(5061 [MSFT]) -0.013972 -0.026171 -0.008274 6.0 2.0
2012-10-25 00:00:00+00:00 Equity(24 [AAPL]) -0.044768 -0.159085 -0.268143 -1.0 2.0
In [67]:
alphalens.tears.create_full_tear_sheet(factor_data, long_short=True )
Quantiles Statistics
min max mean std count count %
factor_quantile
1.0 -3.0 4.0 -0.780806 1.523065 496222 59.325616
2.0 -1.0 6.0 4.195229 1.766698 340216 40.674384
Returns Analysis
20D 40D 60D
Ann. alpha 0.000 0.002 0.002
beta -0.028 -0.040 -0.048
Mean Period Wise Return Top Quantile (bps) -1.669 -2.780 -2.652
Mean Period Wise Return Bottom Quantile (bps) 0.956 1.304 0.799
Mean Period Wise Spread (bps) -2.625 -4.125 -3.503
<matplotlib.figure.Figure at 0x7f79f79e9650>
Information Analysis
20D 40D 60D
IC Mean -0.001 -0.004 -0.005
IC Std. 0.109 0.107 0.110
Risk-Adjusted IC -0.012 -0.035 -0.044
t-stat(IC) -0.493 -1.449 -1.828
p-value(IC) 0.622 0.147 0.068
IC Skew 0.268 0.336 2.373
IC Kurtosis 56.806 60.359 56.846
Turnover Analysis
20D 40D 60D
Quantile 1 Mean Turnover 0.207 0.261 0.291
Quantile 2 Mean Turnover 0.291 0.364 0.405
20D 40D 60D
Mean Factor Rank Autocorrelation 0.553 0.43 0.361
In [ ]: