def testClenowTradeStart(self): mf = MultiFeed() self.setupFeed(mf) strat = ClenowBreakoutStrategy(mf,tradeStart=datetime.datetime(2012,8,11)) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() strat.exitPositions() strat.getBroker().executeSessionClose() tlog = '%s/trade3.log' % os.path.dirname(__file__) tradesAnalyzer.writeTradeLog(tlog) self.assertAlmostEqual(strat.getResult(),986794.67,places=2) self.assertEqual(tradesAnalyzer.getCount(),5) self.assertEqual(tradesAnalyzer.getProfitableCount(),1) self.assertEqual(tradesAnalyzer.getUnprofitableCount(),4) self.assertTrue(test_util.file_compare('%s/trade3.reflog' % os.path.dirname(__file__), tlog)) os.remove(tlog)
def testBasic(self): mf = MultiFeed() mf.register_feed(self._feed) strat = MyStrategy(mf) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() strat.exitPositions() self.assertAlmostEqual(strat.getResult(), 996193.19, places=2) self.assertEqual(tradesAnalyzer.getCount(), 2) self.assertEqual(tradesAnalyzer.getProfitableCount(), 0) self.assertEqual(tradesAnalyzer.getUnprofitableCount(), 2) tlog = "%s/trade.log" % os.path.dirname(__file__) tradesAnalyzer.writeTradeLog(tlog) self.assertTrue(test_util.file_compare("%s/trade1.reflog" % os.path.dirname(__file__), tlog)) os.remove(tlog)
def testMacross(self): mf = MultiFeed() self.setupFeed(mf) strat = MACrossStrategy(mf,shortPeriod=10,longPeriod=100) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() strat.exitPositions() tlog = '%s/trade5.log' % os.path.dirname(__file__) tradesAnalyzer.writeTradeLog(tlog) self.assertAlmostEqual(strat.getResult(),992595.00,places=2) self.assertEqual(tradesAnalyzer.getCount(),10) self.assertEqual(tradesAnalyzer.getProfitableCount(),4) self.assertEqual(tradesAnalyzer.getUnprofitableCount(),6) self.assertTrue(test_util.file_compare('%s/trade5.reflog' % os.path.dirname(__file__), tlog)) os.remove(tlog)
def testClenowTradeStart(self): mf = MultiFeed() self.setupFeed(mf) strat = ClenowBreakoutStrategy(mf, tradeStart=datetime.datetime( 2012, 8, 11)) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() strat.exitPositions() strat.getBroker().executeSessionClose() tlog = '%s/trade3.log' % os.path.dirname(__file__) tradesAnalyzer.writeTradeLog(tlog) self.assertAlmostEqual(strat.getResult(), 986794.67, places=2) self.assertEqual(tradesAnalyzer.getCount(), 5) self.assertEqual(tradesAnalyzer.getProfitableCount(), 1) self.assertEqual(tradesAnalyzer.getUnprofitableCount(), 4) self.assertTrue( test_util.file_compare( '%s/trade3.reflog' % os.path.dirname(__file__), tlog)) os.remove(tlog)
def testNeg(self): f1 = Feed(self._inst1) f2 = Feed(self._inst1) mf = MultiFeed() mf.register_feed(f1) # this should fail because both feeds are for the same symbol with self.assertRaises(Exception): mf.register_feed(f2)
def __init__(self, sectorMap, modelType=None, cash=1000000, tradeStart=None, compounding=True, positionsFile=None, equityFile=None, returnsFile=None, summaryFile=None, parms=None): self._runGroups = {} self._startingCash = cash self._db = InstrumentDb.Instance() self._feed = MultiFeed() self._broker = BacktestingFuturesBroker( cash, self._feed, commission=FuturesCommission(2.50)) show = True for sec in sectorMap: for sym in sectorMap[sec]: self._feed.register_feed(Feed(self._db.get(sym))) # if desired can instantiate a strategy per symbol - may actually # want to think about this as a default behavior. The only thing # impacted is reporting of sector results # strategySymbols = { sym } # rgkey = '%s-%s' % (sec, sym) strategySymbols = sectorMap[sec] rgkey = sec if not modelType: modelType = 'breakout' strategy = StrategyFactory.create(modelType, self._feed, symbols=strategySymbols, broker=self._broker, cash=cash, compounding=compounding, parms=parms) self._runGroups[rgkey] = InstrumentedStrategy(strategy) if show: show = False strategy.showParms() self._trading = False self._posfile = self.__openFileorNull(positionsFile) self._equityfile = self.__openFileorNull(equityFile) self._returnsfile = self.__openFileorNull(returnsFile) self._summaryfile = self.__openFileorNull(summaryFile) self._dd = DrawDown() self._dd.attached(self) self._positionAlerts = []
def testMarketOrderShort(self): self._placed_smo = False mf = MultiFeed() mf.register_feed(self._feed) self._broker = BacktestingBroker(10000, mf) mf.subscribe(self.on_bars_3) self._broker.getOrderUpdatedEvent().subscribe(self.on_order_update_3) mf.start()
def testFirstLast(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) self._count = 0 mf.subscribe(self.on_bars_basic) mf.start(first=datetime.datetime(2012, 7, 1), last=datetime.datetime(2012, 7, 31)) self.assertEqual(self._count, 22) self.assertEqual(mf.get_last_close('AC'), 2.565)
def testBasic(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) self.assertEqual(f1, mf.get_feed('AC')) self._count = 0 mf.subscribe(self.on_bars_basic) mf.start() self.assertEqual(self._count, 257)
def testShortEntry(self): self._placed_markorder = False mf = MultiFeed() mf.register_feed(Feed(InstrumentDb.Instance().get('CT'))) self._broker = BacktestingFuturesBroker(1000000, mf) mf.subscribe(self.on_bars_3) self._broker.getOrderUpdatedEvent().subscribe(self.on_order_update_3) mf.start() self.assertAlmostEqual(self._broker.getCash(), 1119750.0, places=2) self.assertEqual(self._broker.calc_margin(), 800000.0)
def testMarketOrder(self): self._placed_markorder = False mf = MultiFeed() mf.register_feed(Feed(InstrumentDb.Instance().get('AC'))) self._broker = BacktestingFuturesBroker(1000000, mf) mf.subscribe(self.on_bars_1) self._broker.getOrderUpdatedEvent().subscribe(self.on_order_update_1) mf.start() self.assertEqual(self._broker.getCash(), 1000015.0) self.assertEqual(self._broker.calc_margin(), 160000.0)
def testMarketOrderMarginCall(self): self._placed_markorder = False mf = MultiFeed() mf.register_feed(Feed(InstrumentDb.Instance().get('CT'))) self._broker = BacktestingFuturesBroker(1000000, mf) mf.subscribe(self.on_bars_2) self._broker.getOrderUpdatedEvent().subscribe(self.on_order_update_2) with self.assertRaisesRegexp(Exception, "Margin Call"): mf.start() self.assertAlmostEqual(self._broker.getCash(), 214000.0, places=2) self.assertEqual(self._broker.calc_margin(), 800000.0)
def testFirstOption(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) self._count = 0 mf.subscribe(self.on_bars_basic) mf.start(first=datetime.datetime(2012, 7, 1)) self.assertEqual(self._count, 143)
def testFirstLast(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) self._count = 0 mf.subscribe(self.on_bars_basic) mf.start(first=datetime.datetime(2012,7,1),last=datetime.datetime(2012,7,31)) self.assertEqual(self._count, 22) self.assertEqual(mf.get_last_close('AC'), 2.565)
def testBasic(self): mf = MultiFeed() mf.register_feed(self._feed) strat = MyStrategy(mf) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() self.assertAlmostEqual(strat.getResult(),1007071.67,places=2) self.assertEqual(tradesAnalyzer.getCount(),1) self.assertEqual(tradesAnalyzer.getProfitableCount(),1) self.assertEqual(tradesAnalyzer.getUnprofitableCount(),0)
def testBasic(self): mf = MultiFeed() mf.register_feed(self._feed) strat = MyStrategy(mf) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() strat.exitPositions() self.assertAlmostEqual(strat.getResult(), 996193.19, places=2) self.assertEqual(tradesAnalyzer.getCount(), 2) self.assertEqual(tradesAnalyzer.getProfitableCount(), 0) self.assertEqual(tradesAnalyzer.getUnprofitableCount(), 2) tlog = '%s/trade.log' % os.path.dirname(__file__) tradesAnalyzer.writeTradeLog(tlog) self.assertTrue( test_util.file_compare( '%s/trade1.reflog' % os.path.dirname(__file__), tlog)) os.remove(tlog)
def testFirstOption(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) self._count = 0 mf.subscribe(self.on_bars_basic) mf.start(first=datetime.datetime(2012,7,1)) self.assertEqual(self._count, 143)
def __init__(self, sectorMap, modelType=None, cash = 1000000, tradeStart=None, compounding = True, positionsFile=None, equityFile=None, returnsFile=None, summaryFile=None, parms = None ): self._runGroups = {} self._startingCash = cash self._db = InstrumentDb.Instance() self._feed = MultiFeed() self._broker = BacktestingFuturesBroker(cash, self._feed, commission=FuturesCommission(2.50)) show = True for sec in sectorMap: for sym in sectorMap[sec]: self._feed.register_feed(Feed(self._db.get(sym))) # if desired can instantiate a strategy per symbol - may actually # want to think about this as a default behavior. The only thing # impacted is reporting of sector results # strategySymbols = { sym } # rgkey = '%s-%s' % (sec, sym) strategySymbols = sectorMap[sec] rgkey = sec if not modelType: modelType = 'breakout' strategy = StrategyFactory.create(modelType, self._feed, symbols=strategySymbols, broker=self._broker, cash=cash, compounding=compounding, parms = parms) self._runGroups[rgkey] = InstrumentedStrategy(strategy) if show: show = False strategy.showParms() self._trading = False self._posfile = self.__openFileorNull(positionsFile) self._equityfile = self.__openFileorNull(equityFile) self._returnsfile = self.__openFileorNull(returnsFile) self._summaryfile = self.__openFileorNull(summaryFile) self._dd = DrawDown() self._dd.attached(self) self._positionAlerts = []
def testBasic(self): mf = MultiFeed() mf.register_feed(self._feed) strat = MyStrategy(mf) # Attach a few analyzers to the strategy before executing it. retAnalyzer = returns.Returns() strat.attachAnalyzer(retAnalyzer) sharpeRatioAnalyzer = sharpe.SharpeRatio() strat.attachAnalyzer(sharpeRatioAnalyzer) drawDownAnalyzer = drawdown.DrawDown() strat.attachAnalyzer(drawDownAnalyzer) tradesAnalyzer = trades.Trades() strat.attachAnalyzer(tradesAnalyzer) mf.start() self.assertAlmostEqual(strat.getResult(), 1007071.67, places=2) self.assertEqual(tradesAnalyzer.getCount(), 1) self.assertEqual(tradesAnalyzer.getProfitableCount(), 1) self.assertEqual(tradesAnalyzer.getUnprofitableCount(), 0)
class Controller(object): def __init__(self, sectorMap, modelType=None, cash=1000000, tradeStart=None, compounding=True, positionsFile=None, equityFile=None, returnsFile=None, summaryFile=None, parms=None): self._runGroups = {} self._startingCash = cash self._db = InstrumentDb.Instance() self._feed = MultiFeed() self._broker = BacktestingFuturesBroker( cash, self._feed, commission=FuturesCommission(2.50)) show = True for sec in sectorMap: for sym in sectorMap[sec]: self._feed.register_feed(Feed(self._db.get(sym))) # if desired can instantiate a strategy per symbol - may actually # want to think about this as a default behavior. The only thing # impacted is reporting of sector results # strategySymbols = { sym } # rgkey = '%s-%s' % (sec, sym) strategySymbols = sectorMap[sec] rgkey = sec if not modelType: modelType = 'breakout' strategy = StrategyFactory.create(modelType, self._feed, symbols=strategySymbols, broker=self._broker, cash=cash, compounding=compounding, parms=parms) self._runGroups[rgkey] = InstrumentedStrategy(strategy) if show: show = False strategy.showParms() self._trading = False self._posfile = self.__openFileorNull(positionsFile) self._equityfile = self.__openFileorNull(equityFile) self._returnsfile = self.__openFileorNull(returnsFile) self._summaryfile = self.__openFileorNull(summaryFile) self._dd = DrawDown() self._dd.attached(self) self._positionAlerts = [] def __openFileorNull(self, file_): if file_: return open(file_, 'w') else: return open('/dev/null', 'w') def getBroker(self): return self._broker def getEquity(self): return self._broker.getCash() def drawdown(self): return self._dd def net_return(self): return self._totalProfit / self._startingCash def get_net_profit(self): return self._totalProfit def get_trade_profit(self): return self._tradeProfit def get_position_alerts(self): return self._positionAlerts def run(self, feedStart, tradeStart, tradeEnd): # sanity check our parms assert (feedStart <= tradeStart) assert (tradeStart <= tradeEnd) # set each feed cursor to our feedStart for sec in self._runGroups: self._runGroups[sec].feed().set_cursor(feedStart) # emit bars one datetime at a time nextDateTime = self._feed.get_next_bars_date() while nextDateTime != None and nextDateTime < tradeEnd: if not self._trading: if nextDateTime >= tradeStart: self._trading = True self._handle_trade_start(nextDateTime) #print 'emitting bars for date %s' % nextDateTime lastEmitDate = nextDateTime self._feed.start(last=nextDateTime) nextDateTime = self._feed.get_next_bars_date() if self._trading and nextDateTime != None: self._print_sector_equity(lastEmitDate) self._dd.beforeOnBars(self) self._handle_trade_end(lastEmitDate) def dumpFeed(self, symbol): feed = self._feed.get_feed(symbol) of = open('%s.csv' % symbol, 'w') feed.write_csv(of) of.close() def writeAllTrades(self, filename): # get one list with all trades alltrades = [] for sec in self._runGroups: alltrades.extend( self._runGroups[sec].trades_analyzer().trade_records()) # now we want to sort this by trade entry alltrades = sorted(alltrades, key=lambda x: x.getEntryDate()) # want to sum total trade profit for verification purposes self._tradeProfit = 0.0 file_ = open(filename, 'w') wins = 0 winamt = 0.0 winmargin = 0.0 losemargin = 0.0 # write the header row file_.write( 'description,symbol,units,entryDate,entryPrice,exitDate,exitPrice,commissions,profitLoss\n' ) for t in alltrades: self._tradeProfit += t.getNetProfit(0) profit = t.getNetProfit(0) file_.write('%s,%s,%d,%s,%f,%s,%f,%0.2f,%0.2f\n' % (self._db.get(t.getSymbol()).description(), t.getSymbol(), t.getTradeSize(), t.getEntryDate(), t.getEntryPrice(), t.getExitDate(), t.getExitPrice(), t.getCommissions(), profit)) if profit > 0.0: wins += 1 winamt += profit winmargin += t.getInitialMargin() else: losemargin += t.getInitialMargin() file_.close() logger.info('Total Trades : %d' % len(alltrades)) logger.info('Total Net Profit: $%0.2f' % self._tradeProfit) tot_avg = (self._tradeProfit / len(alltrades)) if len(alltrades) else 0.0 logger.info('Total Avg Profit: $%0.2f' % tot_avg) winrate = wins * 100.0 / len(alltrades) if len(alltrades) else 0.0 logger.info('Winning Trades : %d (%0.1f%%)' % (wins, winrate)) winavg = winamt / wins if wins else 0.0 winmarg = (winamt * 100.0 / winmargin) if winmargin else 0.0 logger.info('Average Winner : $%0.2f (%0.1f%%)' % (winavg, winmarg)) numloss = len(alltrades) - wins loserate = numloss * 100.0 / len(alltrades) if len(alltrades) else 0.0 logger.info('Losing Trades : %d (%0.1f%%)' % (numloss, loserate)) loseavg = (self._tradeProfit - winamt) / numloss if numloss else 0.0 losemarg = (self._tradeProfit - winamt) * 100.0 / losemargin if losemargin else 0.0 logger.info('Average Loser : $%0.2f (%0.1f%%)' % (loseavg, losemarg)) def writeTSSBTrades(self, filebase): # get one list with all trades alltrades = [] for sec in self._runGroups: alltrades.extend( self._runGroups[sec].trades_analyzer().trade_records()) # now we want to sort this by trade entry alltrades = sorted(alltrades, key=lambda x: x.getEntryDate()) # want to sum total trade profit for verification purposes sf = open('%s_short.csv' % filebase, 'w') lf = open('%s_long.csv' % filebase, 'w') # write the header rows sf.write('Date,Market,Contracts,Points,SCALEDPROFIT,REALPROFIT\n') lf.write('Date,Market,Contracts,Points,SCALEDPROFIT,REALPROFIT\n') # write out all the trades that actually occurred for t in alltrades: fp = sf if t.getTradeSize() < 0 else lf fp.write('%s,%s,%d,%0.3f,%0.5f,%0.5f\n' % (t.getEntryDate().strftime("%Y%m%d"), t.getSymbol(), t.getTradeSize(), t.getExitPrice() - t.getEntryPrice(), t.getNetProfit(0) / t.getInitialMargin(), t.getNetProfit(0) / self._startingCash * 100.0)) sf.close() lf.close() def writePositionAlerts(self, filename): wf = open(filename, "w") wf.write('EntryDate,Symbol,Quantity,Action,ImpliedRisk,StopPrice\n') alerts = sorted(self._positionAlerts, key=lambda x: x.datetime) for alert in alerts: wf.write('%s\n' % alert) wf.close() def _handle_trade_start(self, datetime): self._broker.setCash(self._startingCash) for sec in self._runGroups: self._reset_broker(self._runGroups[sec]) self._print_sector_positions(datetime) def _handle_trade_end(self, datetime): # by this point we have consumed the entire feed so we need to make # sure that any existing exit orders are closed. In a corner case # what can happen is a new trade that will be filled on close in the # executeSessionClose below. This trade could then hit a stop-loss # from an earlier price in the day for sec in self._runGroups: self._runGroups[sec].strategy().cancelExitOrders() # first we need new positions to be taken if executed after # market close on last bar self.getBroker().executeSessionClose() # want to report our positions before exiting them self._print_sector_positions(datetime) # now we want to grab position information self._positionAlerts = [] for sec in self._runGroups: for pos in self._runGroups[sec].strategy().getPositions( ).itervalues(): # create a 7-tuple: (date, symbol, desc, quantity, action, risk, stop) entry = pos.getEntryOrder() self._positionAlerts.append( Alert(entry.getExecutionInfo().getDateTime(), entry.getInstrument(), self._db.get(entry.getInstrument()).description(), entry.getQuantity(), Order.Action.action_strs[entry.getAction()], pos.getImpliedRisk(), self._runGroups[sec].strategy().getCurrentExit(pos))) # exit all positions - this needs to happen before we report final equity and returns for sec in self._runGroups: self._runGroups[sec].strategy().exitPositions() # we must call executeSessionClose once after the call to exitPositions for # each of our rungroup strategy instances. They executes the market orders # to close open positions self.getBroker().executeSessionClose() self._dd.beforeOnBars(self) self._print_sector_equity(datetime) self._print_returns_summary(datetime, final=True) self._print_sector_returns() self._posfile.close() self._equityfile.close() self._returnsfile.close() self._summaryfile.close() def _reset_broker(self, istrat): '''Resets the equity in the broker to our starting cash position.''' # this is kind of a kludge but it's nice to be able to sum the trade profits # and match the equity amounts (from startingCash). We need to adjust the # starting cash down by the commission required to enter into our initial positions # since they are counted against us in the trade entry_commission = istrat.trades_analyzer().reset( istrat.strategy().getBroker().get_last_mark_to_market()) self._broker.setCash(self._broker.getCash() - entry_commission) def _print_sector_returns(self): try: self._returnheader except: # means we need to print headers self._returnheader = True str_ = 'Name,Long %,Short %,Total %' self._returnsfile.write('%s\n' % str_) total_long_profit = 0.0 total_profit = 0.0 str_ = '' for sec in sorted(self._runGroups): profit = 0.0 long_profit = 0.0 for trade in self._runGroups[sec].trades_analyzer().trade_records( ): profit += trade.getNetProfit(0) if trade.getTradeSize() > 0: long_profit += trade.getNetProfit(0) short_profit = profit - long_profit str_ = '%s,%0.1f,%0.1f,%0.1f' % \ (sec, long_profit / self._startingCash * 100.0, short_profit / self._startingCash * 100.0, profit / self._startingCash * 100.0) total_long_profit += long_profit total_profit += profit self._returnsfile.write('%s\n' % str_) total_short_profit = total_profit - total_long_profit str_ = 'total,%0.1f,%0.1f,%0.1f' % \ (total_long_profit / self._startingCash * 100.0, total_short_profit / self._startingCash * 100.0, total_profit / self._startingCash * 100.0) self._returnsfile.write('%s\n' % str_) self._totalProfit = total_profit def _print_returns_summary(self, datetime, final=False): try: self._summaryHeader except: # means we need to print headers self._summaryHeader = True self._sumCurMonth = None self._sumCurYear = None str_ = 'Year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,Full Year' self._summaryfile.write('%s\n' % str_) if self._sumCurYear != datetime.year or final: if self._sumCurYear or final: # this means the year just rolled on us and we have to output a new row self._sumMonthData[self._sumCurMonth - 1] = (self.getEquity() - self._sumMonthStart ) / self._sumMonthStart * 100.0 str_ = '%s,' % self._sumCurYear for r in self._sumMonthData: str_ = str_ + '%0.1f,' % r str_ = str_ + '%0.1f' % ( ((self.getEquity() - self._sumYearStart) / self._sumYearStart) * 100.0) self._summaryfile.write('%s\n' % str_) # now reset and get ready for this year self._sumCurYear = datetime.year self._sumCurMonth = datetime.month self._sumMonthData = numpy.zeros(12) self._sumYearStart = self.getEquity() self._sumMonthStart = self.getEquity() elif self._sumCurMonth != datetime.month: # the month rolled on us so put a new entry in self._sumMonthData[self._sumCurMonth - 1] = (( (self.getEquity() - self._sumMonthStart) / self._sumMonthStart) * 100.0) self._sumMonthStart = self.getEquity() self._sumCurMonth = datetime.month else: # nothing to do - just a mid-month data point pass def _print_sector_equity(self, datetime): self._print_returns_summary(datetime) try: self._equityheader except: # means we need to print headers self._equityheader = True str_ = 'Datetime,' for sec in sorted(self._runGroups): str_ = str_ + '%s-Equity,%s-Margin,' % (sec, sec) str_ = str_ + 'Total-Equity,Total-Margin' self._equityfile.write('%s\n' % str_) # going to build a row of text for output to the equity report str_ = '%s,' % datetime total_equity = 0.0 total_margin = 0.0 for sec in sorted(self._runGroups): equity = self._runGroups[sec].getEquity() margin = self._runGroups[sec].calc_margin() str_ = str_ + '%0.2f,%0.2f,' % (equity, margin) total_equity += equity total_margin += margin str_ = str_ + '%0.2f,%0.2f' % (self.getEquity() - self._startingCash, total_margin) self._equityfile.write('%s\n' % str_) def _print_sector_positions(self, datetime): try: self._posheader except: # means we need to print headers self._posheader = True str_ = 'Datetime,' for sec in sorted(self._runGroups): str_ = str_ + '%s-Long,%s-Short,' % (sec, sec) str_ = str_ + 'Total-Long,Total-Short' self._posfile.write('%s\n' % str_) # going to build a row of text for output to the positions report str_ = '%s,' % datetime total_longs = 0 total_shorts = 0 for sec in sorted(self._runGroups): longs = 0 shorts = 0 sec_positions = self._runGroups[sec].strategy().getPositions() for sym in sec_positions: if sec_positions[sym].isLong(): longs = longs + 1 else: shorts = shorts + 1 str_ = str_ + '%s,%s,' % (longs, shorts) total_longs += longs total_shorts += shorts # now the totals str_ = str_ + '%s,%s' % (total_longs, total_shorts) self._posfile.write('%s\n' % str_)
class Controller(object): def __init__(self, sectorMap, modelType=None, cash = 1000000, tradeStart=None, compounding = True, positionsFile=None, equityFile=None, returnsFile=None, summaryFile=None, parms = None ): self._runGroups = {} self._startingCash = cash self._db = InstrumentDb.Instance() self._feed = MultiFeed() self._broker = BacktestingFuturesBroker(cash, self._feed, commission=FuturesCommission(2.50)) show = True for sec in sectorMap: for sym in sectorMap[sec]: self._feed.register_feed(Feed(self._db.get(sym))) # if desired can instantiate a strategy per symbol - may actually # want to think about this as a default behavior. The only thing # impacted is reporting of sector results # strategySymbols = { sym } # rgkey = '%s-%s' % (sec, sym) strategySymbols = sectorMap[sec] rgkey = sec if not modelType: modelType = 'breakout' strategy = StrategyFactory.create(modelType, self._feed, symbols=strategySymbols, broker=self._broker, cash=cash, compounding=compounding, parms = parms) self._runGroups[rgkey] = InstrumentedStrategy(strategy) if show: show = False strategy.showParms() self._trading = False self._posfile = self.__openFileorNull(positionsFile) self._equityfile = self.__openFileorNull(equityFile) self._returnsfile = self.__openFileorNull(returnsFile) self._summaryfile = self.__openFileorNull(summaryFile) self._dd = DrawDown() self._dd.attached(self) self._positionAlerts = [] def __openFileorNull(self, file_): if file_: return open(file_,'w') else: return open('/dev/null','w') def getBroker(self): return self._broker def getEquity(self): return self._broker.getCash() def drawdown(self): return self._dd def net_return(self): return self._totalProfit / self._startingCash def get_net_profit(self): return self._totalProfit def get_trade_profit(self): return self._tradeProfit def get_position_alerts(self): return self._positionAlerts def run(self, feedStart, tradeStart, tradeEnd): # sanity check our parms assert(feedStart <= tradeStart) assert(tradeStart <= tradeEnd) # set each feed cursor to our feedStart for sec in self._runGroups: self._runGroups[sec].feed().set_cursor(feedStart) # emit bars one datetime at a time nextDateTime = self._feed.get_next_bars_date() while nextDateTime != None and nextDateTime < tradeEnd: if not self._trading: if nextDateTime >= tradeStart: self._trading = True self._handle_trade_start(nextDateTime) #print 'emitting bars for date %s' % nextDateTime lastEmitDate = nextDateTime self._feed.start(last=nextDateTime) nextDateTime = self._feed.get_next_bars_date() if self._trading and nextDateTime != None: self._print_sector_equity(lastEmitDate) self._dd.beforeOnBars(self) self._handle_trade_end(lastEmitDate) def dumpFeed(self, symbol): feed = self._feed.get_feed(symbol) of = open('%s.csv' % symbol,'w') feed.write_csv(of) of.close() def writeAllTrades(self, filename): # get one list with all trades alltrades = [] for sec in self._runGroups: alltrades.extend(self._runGroups[sec].trades_analyzer().trade_records()) # now we want to sort this by trade entry alltrades = sorted(alltrades, key=lambda x: x.getEntryDate()) # want to sum total trade profit for verification purposes self._tradeProfit = 0.0 file_ = open(filename,'w') wins = 0 winamt = 0.0 winmargin = 0.0 losemargin = 0.0 # write the header row file_.write('description,symbol,units,entryDate,entryPrice,exitDate,exitPrice,commissions,profitLoss\n') for t in alltrades: self._tradeProfit += t.getNetProfit(0) profit = t.getNetProfit(0) file_.write('%s,%s,%d,%s,%f,%s,%f,%0.2f,%0.2f\n' % (self._db.get(t.getSymbol()).description(), t.getSymbol(), t.getTradeSize(), t.getEntryDate(), t.getEntryPrice(), t.getExitDate(), t.getExitPrice(), t.getCommissions(), profit)) if profit > 0.0: wins += 1 winamt += profit winmargin += t.getInitialMargin() else: losemargin += t.getInitialMargin() file_.close() logger.info('Total Trades : %d' % len(alltrades)) logger.info('Total Net Profit: $%0.2f' % self._tradeProfit) tot_avg = (self._tradeProfit / len(alltrades)) if len(alltrades) else 0.0 logger.info('Total Avg Profit: $%0.2f' % tot_avg) winrate = wins * 100.0 / len(alltrades) if len(alltrades) else 0.0 logger.info('Winning Trades : %d (%0.1f%%)' % (wins, winrate)) winavg = winamt/ wins if wins else 0.0 winmarg = (winamt * 100.0/winmargin) if winmargin else 0.0 logger.info('Average Winner : $%0.2f (%0.1f%%)' % (winavg, winmarg)) numloss = len(alltrades) - wins loserate = numloss * 100.0/len(alltrades) if len(alltrades) else 0.0 logger.info('Losing Trades : %d (%0.1f%%)' % (numloss,loserate)) loseavg = (self._tradeProfit - winamt) / numloss if numloss else 0.0 losemarg = (self._tradeProfit - winamt) *100.0/losemargin if losemargin else 0.0 logger.info('Average Loser : $%0.2f (%0.1f%%)' % (loseavg,losemarg)) def writeTSSBTrades(self, filebase): # get one list with all trades alltrades = [] for sec in self._runGroups: alltrades.extend(self._runGroups[sec].trades_analyzer().trade_records()) # now we want to sort this by trade entry alltrades = sorted(alltrades, key=lambda x: x.getEntryDate()) # want to sum total trade profit for verification purposes sf = open('%s_short.csv' % filebase,'w') lf = open('%s_long.csv' % filebase,'w') # write the header rows sf.write('Date,Market,Contracts,Points,SCALEDPROFIT,REALPROFIT\n') lf.write('Date,Market,Contracts,Points,SCALEDPROFIT,REALPROFIT\n') # write out all the trades that actually occurred for t in alltrades: fp = sf if t.getTradeSize() < 0 else lf fp.write('%s,%s,%d,%0.3f,%0.5f,%0.5f\n' % (t.getEntryDate().strftime("%Y%m%d"), t.getSymbol(), t.getTradeSize(), t.getExitPrice() - t.getEntryPrice(), t.getNetProfit(0) / t.getInitialMargin(), t.getNetProfit(0) / self._startingCash * 100.0)) sf.close() lf.close() def writePositionAlerts(self, filename): wf = open(filename,"w") wf.write('EntryDate,Symbol,Quantity,Action,ImpliedRisk,StopPrice\n') alerts = sorted(self._positionAlerts, key=lambda x: x.datetime) for alert in alerts: wf.write('%s\n' % alert) wf.close() def _handle_trade_start(self, datetime): self._broker.setCash(self._startingCash) for sec in self._runGroups: self._reset_broker(self._runGroups[sec]) self._print_sector_positions(datetime) def _handle_trade_end(self, datetime): # by this point we have consumed the entire feed so we need to make # sure that any existing exit orders are closed. In a corner case # what can happen is a new trade that will be filled on close in the # executeSessionClose below. This trade could then hit a stop-loss # from an earlier price in the day for sec in self._runGroups: self._runGroups[sec].strategy().cancelExitOrders() # first we need new positions to be taken if executed after # market close on last bar self.getBroker().executeSessionClose() # want to report our positions before exiting them self._print_sector_positions(datetime) # now we want to grab position information self._positionAlerts = [] for sec in self._runGroups: for pos in self._runGroups[sec].strategy().getPositions().itervalues(): # create a 7-tuple: (date, symbol, desc, quantity, action, risk, stop) entry = pos.getEntryOrder() self._positionAlerts.append( Alert( entry.getExecutionInfo().getDateTime(), entry.getInstrument(), self._db.get(entry.getInstrument()).description(), entry.getQuantity(), Order.Action.action_strs[entry.getAction()], pos.getImpliedRisk(), self._runGroups[sec].strategy().getCurrentExit(pos) ) ) # exit all positions - this needs to happen before we report final equity and returns for sec in self._runGroups: self._runGroups[sec].strategy().exitPositions() # we must call executeSessionClose once after the call to exitPositions for # each of our rungroup strategy instances. They executes the market orders # to close open positions self.getBroker().executeSessionClose() self._dd.beforeOnBars(self) self._print_sector_equity(datetime) self._print_returns_summary(datetime, final=True) self._print_sector_returns() self._posfile.close() self._equityfile.close() self._returnsfile.close() self._summaryfile.close() def _reset_broker(self, istrat): '''Resets the equity in the broker to our starting cash position.''' # this is kind of a kludge but it's nice to be able to sum the trade profits # and match the equity amounts (from startingCash). We need to adjust the # starting cash down by the commission required to enter into our initial positions # since they are counted against us in the trade entry_commission = istrat.trades_analyzer().reset(istrat.strategy().getBroker().get_last_mark_to_market()) self._broker.setCash(self._broker.getCash() - entry_commission) def _print_sector_returns(self): try: self._returnheader except: # means we need to print headers self._returnheader = True str_ = 'Name,Long %,Short %,Total %' self._returnsfile.write('%s\n' % str_) total_long_profit = 0.0 total_profit = 0.0 str_ = '' for sec in sorted(self._runGroups): profit = 0.0 long_profit = 0.0 for trade in self._runGroups[sec].trades_analyzer().trade_records(): profit += trade.getNetProfit(0) if trade.getTradeSize() > 0: long_profit += trade.getNetProfit(0) short_profit = profit - long_profit str_ = '%s,%0.1f,%0.1f,%0.1f' % \ (sec, long_profit / self._startingCash * 100.0, short_profit / self._startingCash * 100.0, profit / self._startingCash * 100.0) total_long_profit += long_profit total_profit += profit self._returnsfile.write('%s\n' % str_) total_short_profit = total_profit - total_long_profit str_ = 'total,%0.1f,%0.1f,%0.1f' % \ (total_long_profit / self._startingCash * 100.0, total_short_profit / self._startingCash * 100.0, total_profit / self._startingCash * 100.0) self._returnsfile.write('%s\n' % str_) self._totalProfit = total_profit def _print_returns_summary(self, datetime, final=False): try: self._summaryHeader except: # means we need to print headers self._summaryHeader = True self._sumCurMonth = None self._sumCurYear = None str_ = 'Year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,Full Year' self._summaryfile.write('%s\n' % str_) if self._sumCurYear != datetime.year or final: if self._sumCurYear or final: # this means the year just rolled on us and we have to output a new row self._sumMonthData[self._sumCurMonth-1] = (self.getEquity() - self._sumMonthStart) / self._sumMonthStart * 100.0 str_ = '%s,' % self._sumCurYear for r in self._sumMonthData: str_ = str_ + '%0.1f,' % r str_ = str_ + '%0.1f' % (((self.getEquity() - self._sumYearStart) / self._sumYearStart) * 100.0) self._summaryfile.write('%s\n' % str_) # now reset and get ready for this year self._sumCurYear = datetime.year self._sumCurMonth = datetime.month self._sumMonthData = numpy.zeros(12) self._sumYearStart = self.getEquity() self._sumMonthStart = self.getEquity() elif self._sumCurMonth != datetime.month: # the month rolled on us so put a new entry in self._sumMonthData[self._sumCurMonth-1] = (((self.getEquity() - self._sumMonthStart) / self._sumMonthStart)*100.0) self._sumMonthStart = self.getEquity() self._sumCurMonth = datetime.month else: # nothing to do - just a mid-month data point pass def _print_sector_equity(self, datetime): self._print_returns_summary(datetime) try: self._equityheader except: # means we need to print headers self._equityheader = True str_ = 'Datetime,' for sec in sorted(self._runGroups): str_ = str_ + '%s-Equity,%s-Margin,' % (sec,sec) str_ = str_ + 'Total-Equity,Total-Margin' self._equityfile.write('%s\n' % str_) # going to build a row of text for output to the equity report str_ = '%s,' % datetime total_equity = 0.0 total_margin = 0.0 for sec in sorted(self._runGroups): equity = self._runGroups[sec].getEquity() margin = self._runGroups[sec].calc_margin() str_ = str_ + '%0.2f,%0.2f,' % (equity,margin) total_equity += equity total_margin += margin str_ = str_ + '%0.2f,%0.2f' % (self.getEquity()-self._startingCash,total_margin) self._equityfile.write('%s\n' % str_) def _print_sector_positions(self, datetime): try: self._posheader except: # means we need to print headers self._posheader = True str_ = 'Datetime,' for sec in sorted(self._runGroups): str_ = str_ + '%s-Long,%s-Short,' % (sec,sec) str_ = str_ + 'Total-Long,Total-Short' self._posfile.write('%s\n' % str_) # going to build a row of text for output to the positions report str_ = '%s,' % datetime total_longs = 0 total_shorts = 0 for sec in sorted(self._runGroups): longs = 0 shorts = 0 sec_positions = self._runGroups[sec].strategy().getPositions() for sym in sec_positions: if sec_positions[sym].isLong(): longs = longs + 1 else: shorts = shorts + 1 str_ = str_ + '%s,%s,' % (longs, shorts) total_longs += longs total_shorts += shorts # now the totals str_ = str_ + '%s,%s' % (total_longs, total_shorts) self._posfile.write('%s\n' % str_)
def testFirstLastBoundary(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) mf.subscribe(self.on_bars_basic) self._count = 0 mf.start(first=datetime.datetime(2012,7,2),last=datetime.datetime(2012,7,2)) self.assertEqual(self._count, 1) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012,7,3)) self._count = 0 mf.start(first=datetime.datetime(2012,7,1),last=datetime.datetime(2012,7,2)) self.assertEqual(self._count, 1) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012,7,3)) # 7/1 is not a trading day so first bar is actually 7/2 which is after last self._count = 0 mf.start(first=datetime.datetime(2012,7,1),last=datetime.datetime(2012,7,1)) self.assertEqual(self._count, 0) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012,7,2))
def testFirstLastBoundary(self): f1 = Feed(self._inst1) f2 = Feed(self._inst2) mf = MultiFeed() mf.register_feed(f1) mf.register_feed(f2) mf.subscribe(self.on_bars_basic) self._count = 0 mf.start(first=datetime.datetime(2012, 7, 2), last=datetime.datetime(2012, 7, 2)) self.assertEqual(self._count, 1) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012, 7, 3)) self._count = 0 mf.start(first=datetime.datetime(2012, 7, 1), last=datetime.datetime(2012, 7, 2)) self.assertEqual(self._count, 1) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012, 7, 3)) # 7/1 is not a trading day so first bar is actually 7/2 which is after last self._count = 0 mf.start(first=datetime.datetime(2012, 7, 1), last=datetime.datetime(2012, 7, 1)) self.assertEqual(self._count, 0) self.assertEqual(mf.get_next_bars_date(), datetime.datetime(2012, 7, 2))