Help with Simple Bollinger Band Strategy

553 views
Skip to first unread message

Daniel Wu

unread,
Nov 9, 2013, 5:02:24 PM11/9/13
to zip...@googlegroups.com
Hi, I'm new to zipline. I'm trying to get the below bollinger band strategy to work, but I'm encountering a lot of problems. In particular, I'm getting 0's for all my values. 


class BollingerBands(TradingAlgorithm):    
    
# In these two lines, we set the maximum and minimum we want our algorithm 
# to go long or short our security.  You don't have to set limits like this
# when you write an algorithm, but it's good practice.
    
    #def initialize(context):
    #    context.aapl = sid(2174)
    #    context.max_notional = 1000000.1
    #    context.min_notional = -1000000.0
    #    context.stock = sid(2174)
    #    context.qty = 1000
    def initialize(self):
        self.invested = False 
    
    def get_bb(self, df):
        df['ma']=pd.stats.moments.rolling_mean(df['price'], 20)
        df['std']=pd.stats.moments.rolling_std(df['price'], 20)
        df['upperBB']=df['ma']+(df['std']*2)
        df['lowerBB']=df['ma']-(df['std']*2)
        #print df.irow(31)
        return df
    
    def handle_data(self, df):
        self.price = df['price']
        self.ma = df['ma']
        self.std = df['std']
        self.upperBB = df['upperBB']
        self.lowerBB = df['lowerBB']
        self.buy = False
        self.sell = False
        
        # SELL and TOP OF BAND
        if self.price > self.upperBB and not self.invested:
            self.order('price',-100)
            self.invested = False
            self.buy = False
        elif self.price < self.lowerBB and self.invested:
            self.order('price', 100)
            self.invested = True
            self.sell = True 
        self.record(ma=self.ma, upperBB=self.upperBB,lowerBB=self.lowerBB,buy=self.buy,sell=self.sell)
        return 

Peter Cawthron

unread,
Nov 9, 2013, 7:11:53 PM11/9/13
to zip...@googlegroups.com
Hello Daniel,

I am very new to zipline as well but this may help a little.

P.

from datetime import datetime 
import pytz
from zipline.algorithm import TradingAlgorithm 
from zipline.utils.factory import load_from_yahoo
from zipline.transforms.ta import BBANDS

upperband = 0
lowerband = 1

class BollingerBands(TradingAlgorithm):   
   
    def initialize(self):
        self.invested = False
        self.bbands_trans = BBANDS(timeperiod=30)

    def handle_data(self, data):
        self.bbands = self.bbands_trans.handle_data(data)
        if self.bbands is None:
            return
        upper = self.bbands['AAPL'][upperband]
        lower = self.bbands['AAPL'][lowerband]
        price =  data['AAPL'].price
        #print upper, price, lower
        # SELL AT TOP OF BAND
        if price > upper and not self.invested:
            print "Selling..."
            self.order('price',-100)
            self.invested = True
        elif price < lower and self.invested:
            print "Covering short position..."
            self.order('price', 100)
            self.invested = False
  
if __name__ == '__main__': 
    start = datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc) 
    end = datetime(2008, 6, 30, 0, 0, 0, 0, pytz.utc) 
    data = load_from_yahoo(stocks=['AAPL'], indexes={}, start=start, 
                           end=end) 
    simple_algo = BollingerBands() 
    results = simple_algo.run(data)

Daniel Wu

unread,
Nov 9, 2013, 10:24:16 PM11/9/13
to Peter Cawthron, zip...@googlegroups.com
Thanks so much. I tried to run your code but it is asking me to import talib. 

 I did the below, but I get the following error. Any ideas?

AJs-MacBook-Pro:zipline tutorial AJ$ easy_install TA-lib

Searching for TA-lib
Best match: TA-Lib 0.4.7
Processing TA_Lib-0.4.7.zip
Writing /var/folders/x8/dmmzl8kd2lx4k24lb6fkqx300000gn/T/easy_install-8x8Eon/ta-lib-TA_Lib-0.4.7/setup.cfg
Running ta-lib-TA_Lib-0.4.7/setup.py -q bdist_egg --dist-dir /var/folders/x8/dmmzl8kd2lx4k24lb6fkqx300000gn/T/easy_install-8x8Eon/ta-lib-TA_Lib-0.4.7/egg-dist-tmp-r0ZU4u
unable to execute /: Permission denied
error: Setup script exited with error: command '/' failed with exit status 1



--
You received this message because you are subscribed to a topic in the Google Groups "Zipline Python Opensource Backtester" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/zipline/xr1ZZBiq0c0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to zipline+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Daniel Wu

unread,
Nov 9, 2013, 10:40:17 PM11/9/13
to Peter Cawthron, zipline
Just an update. I downloaded xcode and am now getting a new error:


this person has the same error: https://github.com/quantopian/zipline/issues/168

Their conclusion was to install Tal-lib C library using a pull request.

How does one do that? 

I visited the Tal-lib C website: http://ta-lib.org/hdr_dw.html and downloaded and unzipped "ta-lib-0.4.0-msvc.zip" . What am I supposed to do with this file? 


AJs-MacBook-Pro:zipline tutorial AJ$ easy_install TA-lib
Searching for TA-lib
Reading http://pypi.python.org/simple/TA-lib/
Reading http://github.com/mrjbq7/ta-lib
Best match: TA-Lib 0.4.7
Downloading https://github.com/mrjbq7/ta-lib/archive/TA_Lib-0.4.7.zip
Processing TA_Lib-0.4.7.zip
Writing /var/folders/x8/dmmzl8kd2lx4k24lb6fkqx300000gn/T/easy_install-gykmut/ta-lib-TA_Lib-0.4.7/setup.cfg
Running ta-lib-TA_Lib-0.4.7/setup.py -q bdist_egg --dist-dir /var/folders/x8/dmmzl8kd2lx4k24lb6fkqx300000gn/T/easy_install-gykmut/ta-lib-TA_Lib-0.4.7/egg-dist-tmp-Rf126H
talib/common.c:314:10: fatal error: 'ta-lib/ta_defs.h' file not
      found
#include "ta-lib/ta_defs.h"
         ^
1 error generated.

Thank you!
Dan

PhD Student: Social Policy and Sociology

Peter Cawthron

unread,
Nov 10, 2013, 7:19:43 AM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Daniel,

I'm on Windows so my experince won't help you. If I try to install TA-Lib via pip I also get errors so I use the Windows binary at http://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib

Have you downloaded the TA-LIb (the library) source from http://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ ? On Windows this goes in c:\ta-lib. I think this is required in addition to ta-lib (the wrapper). Things are now a bit confused as the Python package for the wrapper is also now called TA-Lib.

P.

Peter Cawthron

unread,
Nov 10, 2013, 7:23:46 AM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Daniel,

Sorry, I missed your update. You really need a Linux user to help you but is this any use? https://github.com/mrjbq7/ta-lib/issues/47

P.

Daniel Wu

unread,
Nov 10, 2013, 8:20:28 AM11/10/13
to Peter Cawthron, zip...@googlegroups.com
Thanks so much Peter! I know you're on windows, so I'm posting it to everyone just in case they know of a solution.

I followed the instructions and I ultimately get this error. 
./configure works, but when i type "make" i get this:

(cd .libs/libta_lib.lax/libta_abstract.a && ar x /Users/AJ/Desktop/ta-lib 2/src/ta_abstract/.libs/libta_abstract.a)
ar: /Users/AJ/Desktop/ta-lib: Inappropriate file type or format
make[2]: *** [libta_lib.la] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all-recursive] Error 1

Thank you!
Dan

PhD Student: Social Policy and Sociology


Daniel Wu

unread,
Nov 10, 2013, 8:53:22 AM11/10/13
to Peter Cawthron, zipline
Hey Peter,

In the meantime, can I ask how you might create your own "transform" attribute? How could I replace the below line with a function I create myself using pandas rolling mean and rolling standard deviation functions? 

self.bbands_trans = BBANDS(timeperiod=30)

Peter Cawthron

unread,
Nov 10, 2013, 9:32:02 AM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Daniel,

Using pandas would be a nice solution but a little beyond me in zipline at present. This is as far as I've got!


P.

from datetime import datetime 
import pytz
from zipline.algorithm import TradingAlgorithm 
from zipline.utils.factory import load_from_yahoo
from collections import deque
import numpy as np

window           = 30
price_window     = deque(maxlen=window)


class BollingerBands(TradingAlgorithm):   
   
    def initialize(self):
        self.invested = False

    def handle_data(self, data):

        price =  data['AAPL'].price
        price_window.append(price)
        if len(price_window) < window:
            return
        ave = np.mean(price_window)
        std = np.std(price_window)
        upper = ave + 2 * std
        lower = ave - 2 * std

       
        #print upper, price, lower

        # SELL AT TOP OF BAND
        if price > upper and not self.invested:
            print "Selling..."
            self.order('price',-100)
            self.invested = True
        elif price < lower and self.invested:
            print "Covering short position..."
            self.order('price', 100)
            self.invested = False
  
if __name__ == '__main__': 
    start = datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc) 
    end = datetime(2008, 6, 30, 0, 0, 0, 0, pytz.utc) 
    data = load_from_yahoo(stocks=['AAPL'], indexes={}, start=start, 
                           end=end) 
    simple_algo = BollingerBands() 
    results = simple_algo.run(data)



Daniel Wu

unread,
Nov 10, 2013, 9:39:26 AM11/10/13
to Peter Cawthron, zipline
Hi Peter,

Thank you sooo much for your continued responses. I'm looking at your implementation and it looks pretty similar to mine (see my original email), except you're using numpy functions. I used  df['ma']=pd.stats.moments.rolling_mean(df['price'], 20) and df['std']=pd.stats.moments.rolling_std(df['price'], 20), which doesn't require creating price windows, i think. However, I'm not clear why my bollinger band implementation is not working - it gives me all zeros! 




Thank you!
Dan

PhD Student: Social Policy and Sociology


Message has been deleted
Message has been deleted

Peter Cawthron

unread,
Nov 10, 2013, 12:16:21 PM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Daniel,

Not quite what you had in mind but it's getting closer. And it trades - badly!

P.

from datetime import datetime 
import pytz
from zipline.algorithm import TradingAlgorithm
from zipline.transforms import batch_transform
from zipline.utils.factory import load_from_yahoo
import pandas as pd
import matplotlib.pyplot as plt

window           = 20

STOCKS = ['AAPL', 'MSFT']

@batch_transform
def get_prices(data, sids):
    prices = data.price[sids]
    return prices 

class BollingerBands(
TradingAlgorithm):   
   
    def initialize(self, window_length=20, refresh_period=0):
        self.invested       = False
        self.first_time     = True
        self.window_length  = window_length
        self.refresh_period = refresh_period
        self.get_prices = get_prices(refresh_period=self.refresh_period,
                                           window_length=self.window_length)

    def handle_data(self, data):
        prices = self.get_prices.handle_data(data, STOCKS)
        if prices is None:
            return
        if self.first_time:
            print prices.head()
            self.first_time = False
           
        ave = pd.stats.moments.rolling_mean(prices['AAPL'], 20)[-1:]
        std = pd.stats.moments.rolling_std(prices['AAPL'], 20)[-1:]

        upper = ave + 2 * std
        lower = ave - 2 * std
       
        #print upper, ave, lower


        # SELL AT TOP OF BAND
        if data[STOCKS[0]].price > upper and not self.invested:
            print "Selling..."
            self.order(STOCKS[0],-100)
            self.invested = True
        elif data[STOCKS[0]].price < lower and self.invested:
            print "Covering short position..."
            self.order(STOCKS[0], 100)

            self.invested = False

if __name__ == '__main__': 
    start = datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc) 
    end = datetime(2008, 6, 30, 0, 0, 0, 0, pytz.utc) 
    data = load_from_yahoo(stocks=STOCKS, indexes={}, start=start, 
                           end=end) 
    simple_algo = BollingerBands() 
    results = simple_algo.run(data)
    results.portfolio_value.plot()
    plt.show()

Daniel Wu

unread,
Nov 10, 2013, 12:35:42 PM11/10/13
to zip...@googlegroups.com
Thank you so much Peter! I'm going to piece through your code and see what is different.

From your initial observations, what was my major mistake? 

Peter Cawthron

unread,
Nov 10, 2013, 12:38:45 PM11/10/13
to zip...@googlegroups.com
Hello Daniel,

If it was Quantopian I might be able to say but I'm new to zipline. If I work anything out I'll let you know.

P.

Daniel Wu

unread,
Nov 10, 2013, 1:40:54 PM11/10/13
to Peter Cawthron, zipline
Thanks so much Peter! Hopefully one last question (for now). How can I graph where the sells and buys were made? I'm trying to format the below code, but it doesn't seem to be working. No worries if you don't know. I've changed the below code to include self.buy and self.sell 

    f, (ax1,ax2) = plt.subplots(2, sharex=True)
    #axarr[0].figsize(10,10)
    ax1.plot(results2.portfolio_value)
    ax1.set_title('Portfolio Results')
    ax2.set_title('DJIA')
    ax2.plot(data['DJIA'])
    ax2.plot(results[['upper', 'lower', 'ave']])
    #fig = figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
    ax2.plot(results2[results2.buy==True].index, results.lower[results2.buy==True],
         '^', markersize=10, color='m')
    ax2.plot(results2[results2.sell==True].index, results.upper[results2.sell==True],
         'v', markersize=10, color='k')


@batch_transform
def get_prices(data, sids):
    prices = data.price[sids]
    return prices  

class BollingerBands(TradingAlgorithm):    
    
    def initialize(self, window_length=20, refresh_period=0):
        self.invested       = False
        self.first_time     = True
        self.buy = False
        self.sell = False
        self.window_length  = window_length
        self.refresh_period = refresh_period
        self.get_prices = get_prices(refresh_period=self.refresh_period,
                                           window_length=self.window_length)

    def handle_data(self, data):
        self.buy = False
        self.sell = False
        prices = self.get_prices.handle_data(data, STOCKS)
        if prices is None:
            return
        if self.first_time:
            print prices.head()
            self.first_time = False
            
        ave = pd.stats.moments.rolling_mean(prices['DJIA'], 20)[-1:]
        std = pd.stats.moments.rolling_std(prices['DJIA'], 20)[-1:]

        upper = ave + (2 * std)
        lower = ave - (2 * std)
        
        #print upper, ave, lower

        # SELL AT TOP OF BAND
        if data[STOCKS[0]].price > upper and not self.invested:
            print "Selling..."
            self.order(STOCKS[0],-100)
            self.invested = True
            self.buy= True
        elif data[STOCKS[0]].price < lower and self.invested:
            print "Covering short position..."
            self.order(STOCKS[0], 100)
            self.invested = False
            self.sell = True
        self.record(ave=ave, upper=upper,lower=lower,buy=self.buy,sell=self.sell, invested=self.invested)
        return


Peter Cawthron

unread,
Nov 10, 2013, 2:47:11 PM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Daniel,

This is justed copy/pasted from the dual_ema_talib.py example.

P.



from datetime import datetime 
import pytz
from zipline.algorithm import TradingAlgorithm
from zipline.transforms import batch_transform
from zipline.utils.factory import load_from_yahoo
import pandas as pd
import matplotlib.pyplot as plt

window           = 20

STOCKS = ['AAPL', 'MSFT']

@batch_transform
def get_prices(data, sids):
    prices = data.price[sids]
    return prices 

class BollingerBands(TradingAlgorithm):   
   
    def initialize(self, window_length=20, refresh_period=0):
        self.invested       = False
        self.first_time     = True
        self.window_length  = window_length
        self.refresh_period = refresh_period
        self.get_prices = get_prices(refresh_period=self.refresh_period,
                                           window_length=self.window_length)

    def handle_data(self, data):
        prices = self.get_prices.handle_data(data, STOCKS)
        if prices is None:
            return

        self.buy = False
        self.sell = False

        if self.first_time:
            print prices.head()
            self.first_time = False
           
        ave = pd.stats.moments.rolling_mean(prices['AAPL'], 20)[-1:]
        std = pd.stats.moments.rolling_std(prices['AAPL'], 20)[-1:]
        upper = ave + 2 * std
        lower = ave - 2 * std
       
        #print upper, ave, lower

        # SELL AT TOP OF BAND
        if data[STOCKS[0]].price > upper and not self.invested:
            print "Selling..."
            self.order(STOCKS[0],-100)
            self.sell = True
            self.invested = True

        elif data[STOCKS[0]].price < lower and self.invested:
            print "Covering short position..."
            self.order(STOCKS[0], 100)
            self.buy = True
            self.invested = False

        self.record(AAPL=data['AAPL'].price,
                    upper=upper,
                    lower=lower,
                    buy=self.buy,
                    sell=self.sell)


if __name__ == '__main__': 
    start = datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc) 
    end = datetime(2008, 6, 30, 0, 0, 0, 0, pytz.utc) 
    data = load_from_yahoo(stocks=STOCKS, indexes={}, start=start, 
                           end=end) 
    simple_algo = BollingerBands() 
    results = simple_algo.run(data).dropna()
    #results.portfolio_value.plot()
    #plt.show()

    fig = plt.figure()
    ax1 = fig.add_subplot(211, ylabel='portfolio value')
    results.portfolio_value.plot(ax=ax1)

    ax2 = fig.add_subplot(212)
    results[['AAPL']].plot(ax=ax2)

    ax2.plot(results.ix[results.buy].index, results.lower[results.buy],

             '^', markersize=10, color='m')
    ax2.plot(results.ix[results.sell].index, results.upper[results.sell],

             'v', markersize=10, color='k')
    plt.legend(loc=0)
    plt.gcf().set_size_inches(18, 8)
    plt.show()

Daniel Wu

unread,
Nov 10, 2013, 4:51:08 PM11/10/13
to Peter Cawthron, zipline
Thanks so much. you are amazing! I was also formatting from a different example (moving average), but i guess i was missing something because the code above works.

hopefully last question for today: can you explain why you need to grab the last value (and not the first) for std and average? 
std = pd.stats.moments.rolling_std(prices['AAPL'], 20)[-1:]



Thank you!
Dan

PhD Student: Social Policy and Sociology


Peter Cawthron

unread,
Nov 10, 2013, 5:13:43 PM11/10/13
to zip...@googlegroups.com, Peter Cawthron
Hello Danilel,

I view the prices data as a row with the oldest (0th) on the left and the newest (length-1th) on the right i.e.

        if self.first_time:
            print prices.head()
            print prices.ix[0]
            print prices.ix[window - 1]
            self.first_time = False

Then the most recent 'rolling' mean or standard deviation would also be the right-most i.e. [-1:]. I hope that's right!

P.
Reply all
Reply to author
Forward
0 new messages