pythonquantitative-financealgorithmic-tradingziplinepyalgotrade

More examples for the event profiler in pyalgotrade


I'm trying to learn how to implement custom strategies into the event profiler for pyalgotrade. This is the default example they give.

from pyalgotrade           import eventprofiler
from pyalgotrade.technical import stats
from pyalgotrade.technical import roc
from pyalgotrade.technical import ma
from pyalgotrade.tools     import yahoofinance

#     Event inspired on an example from Ernie Chan's book:
#    'Algorithmic Trading: Winning Strategies and Their Rationale'

class BuyOnGap(eventprofiler.Predicate):
    def __init__(self, feed):
        stdDevPeriod = 90
        smaPeriod = 20
        self.__returns = {}
        self.__stdDev = {}
        self.__ma = {}
        for instrument in feed.getRegisteredInstruments():
            priceDS = feed[instrument].getAdjCloseDataSeries()
            #      Returns over the adjusted close values.
            self.__returns[instrument] = roc.RateOfChange(priceDS, 1)
            #      StdDev over those returns.
            self.__stdDev[instrument] = stats.StdDev(self.__returns[instrument], stdDevPeriod)
            #      MA over the adjusted close values.
            self.__ma[instrument] = ma.SMA(priceDS, smaPeriod)

    def __gappedDown(self, instrument, bards):
        ret = False
        if self.__stdDev[instrument][-1] != None:
            prevBar = bards[-2]
            currBar = bards[-1]
            low2OpenRet = (currBar.getAdjOpen() - prevBar.getAdjLow()) / float(prevBar.getAdjLow())
            if low2OpenRet < (self.__returns[instrument][-1] - self.__stdDev[instrument][-1]):
                ret = True
        return ret

    def __aboveSMA(self, instrument, bards):
        ret = False
        if self.__ma[instrument][-1] != None and bards[-1].getAdjOpen() > self.__ma[instrument][-1]:
            ret = True
        return ret

    def eventOccurred(self, instrument, bards):
        ret = False
        if self.__gappedDown(instrument, bards) and self.__aboveSMA(instrument, bards):
            ret = True
        return ret

def main(plot):
    instruments = ["AA", "AES", "AIG"]
    feed = yahoofinance.build_feed(instruments, 2008, 2009, ".")

    predicate = BuyOnGap(feed)
    eventProfiler = eventprofiler.Profiler(predicate, 5, 5)
    eventProfiler.run(feed, True)

    results = eventProfiler.getResults()
    print "%d events found" % (results.getEventCount())
    if plot:
        eventprofiler.plot(results)

if __name__ == "__main__":
    main(True)

enter image description here Does any one have a source of some more examples ?

I'm trying to figure out how the eventprofiler takes in and uses data, there's quite a few class methods that are called though and I'm finding it a little tricky to dissect it.

I want to start off simple and just use price and volume. It is, one strategy would be
if volume > 1000 and close < 50: event == True

Any help would be appreciated.

P.s.: bonus question: is there a similar event profiler for zipline ?

EDIT: Thanks to user3666197 I was able to make the changes I wanted to however i'm getting this error:

Traceback (most recent call last):
  File "C:\Users\David\Desktop\Python\Coursera\Computational Finance\Week2\PyAlgoTrade\Bitfinex\FCT\FCT_single_event_test.py", line 43, in <module>
    main(True)
  File "C:\Users\David\Desktop\Python\Coursera\Computational Finance\Week2\PyAlgoTrade\Bitfinex\FCT\FCT_single_event_test.py", line 35, in main
    eventProfiler.run(feed, True)
  File "C:\Python27\lib\site-packages\pyalgotrade\eventprofiler.py", line 215, in run
    disp.run()
  File "C:\Python27\lib\site-packages\pyalgotrade\dispatcher.py", line 102, in run
    eof, eventsDispatched = self.__dispatch()
  File "C:\Python27\lib\site-packages\pyalgotrade\dispatcher.py", line 90, in __dispatch
    if self.__dispatchSubject(subject, smallestDateTime):
  File "C:\Python27\lib\site-packages\pyalgotrade\dispatcher.py", line 68, in __dispatchSubject
    ret = subject.dispatch() is True
  File "C:\Python27\lib\site-packages\pyalgotrade\feed\__init__.py", line 105, in dispatch
    self.__event.emit(dateTime, values)
  File "C:\Python27\lib\site-packages\pyalgotrade\observer.py", line 59, in emit
    handler(*args, **kwargs)
  File "C:\Python27\lib\site-packages\pyalgotrade\eventprofiler.py", line 172, in __onBars
    eventOccurred = self.__predicate.eventOccurred(instrument, self.__feed[instrument])
  File "C:\Python27\lib\site-packages\pyalgotrade\eventprofiler.py", line 89, in eventOccurred
    raise NotImplementedError()
NotImplementedError
[Finished in 1.9s]

I've had a look at the source 'eventprofiler.py' and can't figure out what it is. Here's the code

from pyalgotrade import eventprofiler
from pyalgotrade.technical import stats
from pyalgotrade.technical import roc
from pyalgotrade.technical import ma
from pyalgotrade.barfeed import csvfeed

# Event inspired on an example from Ernie Chan's book:
# 'Algorithmic Trading: Winning Strategies and Their Rationale'

class single_event_strat( eventprofiler.Predicate ):
    def __init__(self,feed):
        self.__returns = {} # CLASS ATTR
        for inst in feed.getRegisteredInstruments():

            priceDS = feed[inst].getAdjCloseDataSeries() # STORE: priceDS ( a temporary representation )

            self.__returns[inst] = roc.RateOfChange( priceDS, 1 )
            # CALC:  ATTR <- Returns over the adjusted close values, consumed priceDS 
            #( could be expressed as self.__returns[inst] = roc.RateOfChange( ( feed[inst].getAdjCloseDataSeries() ), 1 ), 
            #but would be less readable

    def eventOccoured( self, instrument, aBarDS):
        if (aBarDS[-1].getVolume() > 1000 and aBarDS[-1].getClose() > 50 ):
            return True
        else: 
            return False

def main(plot):
    feed = csvfeed.GenericBarFeed(0)

    feed.addBarsFromCSV('FCT', "FCT_daily_converted.csv")

    predicate = single_event_strat(feed)
    eventProfiler = eventprofiler.Profiler(predicate, 5, 5)
    eventProfiler.run(feed, True)

    results = eventProfiler.getResults()
    print "%d events found" % (results.getEventCount())
    if plot:
        eventprofiler.plot(results)

if __name__ == "__main__":
    main(True)

Solution

  • Quant Prologue:

    Many thanks to prof. Tucker BALCH, GA-TECH [GA], and his team, for QSTK-initiative and the innovative approaches to Quantitative Finance Modelling. enter image description here

    How the eventprofiler takes in data?

    Simply, it receives access to the full feed both in eventprofiler.run( feed , ... ) and also as an eventprofiler.Predicate-wrapped feed, where it gets the all feed-details' access right into the .Predicate instance by a Class-parameter, so as to be able to calculate there implemented all details the strategy calculus requires. Smart, isn't it?

    The rest is done by a re-use of their class-methods.


    How to build one's own?

    It is quite enough to declare an appropriately equipped eventprofiler.Predicate, that will be injected into the evenprofiler instantiation:

    class DavidsSTRATEGY( eventprofiler.Predicate ):
          def __init__( self, feed ):                         # mandatory .__init__
              """                                               no need for this code
                                                                left here for didactic
                                                                purposes only
                                                                to show the principle
    
              self.__returns = {}                                         # CLASS ATTR
    
              for                inst in feed.getRegisteredInstruments():
                  priceDS = feed[inst].getAdjCloseDataSeries()            # STORE: priceDS ( a temporary representation )
                  self.__returns[inst] = roc.RateOfChange( priceDS, 1 )   # CALC:  ATTR <- Returns over the adjusted close values, consumed priceDS ( could be expressed as self.__returns[inst] = roc.RateOfChange( ( feed[inst].getAdjCloseDataSeries() ), 1 ), but would be less readable
              """
    
          def eventOccurred( self, instrument, aBarDS ):       # mandatory .eventOccured
              if (   aBarDS[-1].getVolume() > 1000             # "last" Bar's Volume >
                 and aBarDS[-1].getClose()  <   50             # "last" Bar's Close  <
                 ):
                     return True
              else:
                     return False
    

    The rest would be as simple as always:

    eventProfiler = eventprofiler.Profiler( predicate = DavidsSTRATEGY( feed ), 5, 5 )
    eventProfiler.run( feed, True )
    

    Quant Epilogue:

    A carefull reader has noted, that the proposed code does not call .getAdjClose() method. The reasoning is, that deep-backtesting may get skewed by the very Close adjustments, on making decisions in times, when adjustments are not yet known. It is a fair duty of every professional Quantitative Analyst to avoid any means of peeking into the future and carefully decide, where adjustments are needed, with respect to portfolio valuation, if an instrument holding time has exprienced an adjustment, within it's respective lifecycle, or not.