diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index e90d4e0d..63129b31 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -40,9 +40,12 @@ Performance Tracking | | through all the events delivered to this tracker. | | | For details look at the comments for | | | :py:meth:`zipline.finance.risk.RiskMetrics.to_dict`| + +-----------------+----------------------------------------------------+ + | exceeded_max_ | True if the simulation was stopped because single | + | loss | day losses exceeded the max_drawdown stipulated in | + | | trading_environment. | +-----------------+----------------------------------------------------+ - Position Tracking ================= @@ -214,7 +217,7 @@ class PerformanceTracker(): 'capital_base' : self.capital_base, 'cumulative_perf' : self.cumulative_performance.to_dict(), 'daily_perf' : self.todays_performance.to_dict(), - 'cumulative_risk_metrics' : self.cumulative_risk_metrics.to_dict(), + 'cumulative_risk_metrics' : self.cumulative_risk_metrics.to_dict() } def log_order(self, order): @@ -264,21 +267,37 @@ class PerformanceTracker(): # calculate progress of test self.progress = self.day_count / self.total_days + if self.trading_environment.max_drawdown: + max_dd = -1 * self.trading_environment.max_drawdown + if self.todays_performance.returns < max_dd: + qutil.LOGGER.info("Exceeded max drawdown.") + # mark the perf period with max loss flag, + # so it shows up in the update, but don't end the test + # here. Let the update go out before stopping + self.exceeded_max_loss = True + # Output results if self.result_stream: msg = zp.PERF_FRAME(self.to_dict()) self.result_stream.send(msg) - # check the day's returns versus the max drawdown - max_dd = -1 * self.trading_environment.max_drawdown - if self.todays_performance.returns < max_dd: - qutil.LOGGER.info("Exceeded max drawdown.") - # TODO: any other information we need to relay on the - # result socket? - self.exceeded_max_loss = True + if self.exceeded_max_loss: + # now that we've sent the day's update, kill this test self.handle_simulation_end(skip_close=True) return + # check the day's returns versus the max drawdown + # max_drawdown is optional: + if self.trading_environment.max_drawdown: + max_dd = -1 * self.trading_environment.max_drawdown + if self.todays_performance.returns < max_dd: + qutil.LOGGER.info("Exceeded max drawdown.") + # TODO: any other information we need to relay on the + # result socket? + self.exceeded_max_loss = True + self.handle_simulation_end(skip_close=True) + return + #move the market day markers forward self.market_open = self.market_open + self.calendar_day @@ -317,13 +336,15 @@ class PerformanceTracker(): self.risk_report = risk.RiskReport( self.returns, - self.trading_environment + self.trading_environment, + exceeded_max_loss = self.exceeded_max_loss ) - + if self.result_stream: qutil.LOGGER.info("about to stream the risk report...") - report = self.risk_report.to_dict() - msg = zp.RISK_FRAME(report) + risk_dict = self.risk_report.to_dict() + + msg = zp.RISK_FRAME(risk_dict) self.result_stream.send(msg) # this signals that the simulation is complete. self.result_stream.send("DONE") diff --git a/zipline/finance/risk.py b/zipline/finance/risk.py index 98f7b7e7..248e75a7 100644 --- a/zipline/finance/risk.py +++ b/zipline/finance/risk.py @@ -315,7 +315,11 @@ class RiskMetrics(): class RiskReport(): - def __init__(self, algorithm_returns, trading_environment): + def __init__( + self, + algorithm_returns, + trading_environment, + exceeded_max_loss=False): """ algorithm_returns needs to be a list of daily_return objects sorted in date ascending order @@ -323,6 +327,7 @@ class RiskReport(): self.algorithm_returns = algorithm_returns self.trading_environment = trading_environment + self.exceeded_max_loss = exceeded_max_loss if len(self.algorithm_returns) == 0: start_date = self.trading_environment.period_start @@ -352,10 +357,11 @@ class RiskReport(): provided for each period. """ return { - 'one_month' : [x.to_dict() for x in self.month_periods], - 'three_month' : [x.to_dict() for x in self.three_month_periods], - 'six_month' : [x.to_dict() for x in self.six_month_periods], - 'twelve_month' : [x.to_dict() for x in self.year_periods] + 'one_month' : [x.to_dict() for x in self.month_periods], + 'three_month' : [x.to_dict() for x in self.three_month_periods], + 'six_month' : [x.to_dict() for x in self.six_month_periods], + 'twelve_month' : [x.to_dict() for x in self.year_periods], + 'exceeded_max_loss' : self.exceeded_max_loss } def periodsInRange(self, months_per, start, end): diff --git a/zipline/protocol.py b/zipline/protocol.py index 0736d79c..c3291556 100644 --- a/zipline/protocol.py +++ b/zipline/protocol.py @@ -663,6 +663,7 @@ def convert_transactions(transactions): for txn in transactions: txn['date'] = EPOCH(txn['dt']) del(txn['dt']) + del(txn['source_id']) results.append(txn) return results