REF: Remove asset_finder and multipliers from PositionTracker

This commit is contained in:
Jean Bredeche
2017-04-22 14:01:57 -04:00
parent e429664fa6
commit 9a0d9d868c
5 changed files with 103 additions and 175 deletions
+3 -4
View File
@@ -108,7 +108,7 @@ class BlotterTestCase(WithCreateBarData,
(StopLimitOrder(10, 20), 10, 20)])
def test_blotter_order_types(self, style_obj, expected_lmt, expected_stp):
blotter = Blotter('daily', self.asset_finder)
blotter = Blotter('daily')
blotter.order(self.asset_24, 100, style_obj)
result = blotter.open_orders[self.asset_24][0]
@@ -117,7 +117,7 @@ class BlotterTestCase(WithCreateBarData,
self.assertEqual(result.stop, expected_stp)
def test_cancel(self):
blotter = Blotter('daily', self.asset_finder)
blotter = Blotter('daily')
oid_1 = blotter.order(self.asset_24, 100, MarketOrder())
oid_2 = blotter.order(self.asset_24, 200, MarketOrder())
@@ -261,8 +261,7 @@ class BlotterTestCase(WithCreateBarData,
status indication. When a fill happens, the order should switch
status to OPEN/FILLED as necessary
"""
blotter = Blotter(self.sim_params.data_frequency,
self.asset_finder)
blotter = Blotter(self.sim_params.data_frequency)
# Nothing happens on held of a non-existent order
blotter.hold(56)
self.assertEqual(blotter.new_orders, [])
+72 -75
View File
@@ -276,6 +276,7 @@ class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
def init_class_fixtures(cls):
super(TestSplitPerformance, cls).init_class_fixtures()
cls.asset1 = cls.env.asset_finder.retrieve_asset(1)
cls.asset2 = cls.env.asset_finder.retrieve_asset(2)
def test_multiple_splits(self):
# if multiple positions all have splits at the same time, verify that
@@ -284,17 +285,14 @@ class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
self.trading_calendar,
self.env)
asset1 = self.asset_finder.retrieve_asset(1)
asset2 = self.asset_finder.retrieve_asset(2)
perf_tracker.position_tracker.positions[1] = \
Position(asset1, amount=10, cost_basis=10, last_sale_price=11)
Position(self.asset1, amount=10, cost_basis=10, last_sale_price=11)
perf_tracker.position_tracker.positions[2] = \
Position(asset2, amount=10, cost_basis=10, last_sale_price=11)
Position(self.asset2, amount=10, cost_basis=10, last_sale_price=11)
leftover_cash = perf_tracker.position_tracker.handle_splits(
[(1, 0.333), (2, 0.333)]
[(self.asset1, 0.333), (self.asset2, 0.333)]
)
# we used to have 10 shares that each cost us $10, total $100
@@ -329,7 +327,7 @@ class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
# set up a split with ratio 3 occurring at the start of the second
# day.
splits = {
events[1].dt: [(1, 3)]
events[1].dt: [(self.asset1, 3)]
}
results = calculate_results(self.sim_params,
@@ -1106,8 +1104,7 @@ class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendars,
txn1 = create_txn(self.asset1, trades_1[0].dt, 10.0, 100)
txn2 = create_txn(self.asset2, trades_1[0].dt, 10.0, -100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
pp.position_tracker = pt
@@ -1199,8 +1196,7 @@ class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendars,
self.sim_params,
{1: trades})
txn = create_txn(self.asset1, trades[1].dt, 10.0, 1000)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
pp.position_tracker = pt
@@ -1291,8 +1287,7 @@ class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendars,
self.sim_params,
{1: trades})
txn = create_txn(self.asset1, trades[1].dt, 10.0, 100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency,
period_open=self.sim_params.start_session,
@@ -1410,8 +1405,7 @@ single short-sale transaction"""
{1: trades})
txn = create_txn(self.asset1, trades[1].dt, 10.0, -100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(
1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
@@ -1530,8 +1524,7 @@ single short-sale transaction"""
)
# now run a performance period encompassing the entire trade sample.
ptTotal = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
ptTotal = perf.PositionTracker(self.sim_params.data_frequency)
ppTotal = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
ppTotal.position_tracker = pt
@@ -1638,8 +1631,7 @@ trade after cover"""
short_txn = create_txn(self.asset1, trades[1].dt, 10.0, -100)
cover_txn = create_txn(self.asset1, trades[6].dt, 7.0, 100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
pp.position_tracker = pt
@@ -1725,8 +1717,7 @@ shares in position"
self.sim_params,
{1: trades})
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(
1000.0,
self.env.asset_finder,
@@ -1792,8 +1783,7 @@ shares in position"
self.assertEqual(pp.pnl, -800, "this period goes from +400 to -400")
pt3 = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt3 = perf.PositionTracker(self.sim_params.data_frequency)
pp3 = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
pp3.position_tracker = pt3
@@ -1845,8 +1835,7 @@ shares in position"
transactions = factory.create_txn_history(*history_args)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency)
pp.position_tracker = pt
@@ -1885,8 +1874,7 @@ shares in position"
self.sim_params,
{1: trades})
txn = create_txn(self.asset1, trades[0].dt, 10.0, 100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency,
period_open=self.sim_params.start_session,
@@ -1929,8 +1917,7 @@ shares in position"
self.sim_params,
{1: trades})
txn = create_txn(self.asset1, trades[0].dt, 10.0, 100)
pt = perf.PositionTracker(self.env.asset_finder,
self.sim_params.data_frequency)
pt = perf.PositionTracker(self.sim_params.data_frequency)
pp = perf.PerformancePeriod(1000.0, self.env.asset_finder,
self.sim_params.data_frequency,
period_open=self.sim_params.start_session,
@@ -2003,8 +1990,7 @@ class TestPositionTracker(WithTradingEnvironment,
"""
sim_params = factory.create_simulation_parameters(num_days=4)
pt = perf.PositionTracker(self.env.asset_finder,
sim_params.data_frequency)
pt = perf.PositionTracker(sim_params.data_frequency)
pos_stats = pt.stats()
stats = [
@@ -2025,17 +2011,27 @@ class TestPositionTracker(WithTradingEnvironment,
self.assertNotIsInstance(val, (bool, np.bool_))
def test_position_values_and_exposures(self):
pt = perf.PositionTracker(self.env.asset_finder, None)
pt = perf.PositionTracker(None)
dt = pd.Timestamp("1984/03/06 3:00PM")
pos1 = perf.Position(self.EQUITY1, amount=np.float64(10.0),
last_sale_date=dt, last_sale_price=10)
pos2 = perf.Position(self.EQUITY2, amount=np.float64(-20.0),
last_sale_date=dt, last_sale_price=10)
pos3 = perf.Position(self.FUTURE3, amount=np.float64(30.0),
last_sale_date=dt, last_sale_price=10)
pos4 = perf.Position(self.FUTURE4, amount=np.float64(-40.0),
last_sale_date=dt, last_sale_price=10)
pt.update_positions({1: pos1, 2: pos2, 3: pos3, 4: pos4})
pt.update_position(
self.EQUITY1, amount=np.float64(10.0),
last_sale_date=dt, last_sale_price=10
)
pt.update_position(
self.EQUITY2, amount=np.float64(-20.0),
last_sale_date=dt, last_sale_price=10
)
pt.update_position(
self.FUTURE3, amount=np.float64(30.0),
last_sale_date=dt, last_sale_price=10
)
pt.update_position(
self.FUTURE4, amount=np.float64(-40.0),
last_sale_date=dt, last_sale_price=10
)
# Test long-only methods
pos_stats = pt.stats()
@@ -2092,24 +2088,35 @@ class TestPositionTracker(WithTradingEnvironment,
self.assertEqual(10.5, future_pos.cost_basis)
def test_update_positions(self):
pt = perf.PositionTracker(self.env.asset_finder, None)
pt = perf.PositionTracker(None)
dt = pd.Timestamp("2014/01/01 3:00PM")
pos1 = perf.Position(self.EQUITY1, amount=np.float64(10.0),
last_sale_date=dt, last_sale_price=10)
pos2 = perf.Position(self.EQUITY2, amount=np.float64(-20.0),
last_sale_date=dt, last_sale_price=10)
pos3 = perf.Position(self.FUTURE5, amount=np.float64(30.0),
last_sale_date=dt, last_sale_price=100)
# pos1 = perf.Position(self.EQUITY1, amount=np.float64(10.0),
# last_sale_date=dt, last_sale_price=10)
# pos2 = perf.Position(self.EQUITY2, amount=np.float64(-20.0),
# last_sale_date=dt, last_sale_price=10)
# pos3 = perf.Position(self.FUTURE5, amount=np.float64(30.0),
# last_sale_date=dt, last_sale_price=100)
# Call update_positions twice. When the second call is made,
# self.positions will already contain data. The order of this data
# needs to be preserved so that it is consistent with the order of the
# data stored in the multipliers OrderedDict()'s. If self.positions
# were to be stored as a dict, then its order could change in arbitrary
# ways when the second update_positions call is made. Hence we also
# store it as an OrderedDict.
pt.update_positions({self.EQUITY1: pos1, self.FUTURE5: pos3})
pt.update_positions({self.EQUITY2: pos2})
pt.update_position(
self.EQUITY1,
amount=np.float64(10.0),
last_sale_price=10,
last_sale_date=dt
)
pt.update_position(
self.EQUITY2,
amount=np.float64(-20.0),
last_sale_price=10,
last_sale_date=dt
)
pt.update_position(
self.FUTURE5,
amount=np.float64(30.0),
last_sale_price=100,
last_sale_date=dt
)
pos_stats = pt.stats()
# Test long-only methods
@@ -2132,28 +2139,18 @@ class TestPositionTracker(WithTradingEnvironment,
self.assertEqual(100 + 150000 - 200, pos_stats.net_exposure)
def test_close_position(self):
pt = perf.PositionTracker(self.env.asset_finder, None)
pt = perf.PositionTracker(None)
dt = pd.Timestamp('2017/01/04 3:00PM')
pos1 = perf.Position(
asset=self.FUTURE5,
amount=np.float64(30.0),
last_sale_date=dt,
last_sale_price=100,
)
pos2 = perf.Position(
asset=self.EQUITY1,
amount=np.float64(10.0),
last_sale_date=dt,
last_sale_price=10,
pt.update_position(
asset=self.FUTURE5, amount=np.float64(30.0),
last_sale_date=dt, last_sale_price=100
)
# Update the positions dictionary with `future_sid` first. The order
# matters because it affects the multipliers dictionaries, which are
# OrderedDicts. If `future_sid` is not removed from the multipliers
# dictionaries, equities will hit the incorrect multiplier when
# computing `pt.stats()`.
pt.update_positions({self.FUTURE5: pos1, self.EQUITY1: pos2})
pt.update_position(
asset=self.EQUITY1, amount=np.float64(10.0),
last_sale_date=dt, last_sale_price=10
)
txn = create_txn(self.FUTURE5, dt, 100, -30)
pt.execute_transaction(txn)
-12
View File
@@ -665,18 +665,6 @@ class UnsupportedDatetimeFormat(ZiplineError):
"coercible to a pandas.Timestamp object.")
class PositionTrackerMissingAssetFinder(ZiplineError):
"""
Raised by a PositionTracker if it is asked to update an Asset but does not
have an AssetFinder
"""
msg = (
"PositionTracker attempted to update its Asset information but does "
"not have an AssetFinder. This may be caused by a failure to properly "
"de-serialize a TradingAlgorithm."
)
class AssetDBVersionError(ZiplineError):
"""
Raised by an AssetDBWriter or AssetFinder if the version number in the
+28 -83
View File
@@ -20,11 +20,6 @@ import numpy as np
from collections import namedtuple
from math import isnan
try:
# optional cython based OrderedDict
from cyordereddict import OrderedDict
except ImportError:
from collections import OrderedDict
from six import iteritems, itervalues
from zipline.finance.performance.position import Position
@@ -32,11 +27,9 @@ from zipline.finance.transaction import Transaction
from zipline.utils.input_validation import expect_types
import zipline.protocol as zp
from zipline.assets import (
Equity,
Future,
Asset
)
from zipline.errors import PositionTrackerMissingAssetFinder
from . position import positiondict
log = logbook.Logger('Performance')
@@ -55,18 +48,17 @@ PositionStats = namedtuple('PositionStats',
'net_value'])
def calc_position_values(amounts,
last_sale_prices,
value_multipliers):
iter_amount_price_multiplier = zip(
amounts,
last_sale_prices,
itervalues(value_multipliers),
)
return [
price * amount * multiplier for
price, amount, multiplier in iter_amount_price_multiplier
]
def calc_position_values(positions):
values = []
for position in positions:
if isinstance(position.asset, Future):
# Futures don't have an inherent position value.
values.append(0)
else:
values.append(position.last_sale_price * position.amount)
return values
def calc_net(values):
@@ -74,18 +66,18 @@ def calc_net(values):
return sum(values, np.float64())
def calc_position_exposures(amounts,
last_sale_prices,
exposure_multipliers):
iter_amount_price_multiplier = zip(
amounts,
last_sale_prices,
itervalues(exposure_multipliers),
)
return [
price * amount * multiplier for
price, amount, multiplier in iter_amount_price_multiplier
]
def calc_position_exposures(positions):
exposures = []
for position in positions:
exposure = position.amount * position.last_sale_price
if isinstance(position.asset, Future):
exposure *= position.asset.multiplier
exposures.append(exposure)
return exposures
def calc_long_value(position_values):
@@ -122,44 +114,15 @@ def calc_gross_value(long_value, short_value):
class PositionTracker(object):
def __init__(self, asset_finder, data_frequency):
self.asset_finder = asset_finder
def __init__(self, data_frequency):
# asset => position object
self.positions = positiondict()
# Arrays for quick calculations of positions value
self._position_value_multipliers = OrderedDict()
self._position_exposure_multipliers = OrderedDict()
self._unpaid_dividends = {}
self._unpaid_stock_dividends = {}
self._positions_store = zp.Positions()
self.data_frequency = data_frequency
def _update_asset(self, asset):
try:
self._position_value_multipliers[asset]
self._position_exposure_multipliers[asset]
except KeyError:
# Check if there is an AssetFinder
if self.asset_finder is None:
raise PositionTrackerMissingAssetFinder()
# Collect the value multipliers from applicable assets
asset = self.asset_finder.retrieve_asset(asset)
if isinstance(asset, Equity):
self._position_value_multipliers[asset] = 1
self._position_exposure_multipliers[asset] = 1
if isinstance(asset, Future):
self._position_value_multipliers[asset] = 0
self._position_exposure_multipliers[asset] = asset.multiplier
def update_positions(self, positions):
# update positions in batch
self.positions.update(positions)
for asset, pos in iteritems(positions):
self._update_asset(asset)
@expect_types(asset=Asset)
def update_position(self, asset, amount=None, last_sale_price=None,
last_sale_date=None, cost_basis=None):
@@ -171,7 +134,6 @@ class PositionTracker(object):
if amount is not None:
position.amount = amount
self._update_asset(asset=asset)
if last_sale_price is not None:
position.last_sale_price = last_sale_price
if last_sale_date is not None:
@@ -193,11 +155,7 @@ class PositionTracker(object):
position.update(txn)
if position.amount == 0:
# if this position now has 0 shares, remove it from our internal
# bookkeeping.
del self.positions[asset]
del self._position_value_multipliers[asset]
del self._position_exposure_multipliers[asset]
try:
# if this position exists in our user-facing dictionary,
@@ -205,8 +163,6 @@ class PositionTracker(object):
del self._positions_store[asset]
except KeyError:
pass
else:
self._update_asset(asset)
@expect_types(asset=Asset)
def handle_commission(self, asset, cost):
@@ -221,8 +177,7 @@ class PositionTracker(object):
Parameters
----------
splits: list
A list of splits. Each split is a tuple of (sid, ratio), where
sid is an integer.
A list of splits. Each split is a tuple of (asset, ratio).
Returns
-------
@@ -232,14 +187,12 @@ class PositionTracker(object):
total_leftover_cash = 0
for split in splits:
sid = split[0]
asset = self.asset_finder.retrieve_asset(sid)
asset = split[0]
if asset in self.positions:
# Make the position object handle the split. It returns the
# leftover cash from a fractional share, if there is any.
position = self.positions[asset]
leftover_cash = position.handle_split(asset, split[1])
self._update_asset(split[0])
total_leftover_cash += leftover_cash
return total_leftover_cash
@@ -316,7 +269,6 @@ class PositionTracker(object):
Position(payment_asset)
position.amount += share_count
self._update_asset(payment_asset)
return net_cash_payment
@@ -408,16 +360,9 @@ class PositionTracker(object):
amounts.append(pos.amount)
last_sale_prices.append(pos.last_sale_price)
position_values = calc_position_values(
amounts,
last_sale_prices,
self._position_value_multipliers
)
position_values = calc_position_values(itervalues(self.positions))
position_exposures = calc_position_exposures(
amounts,
last_sale_prices,
self._position_exposure_multipliers
itervalues(self.positions)
)
long_value = calc_long_value(position_values)
-1
View File
@@ -98,7 +98,6 @@ class PerformanceTracker(object):
self.emission_rate = sim_params.emission_rate
self.position_tracker = PositionTracker(
asset_finder=env.asset_finder,
data_frequency=self.sim_params.data_frequency
)