Merge pull request #1337 from quantopian/margin_changes

Capital Changes Refactoring
This commit is contained in:
Andrew Liang
2016-07-25 10:54:34 -04:00
committed by GitHub
6 changed files with 152 additions and 65 deletions
+69 -1
View File
@@ -409,9 +409,13 @@ class TradingAlgorithm(object):
self.benchmark_sid = kwargs.pop('benchmark_sid', None)
# A dictionary of capital change values keyed by timestamp
# A dictionary of capital changes, keyed by timestamp, indicating the
# target/delta of the capital changes, along with values
self.capital_changes = kwargs.pop('capital_changes', {})
# A dictionary of the actual capital change deltas, keyed by timestamp
self.capital_change_deltas = {}
def init_engine(self, get_loader):
"""
Construct and store a PipelineEngine from loader.
@@ -786,6 +790,70 @@ class TradingAlgorithm(object):
return daily_stats
def calculate_capital_changes(self, dt, emission_rate, is_interday):
"""
If there is a capital change for a given dt, this means the the change
occurs before `handle_data` on the given dt. In the case of the
change being a target value, the change will be computed on the
portfolio value according to prices at the given dt
"""
try:
capital_change = self.capital_changes[dt]
except KeyError:
return
if emission_rate == 'daily':
# If we are running daily emission, prices won't
# necessarily be synced at the end of every minute, and we
# need the up-to-date prices for capital change
# calculations. We want to sync the prices as of the
# last market minute, and this is okay from a data portal
# perspective as we have technically not "advanced" to the
# current dt yet.
self.perf_tracker.position_tracker.sync_last_sale_prices(
self.trading_calendar.previous_minute(
dt
),
False,
self.data_portal
)
# Calculate performance before we sync prices price for the current dt
self.perf_tracker.cumulative_performance.calculate_performance()
self.perf_tracker.todays_performance.calculate_performance()
if capital_change['type'] == 'target':
# Get an updated portfolio value as of this dt, but do it in a way
# so that the performance is not recalculated. This is done so
# that `process_capital_change` can find the performance values
# for the end of the subperiod, which is the previous dt
self.perf_tracker.position_tracker.sync_last_sale_prices(
dt,
self._in_before_trading_start,
self.data_portal
)
portfolio_value = \
self.perf_tracker.position_tracker.stats().net_value + \
self.perf_tracker.cumulative_performance.ending_cash
capital_change_amount = capital_change['value'] - portfolio_value
log.info('Processing capital change to target %s at %s. Capital '
'change delta is %s' % (capital_change['value'], dt,
capital_change_amount))
elif capital_change['type'] == 'delta':
capital_change_amount = capital_change['value']
log.info('Processing capital change of delta %s at %s'
% (capital_change_amount, dt))
else:
log.error("Capital change %s does not indicate a valid type "
"('target' or 'delta')" % capital_change)
return
self.capital_change_deltas.update({dt: capital_change_amount})
self.perf_tracker.process_capital_change(capital_change_amount,
is_interday)
@api_method
def get_environment(self, field='platform'):
"""Query the execution environment.