Inspired in QSTK, the eventprofiler module is a tool to analyze, statistically, how events affect future equity prices. The event profiler scans over historical data for a specified event and then calculates the impact of that event on the equity prices in the past and the future over a certain lookback period.
The goal of this tool is to help you quickly validate an idea, before moving forward with the backtesting process.
Bases: object
Results from the profiler.
Returns the number of events occurred. Events that are on the boundary are skipped.
Bases: object
Base class for event identification. You should subclass this to implement the event identification logic.
Override (mandatory) to determine if an event took place in the last bar (bards[-1]).
| Parameters: | 
 | 
|---|---|
| Return type: | boolean. | 
Bases: object
This class is responsible for scanning over historical data and analyzing returns before and after the events.
| Parameters: | 
|---|
Runs the analysis using the bars supplied by the feed.
| Parameters: | 
 | 
|---|
Plots the result of the analysis.
| Parameters: | profilerResults (Results.) – The result of the analysis | 
|---|
The following example is inspired on the ‘Buy-on-Gap Model’ from Ernie Chan’s book: ‘Algorithmic Trading: Winning Strategies and Their Rationale’:
- The idea is to select a stock near the market open whose returns from their previous day’s lows to today’s open are lower that one standard deviation. The standard deviation is computed using the daily close-to-close returns of the last 90 days. These are the stocks that “gapped down”.
- This is narrowed down by requiring the open price to be higher than the 20-day moving average of the closing price.
from __future__ import print_function
from pyalgotrade import eventprofiler
from pyalgotrade.technical import stats
from pyalgotrade.technical import roc
from pyalgotrade.technical import ma
from pyalgotrade.tools import quandl
# 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):
        super(BuyOnGap, self).__init__()
        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] is not None:
            prevBar = bards[-2]
            currBar = bards[-1]
            low2OpenRet = (currBar.getOpen(True) - prevBar.getLow(True)) / float(prevBar.getLow(True))
            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] is not None and bards[-1].getOpen(True) > 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 = ["IBM", "AES", "AIG"]
    feed = quandl.build_feed("WIKI", 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)
The code is doing 4 things:
- Declaring a Predicate that implements the ‘Buy-on-Gap Model’ event identification.
- Loading bars for some stocks.
- Running the analysis.
- Plotting the results.
This is what the output should look like:
 
2017-07-22 00:26:06,574 quandl [INFO] Downloading IBM 2008 to ./WIKI-IBM-2008-quandl.csv
2017-07-22 00:26:08,299 quandl [INFO] Downloading AES 2008 to ./WIKI-AES-2008-quandl.csv
2017-07-22 00:26:09,849 quandl [INFO] Downloading AIG 2008 to ./WIKI-AIG-2008-quandl.csv
2017-07-22 00:26:11,513 quandl [INFO] Downloading IBM 2009 to ./WIKI-IBM-2009-quandl.csv
2017-07-22 00:26:13,128 quandl [INFO] Downloading AES 2009 to ./WIKI-AES-2009-quandl.csv
2017-07-22 00:26:14,626 quandl [INFO] Downloading AIG 2009 to ./WIKI-AIG-2009-quandl.csv
15 events found
Note that Cummulative returns are normalized to the time of the event.