Back to Posts
Listen to Thread

Sometimes looking at stocks, they look trend stationary. Do people do stuff like this? Maybe take into account the residuals? It's kind of lame, but I'd be interested in what the optimal hyperparameters look like.

Clone Algorithm
10
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Information Ratio 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Hello Taylor,

Did you post an algorithm? I'm getting a never-ending "Loading..." message. Please post it again. I want to have a peak at how you did the linear regression.

Grant

Let me just paste the code. Weird stuff has been happening to my computer lately

import statsmodels.api as sm

@batch_transform(window_length=10, refresh_period=1)  
def get_LR(data, sid):  
    ts = data.price[sid]  
    time = sm.add_constant(range(0,len(ts)))  
    slope , intercept = sm.OLS(ts, time).fit().params  
    return slope, intercept

def initialize(context):  
    context.spy = sid(8554)  
    #each to be optimized  
    #also optimize window length  
    context.buyThresh = 2  
    context.sellThresh = -2.5  
    context.invested = 0

# Will be called on every trade event for the securities you specify.  
def handle_data(context, data):  
    myStock = context.spy  
    params = get_LR(data, myStock)  
    if params is None:  
        return  
    if(params[1] > context.buyThresh and not context.invested):  
        order(myStock, 10000)  
    if(params[1] < context.sellThresh and not context.invested):  
        order(myStock, -10000)  
    else:  
        order(myStock, -context.portfolio.positions[myStock].amount)




Hi Taylor and Grant, sorry for the trouble loading the backtest in the thread. We're sorting through an issue on our end and hopefully will have it fixed soon.

thanks,
Jean

Hey all, that issue should be fixed now. Taylor, when you can, please run the backtest again and share that. Hopefully we'll see more than "Loading..." on that one.

-Rich
Quantopian

Taylor,

Here's a version of your algorithm that I tweaked a bit. It seems to be able to profit off of dramatic down-turns in the market...not so sure about the up-swings...might require additional "tuning" or different logic.

Jean & Rich - thanks for the fix!

Grant

Clone Algorithm
12
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Information Ratio 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

bug maybe...doesn't sm.ols() return params in the order of intercept, slope instead of slope intercept?

edit: your logic is right. you're trading on the fitted slope. kind of stupid though because I've never in my life seen a model matrix with a ones column at the end. i'm gonna add a prepend = True to my sm.OLS call.

also your reversing of my idea makes more sense... it's like fading all the dudes that figure trend stationary will persist for a while longer.

just looked at thomas wiecki's slides on the internet that are floating around. could try running grid search on the thing below. might be messed up since it comes out with different results

import matplotlib.pyplot as plt  
import statsmodels.api as sm  
import datetime as dt

from zipline.algorithm import TradingAlgorithm  
from zipline.transforms import batch_transform  
from zipline.utils.factory import load_from_yahoo

@batch_transform  
def get_LR(data, sid):  
    ts = data.price[sid]  
    time = sm.add_constant(range(0,len(ts)), prepend = True)  
    intercept, slope = sm.OLS(ts, time).fit().params  
    return intercept, slope

class slidingLR(TradingAlgorithm):

    def initialize(self, window_length=10, buy_thresh = -1, sell_thresh = 1):  
        self.window_length = window_length  
        self.buyThresh = buy_thresh  
            self.sellThresh = sell_thresh  
            self.invested = False  
        self.getRegression = get_LR(refresh_period = 1, window_length = self.window_length)

    def handle_data(self, data):

        params = self.getRegression.handle_data(data, globalTicker)  
        if params is None:  
                    return  
            slope = params[1]  
        #place orders  
        if(slope < self.buyThresh and not self.invested):  
                self.order(globalTicker, 10000)  
                self.invested = True  
            elif(slope > self.sellThresh and not self.invested):  
                self.order(globalTicker, -10000)  
                self.invested = True  
            elif(abs(self.portfolio.positions[globalTicker].amount) > 0):  
                self.order(globalTicker, -self.portfolio.positions[globalTicker].amount)  
                self.invested = False


globalTicker = "SPY"  
start = dt.datetime(2002,1,3)  
end = dt.datetime(2013,4,5)  
data = load_from_yahoo(stocks=[globalTicker], start = start, end = end, indexes={})

def run_slidingLR(window_length = 10, buy_thresh = -1, sell_thresh = 1):  
    algoObj = slidingLR(window_length, buy_thresh, sell_thresh)  
    results = algoObj.run(data)  
    #return results.portfolio_value[len(results)-1]  
    return results.portfolio_value

results = run_slidingLR(window_length = 10, buy_thresh = -1, sell_thresh = 1)  
results.plot()  
plt.show()  

Hello Taylor,

My hunch is that the algorithm I posted above (the modification of yours) works because it is sensitive to over-reaction to a down-turn. Market participants get scared, etc. so the market tanks and then bounces back--a buying opportunity.

Grant

Log in to reply to this thread.
Not a member? Sign up!