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)
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 beif 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)
Many thanks to prof. Tucker BALCH, GA-TECH [GA], and his team, for
QSTK
-initiative and the innovative approaches to Quantitative Finance Modelling.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.
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 )
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.