def __init__(self, settings): DailyTrigger.__init__(self, settings) # this is a hack until we implement subscribing to multiple streams of data self.uptrendcheck = dict() # if True, we are filtering on uptrending SPY, if false, downtrending SPY # we are using the PREVIOUS bar data, hence the use of HistoricMetric. # we do not now if TODAY will close above/below the ma before we enter our # day trades. An alternative would have been to use the open ticker = settings.get("TrendFilterDailyTrigger", "ticker") self.uptrend = settings.getboolean("TrendFilterDailyTrigger", "uptrend") period = settings.getint("TrendFilterDailyTrigger", "period") close = AdjustedClose() lastclose = HistoricMetric(close, 1) ma = SimpleMovingAverage(metric=close, period=period) lastma = HistoricMetric(ma, 1) fromdt = datetime(1900, 1, 1) todt = datetime.now() datastore = datastorefactory.get_datastore() for dd in datastore.getDailyData(ticker, fromdt, todt): close.handle(dd) lastclose.handle(dd) ma.handle(dd) lastma.handle(dd) if lastma.ready() and lastclose.ready(): if lastclose.value() > lastma.value(): self.uptrendcheck[dd.date] = True if lastclose.value() < lastma.value(): self.uptrendcheck[dd.date] = False
class NewHighDailyTrigger(Check): def __init__(self, settings): self.highperiod = settings.getfloat("NewHighDailyTrigger", "highperiod") self.minprice = settings.getfloat("NewHighDailyTrigger", "minprice") self.maxprice = settings.getfloat("NewHighDailyTrigger", "maxprice") self.high = High() self.highesthigh = Highest(metric=self.high, period=self.highperiod) self.lasthighesthigh = HistoricMetric(metric=self.highesthigh,period=1) def ready(self): if self.lasthighesthigh.ready(): return True return False def check(self): if self.ready() and self.lastpd != None: if self.high.value() > self.lasthighesthigh.value() \ and self.lasthighesthigh.value() >= self.minprice \ and self.lasthighesthigh.value() <= self.maxprice: return True return False def handle(self, perioddata): self.high.handle(perioddata) self.highesthigh.handle(perioddata) self.lasthighesthigh.handle(perioddata) self.lastpd = perioddata def getLongPrice(self): return self.lasthighesthigh.value() def getShortPrice(self): return None
class MultiTapEntryManager(EntryManager): """ Enters a trade after a given number of taps at a high or low, within a tolerance, and on the subsequent break of that high. """ def __init__(self, settings, name=None): EntryManager.__init__(self, settings, name) self.numTaps = settings.getint("MultiTapEntry", "numTaps") self.tapMargin = settings.getfloat("MultiTapEntry", "tapMargin") self.donchianStop = settings.getint("MultiTapEntry", "donchianStop") self.minhour = self._getintsetting(settings, "MultiTapEntry", "minhour") self.maxhour = self._getintsetting(settings, "MultiTapEntry", "maxhour") self.target = self._getfloatsetting(settings, "MultiTapEntry", "target") # TODO replace trigger logic with some kind of hinting/info from DailyDataTrigger self.triggerLongPrice = None self.low = Low() self.lowest = Lowest(self.low, self.donchianStop) self.donchStop = HistoricMetric(self.lowest, 1) self.high = High() self.lasthigh = HistoricMetric(self.high, 1) self.highs = list() self.taps = 0 def handle(self, perioddata): self.taps = 0 if self.lasthigh.ready() and self.lasthigh.value() >= self.triggerLongPrice: for high in self.highs: if self.lasthigh.value() - high <= self.tapMargin: self.taps += 1 self.low.handle(perioddata) self.lowest.handle(perioddata) self.donchStop.handle(perioddata) self.high.handle(perioddata) self.lasthigh.handle(perioddata) self.highs.append(perioddata.high) self.lastdd = perioddata def checkTrade(self): if self.lasthigh.ready() and self.donchStop.ready() \ and self.taps >= self.numTaps and self.lastdd.high > self.lasthigh.value() \ and (self.minhour is None or self.lastdd.date.hour >= self.minhour) \ and (self.maxhour is None or self.lastdd.date.hour < self.maxhour): entryPrice = max(self.lastdd.open, self.lasthigh.value() + 0.01) stop = max(0.0, self.donchStop.value() - 0.01) trade = Trade(self.lastdd.stock, self.lastdd.date, entryPrice, stop) if self.target is not None: trade.target = trade.entryPrice + (trade.entryPrice - trade.stop) * self.target return trade return None
class GapUpEntryManager: def __init__(self, settings): self.minPrice = settings.getfloat("GapAndGoSwingEntry", "minPrice") self.minAvgVol = settings.getint("GapAndGoSwingEntry", "minAvgVol") self.minPercent = settings.getfloat("GapAndGoSwingEntry", "minPercent") targetstr = settings.get("GapAndGoSwingEntry", "target") if targetstr == "None": self.target = None else: self.target = float(targetstr) self.volume = Volume() self.avgvol = SimpleMovingAverage(metric=self.volume, period=21) self.opn = AdjustedOpen() self.close = AdjustedClose() self.lastClose = HistoricMetric(metric=self.close, period=1) self.high = AdjustedHigh() self.lastHigh = HistoricMetric(metric=self.high, period=1) self.low = AdjustedLow() self.inBottomRange=0.1 self.inTopRange=None def handle(self, perioddata): self.close.handle(perioddata) self.lastClose.handle(perioddata) self.high.handle(perioddata) self.lastHigh.handle(perioddata) self.opn.handle(perioddata) self.volume.handle(perioddata) self.avgvol.handle(perioddata) self.low.handle(perioddata) self.lastdd = perioddata def checkTrade(self): if self.close.ready() and self.lastClose.ready() \ and self.opn.ready() and self.volume.ready() \ and self.avgvol.ready(): if self.lastdd.close >= self.minPrice and self.avgvol.value() >= self.minAvgVol \ and self.lastClose.value() > 0 and self.opn.value() > 0 \ and ((self.opn.value()-self.lastClose.value())/self.lastClose.value()) >= self.minPercent \ and self.low.value() >= self.lastHigh.value() \ and (self.inBottomRange == None or ((self.lastdd.adjustedHigh != self.lastdd.adjustedLow) and (self.close.value()-self.lastdd.adjustedLow)/(self.lastdd.adjustedHigh-self.lastdd.adjustedLow)) <= self.inBottomRange) \ and (self.inTopRange == None or ((self.lastdd.adjustedHigh != self.lastdd.adjustedLow) and (self.close.value()-self.lastdd.adjustedLow)/(self.lastdd.adjustedHigh-self.lastdd.adjustedLow)) >= self.inTopRange): stop = max(0.0, self.low.value() - 0.01) # stop = max(0.0, self.lastHigh.value()) trade = Trade(self.lastdd.stock, self.lastdd.date, self.close.value(), stop) if self.target != None: target = self.close.value() + ((self.close.value()-stop)*self.target) trade.target = target return trade return None def recommendedPreload(self): return 22
class PullbackEntryManager(EntryManager): """ Enter after a minimum move size, on a subsequent pullback to a moving average. """ def __init__(self, settings, name=None): EntryManager.__init__(self, settings, name) self.maperiod = settings.getint("PullbackEntry", "ma") self.minmove = self._getfloatsetting(settings, "PullbackEntry", "minmove") self.target = self._getfloatsetting(settings, "PullbackEntry", "target") self.opn = None self.low = None self.high = None self.ma = SimpleMovingAverage(period=self.maperiod) self.lastma = HistoricMetric(self.ma, 1) def handle(self, perioddata): if self.periodData is None or self.periodData.date.day != perioddata.date.day: self.opn = None self.low = None self.high = None EntryManager.handle(self, perioddata) self.ma.handle(perioddata) if self.opn is None: self.opn = perioddata.open if self.low is None or perioddata.low < self.low: self.low = perioddata.low if self.high is None or perioddata.high > self.high: self.high = perioddata.high def checkTrade(self): if self.lastma.ready() and ((self.high - self.opn) / self.opn) >= self.minmove: if self.periodData.low <= self.lastma.value(): entry_price = min(self.periodData.open, self.lastma.value()) stop = self.low - 0.01 trade = Trade(self.lastdd.stock, self.lastdd.date, entry_price, stop) if self.target is not None: trade.target = trade.entryPrice + (trade.entryPrice - trade.stop) * self.target return trade if self.lastma.ready() and ((self.opn - self.low) / self.opn) >= self.minmove: if self.periodData.high >= self.lastma.value(): entry_price = max(self.periodData.open, self.lastma.value()) stop = self.high + 0.01 trade = Trade(self.lastdd.stock, self.lastdd.date, entry_price, stop) if self.target is not None: trade.target = trade.entryPrice + (trade.entryPrice - trade.stop) * self.target return trade return None
class GapDailyTrigger(PriceVolumeDailyTrigger): def __init__(self, settings): PriceVolumeDailyTrigger.__init__(self, settings) self.mingap = settings.getfloat("GapDailyTrigger", "mingap") dfilter = settings.get("GapDailyTrigger", "donchianfilter") if dfilter == "None": self.lasthighesthigh = None self.lastlowestlow = None else: self.high = High() self.highesthigh = Highest(self.high, int(dfilter)) self.lasthighesthigh = HistoricMetric(self.highesthigh, 1) self.low = Low() self.lowestlow = Lowest(self.low, int(dfilter)) self.lastlowestlow = HistoricMetric(self.lowestlow, 1) def ready(self): if PriceVolumeDailyTrigger.ready(self) and self.perioddata is not None and self.lastperioddata is not None: return True def check(self): if self.mingap >= 0.0: if PriceVolumeDailyTrigger.check(self) and self.perioddata is not None and \ (( self.perioddata.open - self.lastperioddata.close) / self.lastperioddata.close) >= self.mingap \ and (self.lasthighesthigh is None or ( self.lasthighesthigh.ready() and self.perioddata.open >= self.lasthighesthigh.value())): return True else: if PriceVolumeDailyTrigger.check(self) and self.perioddata is not None and \ (( self.perioddata.open - self.lastperioddata.close) / self.lastperioddata.close) <= self.mingap \ and (self.lastlowestlow is None or ( self.lastlowestlow.ready() and self.perioddata.open <= self.lastlowestlow.value())): return True return False def handle(self, perioddata): # store previous perioddata before we call superclass self.lastperioddata = self.perioddata if self.lasthighesthigh is not None: self.high.handle(perioddata) self.highesthigh.handle(perioddata) self.lasthighesthigh.handle(perioddata) self.low.handle(perioddata) self.lowestlow.handle(perioddata) self.lastlowestlow.handle(perioddata) PriceVolumeDailyTrigger.handle(self, perioddata)
class PriceVolumeDailyTrigger(DailyTrigger): def __init__(self, settings): DailyTrigger.__init__(self, settings) self.minprice = settings.getfloat("PriceVolumeDailyTrigger", "minprice") self.minvol = settings.getfloat("PriceVolumeDailyTrigger", "minvol") self.vol = Volume() self.avgvol = SimpleMovingAverage(self.vol, 21) self.lastavgvol = HistoricMetric(self.avgvol, 1) self.close = Close() self.lastclose = HistoricMetric(self.close, 1) self._addMetrics(self.vol, self.avgvol, self.lastavgvol, self.close, self.lastclose) def check(self): if self.lastclose.ready() and self.lastavgvol.ready() \ and self.lastclose.value() > self.minprice \ and self.lastavgvol.value() > self.minvol: return True return False
class BulkowskiGapDailyTrigger(PriceVolumeDailyTrigger): def __init__(self, settings): PriceVolumeDailyTrigger.__init__(self, settings) self.mingap = settings.getfloat("BulkowskiGapDailyTrigger", "mingap") self.open = AdjustedOpen() self.close = AdjustedClose() self.lastClose = HistoricMetric(self.close, 1) self.sma = SimpleMovingAverage(metric=self.close, period=50) self.lastsma = HistoricMetric(self.sma, 1) self._addMetrics(self.open, self.close, self.lastClose, self.sma, self.lastsma) def check(self): if PriceVolumeDailyTrigger.check(self): if self.lastClose.value() > 0 and self.open.value() > 0: gap = (self.lastClose.value() - self.open.value())/self.lastClose.value() if gap >= self.mingap: return True return False
class DonchianChannelStopManager(BasicStopManager): def __init__(self, settings): BasicStopManager.__init__(self, settings) channelPeriod = settings.getint("DonchianChannelStopManager", "channelPeriod") self.low = Low() self.high = High() self.lowestLow = Lowest(self.low, channelPeriod) self.highestHigh = Highest(self.high, channelPeriod) self.lastLowestLow = HistoricMetric(self.lowestLow, 1) self.lastHighestHigh = HistoricMetric(self.highestHigh, 1) def handle(self, perioddata): BasicStopManager.handle(self, perioddata) self.low.handle(perioddata) self.high.handle(perioddata) self.lowestLow.handle(perioddata) self.highestHigh.handle(perioddata) self.lastLowestLow.handle(perioddata) self.lastHighestHigh.handle(perioddata) def checkTrade(self, trade): if trade != None and trade.entryPrice > trade.stop: trade.trailingstop = max(trade.stop, self.lastLowestLow.value()-0.01) if trade != None and trade.entryPrice < trade.stop: trade.trailingstop = min(trade.stop, self.lastHighestHigh.value() + 0.01) if trade != None and trade.exit == None \ and trade.entryPrice > trade.stop and self.perioddata.low <= trade.trailingstop: trade.exit = self.perioddata.date trade.exitPrice = min(self.perioddata.open, trade.trailingstop) if trade != None and trade.exit == None \ and trade.entryPrice < trade.stop and self.perioddata.high >= trade.trailingstop: trade.exit = self.perioddata.date trade.exitPrice = max(self.perioddata.open, trade.trailingstop) return trade def recommendedPreload(self): return self.lastHighestHigh.recommendedPreload()
class MomoAfternoonEntryManager(NoScaleInEntryManager): """ Enter on an afternoon break of highs if a minimum move was made in the morning""" def __init__(self, settings, name=None): EntryManager.__init__(self, settings, name=name) self.donchianstop = settings.getint("Strategy", "donchianstop") self.minhour = settings.getint("Strategy", "minhour") self.maxhour = settings.getint("Strategy", "maxhour") self.target = self._getintsetting("Strategy", "target") self.minmove = settings.getfloat("Strategy", "minmove") self.maxprice = self._getfloatsetting("Strategy", "maxprice") self.low = Low() self.lowestlow = Lowest(self.low, self.donchianstop) self.prevLowestLow = HistoricMetric(self.lowestlow, 1) self._addMetric(self.low) self._addMetric(self.lowestlow) self._addMetric(self.prevLowestLow) self.hod = None self.open = None self.hoddate = None def handle(self, perioddata): if self.open is None or self.periodData.date.day != perioddata.date.day: self.open = perioddata.open if self.periodData is not None: # quick, use previous value to update hod if self.hod is None or self.periodData.high >= self.hod: self.hod = self.periodData.high self.hoddate = self.periodData.date # if we are on a new day, clear values if self.periodData.date.day != perioddata.date.day: self.hod = None self.hoddate = None EntryManager.handle(self, perioddata) def _checkTradeNoScale(self): if self.hod is not None \ and self.periodData.high > self.hod \ and self.prevLowestLow.ready() \ and (self.hoddate.hour < 12 or (self.hoddate.hour == 12 and self.hoddate.minute == 0)) \ and self.periodData.date.hour >= self.minhour \ and (self.periodData.date.hour < self.maxhour or (self.periodData.date.hour == self.maxhour and self.periodData.date.minute == 0)) \ and ((self.hod - self.open) / self.open) >= self.minmove: # new trade entry = max(self.periodData.open, self.hod + 0.01) if self.maxprice is None or entry <= self.maxprice: stop = self.prevLowestLow.value() - 0.01 trade = Trade(self.periodData.stock, self.periodData.date, entry, stop) if self.target is not None: target = entry + ((entry - stop) * 2) trade.target = target if self.periodData.low < stop: trade.exit = self.periodData.date trade.exitPrice = stop return trade return None
class NBarEntryManager: def __init__(self, settings): nbars = settings.getint("NBarEntry", "nbars") self.high = High() self.low = Low() self.range = Subtract(self.high, self.low) self.minrange = Lowest(self.range, nbars) self.lastrange = HistoricMetric(self.range, 1) self.lastminrange = HistoricMetric(self.minrange, 2) self.lasthigh = HistoricMetric(self.high,1) self.lastlow = HistoricMetric(self.low,1) def handle(self, perioddata): self.high.handle(perioddata) self.low.handle(perioddata) self.range.handle(perioddata) self.minrange.handle(perioddata) self.lastrange.handle(perioddata) self.lastminrange.handle(perioddata) self.lasthigh.handle(perioddata) self.lastlow.handle(perioddata) self.lastdd = perioddata def checkTrade(self): if self.lastrange.ready() and self.lastminrange.ready() \ and self.lastrange.value() < self.lastminrange.value(): # we have an N-bar, take a break long or short and hold to close if self.lastdd.high > self.lasthigh.value(): entry = max(self.lastdd.open, self.lasthigh.value() + 0.01) stop = self.lastlow.value() - 0.01 if entry < 0: entry = 0 if stop < 0: stop = 0 if entry > stop and entry > 0: trade = Trade(self.lastdd.stock, self.lastdd.date, entry, stop) trade.exit = self.lastdd.date if self.lastdd.low <= stop: trade.exitPrice = stop else: trade.exitPrice = self.lastdd.close return trade elif self.lastdd.low < self.lastlow.value(): entry = min(self.lastdd.open,self.lastlow.value() - 0.01) stop = self.lasthigh.value() + 0.01 if entry < 0: entry = 0 if stop < 0: stop = 0 if entry < stop and entry > 0: trade = Trade(self.lastdd.stock, self.lastdd.date, entry, stop) trade.exit = self.lastdd.date if self.lastdd.high >= stop: trade.exitPrice = stop else: trade.exitPrice = self.lastdd.close return trade return None def recommendedPreload(self): return self.lastminrange.recommendedPreload()
def findsetups(self, fromdt, todt): numstopouts = 0 stocks = self._getTickers(fromdt, datastore) for stock in stocks: # padded extra to make sure 200 day sma has enough trading days to work with before our window dailydata = datastore.getDailyData(stock, fromdt - timedelta(days=(self.slowma*2)), todt) close = Close() fastma = SimpleMovingAverage(period=self.fastma) slowma = SimpleMovingAverage(period=self.slowma) lastfastma = HistoricMetric(metric=fastma, period=1) lastslowma = HistoricMetric(metric=slowma, period=1) atr = ATR(period=14) lastdd = None trade = None for pd in dailydata: close.handle(pd) fastma.handle(pd) slowma.handle(pd) lastfastma.handle(pd) lastslowma.handle(pd) atr.handle(pd) # check for long stopout if trade != None and pd.low < trade.trailingstop: trade.exit = pd.date trade.exitPrice = min(pd.open, trade.trailingstop) self.tradeManager.addTrade(trade) numstopouts = numstopouts + 1 trade = None # check for long exit if trade != None and fastma.value() < slowma.value(): trade.exit = pd.date trade.exitPrice = pd.close self.tradeManager.addTrade(trade) trade = None if fastma.ready() and slowma.ready() and lastfastma.ready() and lastslowma.ready() and atr.ready(): pass # check for new long if trade == None and fastma.ready() and slowma.ready() \ and lastfastma.ready() and lastslowma.ready() \ and (self.atrStop == None or atr.ready()) \ and pd.date >= fromdt \ and fastma.value() > slowma.value() \ and lastfastma.value() <= lastslowma.value() \ and pd.close >= self.minprice: stop = 0 if self.atrStop != None: stop = max(0,pd.close - (float(self.atrStop) * atr.value())) if self.percentStop != None: stop = max(0, pd.close * (1.0 - self.percentStop)) trade = Trade(stock=stock, entry=pd.date, entryPrice=pd.close, stop=stop) if trade != None and trade.entry == None: trade.exit = lastdd.date trade.exitPrice = lastdd.close self.tradeManager.addTrade(trade) trade = None print "num stopouts was %d" % numstopouts return self.tradeManager.getStats()
class MomoDailyTrigger(PriceVolumeDailyTrigger): def __init__(self, settings): PriceVolumeDailyTrigger.__init__(self, settings) self.minatrmultiple = settings.getfloat("MomoDailyTrigger", "minatrmultiple") self.closepercent = settings.get("MomoDailyTrigger", "closepercent") self.openpercent = settings.get("MomoDailyTrigger", "openpercent") self.takeLongs = settings.getboolean("MomoDailyTrigger", "takeLongs") self.takeShorts = settings.getboolean("MomoDailyTrigger", "takeShorts") minaprstr = settings.get("MomoDailyTrigger", "minapr") self.minapr = None if minaprstr != "None": self.minapr = float(minaprstr) dfilter = settings.get("MomoDailyTrigger", "donchianfilter") self.high = High() self.low = Low() self.atr = ATR(14) self.lastatr = HistoricMetric(self.atr, 1) if dfilter == "None": self.lasthighesthigh = None self.lastlowestlow = None else: self.highesthigh = Highest(self.high, int(dfilter)) self.lasthighesthigh = HistoricMetric(self.highesthigh, 1) self.lowestlow = Lowest(self.low, int(dfilter)) self.lastlowestlow = HistoricMetric(self.lowestlow, 1) def ready(self): if PriceVolumeDailyTrigger.ready(self) and self.perioddata is not None and self.lastperioddata is not None: return True def check(self): if self.lastperioddata.close <= 0: return False if self.minapr is None or (self.lastatr.value() / self.lastperioddata.close) < self.minapr: return False if (self.lastperioddata.close - self.lastperioddata.open) < (self.lastatr.value() * self.minatrmultiple): return False if self.lastperioddata.close > self.lastperioddata.open and self.takeLongs: if ((self.lastperioddata.high - self.lastperioddata.close) / ( self.lastperioddata.high - self.lastperioddata.low)) > self.closepercent: return False if ((self.lastperioddata.open - self.lastperioddata.low) / ( self.lastperioddata.high - self.lastperioddata.low)) > self.openpercent: return False return True if self.lastperioddata.close < self.lastperioddata.open and self.takeShorts: if ((self.lastperioddata.close - self.lastperioddata.low) / ( self.lastperioddata.high - self.lastperioddata.low)) > self.closepercent: return False if ((self.lastperioddata.high - self.lastperioddata.open) / ( self.lastperioddata.high - self.lastperioddata.low)) > self.openpercent: return False return True def handle(self, perioddata): # store previous perioddata before we call superclass self.lastperioddata = self.perioddata self.high.handle(perioddata) self.low.handle(perioddata) self.atr.handle(perioddata) self.lastatr.handle(perioddata) if self.lasthighesthigh is not None: self.highesthigh.handle(perioddata) self.lasthighesthigh.handle(perioddata) self.lowestlow.handle(perioddata) self.lastlowestlow.handle(perioddata) PriceVolumeDailyTrigger.handle(self, perioddata) def getLongPrice(self): if self.lastperioddata.close > self.lastperioddata.open: return self.lastperioddata.close return None def getShortPrice(self): if self.lastperioddata.open > self.lastperioddata.close: return self.lastperioddata.close return None