mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-10 12:00:15 +08:00
ENH: Filter out empty positions from portfolio container.
To help prevent algorithms from operating on positions that are not in the existing universe of stocks. Formerly, iterating over positions would return positions for stocks which had zero shares held. (Where an explicit check in algorithm code for `pos.amount != 0` could prevent from using a non-existent position.)
This commit is contained in:
+42
-1
@@ -27,7 +27,8 @@ from zipline.test_algorithms import (TestRegisterTransformAlgorithm,
|
||||
TestTargetAlgorithm,
|
||||
TestOrderPercentAlgorithm,
|
||||
TestTargetPercentAlgorithm,
|
||||
TestTargetValueAlgorithm)
|
||||
TestTargetValueAlgorithm,
|
||||
EmptyPositionsAlgorithm)
|
||||
|
||||
from zipline.sources import (SpecificEquityTrades,
|
||||
DataFrameSource,
|
||||
@@ -194,3 +195,43 @@ class TestTransformAlgorithm(TestCase):
|
||||
instant_fill=True)
|
||||
|
||||
algo.run(self.df)
|
||||
|
||||
|
||||
class TestPositions(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
setup_logger(self)
|
||||
self.sim_params = factory.create_simulation_parameters(num_days=4)
|
||||
setup_logger(self)
|
||||
|
||||
trade_history = factory.create_trade_history(
|
||||
1,
|
||||
[10.0, 10.0, 11.0, 11.0],
|
||||
[100, 100, 100, 300],
|
||||
timedelta(days=1),
|
||||
self.sim_params
|
||||
)
|
||||
self.source = SpecificEquityTrades(event_list=trade_history)
|
||||
|
||||
self.df_source, self.df = \
|
||||
factory.create_test_df_source(self.sim_params)
|
||||
|
||||
self.panel_source, self.panel = \
|
||||
factory.create_test_panel_source(self.sim_params)
|
||||
|
||||
def test_empty_portfolio(self):
|
||||
algo = EmptyPositionsAlgorithm(sim_params=self.sim_params,
|
||||
data_frequency='daily')
|
||||
|
||||
daily_stats = algo.run(self.df)
|
||||
|
||||
expected_position_count = [
|
||||
0, # Before entering the first position
|
||||
1, # After entering, exiting on this date
|
||||
0, # After exiting
|
||||
0,
|
||||
]
|
||||
|
||||
for i, expected in enumerate(expected_position_count):
|
||||
self.assertEqual(daily_stats.ix[i]['num_positions'],
|
||||
expected)
|
||||
|
||||
@@ -355,13 +355,20 @@ class PerformancePeriod(object):
|
||||
positions = self._positions_store
|
||||
|
||||
for sid, pos in iteritems(self.positions):
|
||||
if sid not in positions:
|
||||
if pos.amount != 0 and sid not in positions:
|
||||
positions[sid] = zp.Position(sid)
|
||||
position = positions[sid]
|
||||
position.amount = pos.amount
|
||||
position.cost_basis = pos.cost_basis
|
||||
position.last_sale_price = pos.last_sale_price
|
||||
|
||||
# Remove positions with no amounts from portfolio container
|
||||
# that is passed to the algorithm.
|
||||
# These positions are still stored internally for use with
|
||||
# dividends etc.
|
||||
if pos.amount == 0 and sid in positions:
|
||||
del positions[sid]
|
||||
|
||||
return positions
|
||||
|
||||
def get_positions_list(self):
|
||||
|
||||
@@ -633,3 +633,34 @@ class TALIBAlgorithm(TradingAlgorithm):
|
||||
else:
|
||||
result = (np.nan,) * len(t.talib_fn.output_names)
|
||||
self.talib_results[t].append(result)
|
||||
|
||||
|
||||
class EmptyPositionsAlgorithm(TradingAlgorithm):
|
||||
"""
|
||||
An algorithm that ensures that 'phantom' positions do not appear
|
||||
portfolio.positions in the case that a position has been entered
|
||||
and fully exited.
|
||||
"""
|
||||
def initialize(self, *args, **kwargs):
|
||||
self.ordered = False
|
||||
self.exited = False
|
||||
|
||||
def handle_data(self, data):
|
||||
if not self.ordered:
|
||||
for s in data:
|
||||
self.order(s, 100)
|
||||
self.ordered = True
|
||||
|
||||
if not self.exited:
|
||||
amounts = [pos.amount for pos
|
||||
in self.portfolio.positions.itervalues()]
|
||||
if (
|
||||
all([(amount == 100) for amount in amounts]) and
|
||||
(len(amounts) == len(data.keys()))
|
||||
):
|
||||
for stock in self.portfolio.positions:
|
||||
self.order(stock, -100)
|
||||
self.exited = True
|
||||
|
||||
# Should be 0 when all positions are exited.
|
||||
self.record(num_positions=len(self.portfolio.positions))
|
||||
|
||||
Reference in New Issue
Block a user