mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-03 03:25:52 +08:00
ENH: Add method for computing current portfolio weights
This commit is contained in:
+95
-2
@@ -123,6 +123,7 @@ from zipline.test_algorithms import (
|
||||
TestOrderPercentAlgorithm,
|
||||
TestOrderStyleForwardingAlgorithm,
|
||||
TestOrderValueAlgorithm,
|
||||
TestPositionWeightsAlgorithm,
|
||||
TestRegisterTransformAlgorithm,
|
||||
TestTargetAlgorithm,
|
||||
TestTargetPercentAlgorithm,
|
||||
@@ -1095,10 +1096,62 @@ class TestPositions(WithLogger,
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
|
||||
sids = ASSET_FINDER_EQUITY_SIDS = [1, 133]
|
||||
ASSET_FINDER_EQUITY_SIDS = (1, 133)
|
||||
|
||||
@classmethod
|
||||
def make_equity_daily_bar_data(cls):
|
||||
frame = pd.DataFrame(
|
||||
{
|
||||
'open': [90, 95, 100, 105],
|
||||
'high': [90, 95, 100, 105],
|
||||
'low': [90, 95, 100, 105],
|
||||
'close': [90, 95, 100, 105],
|
||||
'volume': 100,
|
||||
},
|
||||
index=cls.equity_daily_bar_days,
|
||||
)
|
||||
return ((sid, frame) for sid in cls.asset_finder.equities_sids)
|
||||
|
||||
@classmethod
|
||||
def make_futures_info(cls):
|
||||
return pd.DataFrame.from_dict(
|
||||
{
|
||||
1000: {
|
||||
'symbol': 'CLF06',
|
||||
'root_symbol': 'CL',
|
||||
'start_date': cls.START_DATE,
|
||||
'end_date': cls.END_DATE,
|
||||
'auto_close_date': cls.END_DATE + cls.trading_calendar.day,
|
||||
'exchange': 'CME',
|
||||
'multiplier': 100,
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_future_minute_bar_data(cls):
|
||||
trading_calendar = cls.trading_calendars[Future]
|
||||
|
||||
sids = cls.asset_finder.futures_sids
|
||||
minutes = trading_calendar.minutes_for_sessions_in_range(
|
||||
cls.future_minute_bar_days[0],
|
||||
cls.future_minute_bar_days[-1],
|
||||
)
|
||||
frame = pd.DataFrame(
|
||||
{
|
||||
'open': 2.0,
|
||||
'high': 2.0,
|
||||
'low': 2.0,
|
||||
'close': 2.0,
|
||||
'volume': 100,
|
||||
},
|
||||
index=minutes,
|
||||
)
|
||||
return ((sid, frame) for sid in sids)
|
||||
|
||||
def test_empty_portfolio(self):
|
||||
algo = EmptyPositionsAlgorithm(self.sids,
|
||||
algo = EmptyPositionsAlgorithm(self.asset_finder.equities_sids,
|
||||
sim_params=self.sim_params,
|
||||
env=self.env)
|
||||
daily_stats = algo.run(self.data_portal)
|
||||
@@ -1124,6 +1177,46 @@ class TestPositions(WithLogger,
|
||||
empty_positions = daily_stats.positions.map(lambda x: len(x) == 0)
|
||||
self.assertTrue(empty_positions.all())
|
||||
|
||||
def test_position_weights(self):
|
||||
sids = (1, 133, 1000)
|
||||
equity_1, equity_133, future_1000 = \
|
||||
self.asset_finder.retrieve_all(sids)
|
||||
|
||||
algo = TestPositionWeightsAlgorithm(
|
||||
sids_and_amounts=zip(sids, [1, -1, 1]),
|
||||
sim_params=self.sim_params,
|
||||
env=self.env,
|
||||
)
|
||||
daily_stats = algo.run(self.data_portal)
|
||||
|
||||
expected_position_weights = [
|
||||
# No positions held on the first day.
|
||||
pd.Series({}),
|
||||
# Each equity's weight is its price times the number of shares
|
||||
# held. For example, we hold a long position in equity_1 so its
|
||||
# weight is (+95.0 * 1) = 95. For a futures contract, its weight is
|
||||
# the unit price times number of shares held times the multiplier.
|
||||
# For future_1000, this is (2.0 * 1 * 100) = 200.
|
||||
pd.Series({
|
||||
equity_1: 95.0 / (95 + 95 + 200),
|
||||
equity_133: -95.0 / (95 + 95 + 200),
|
||||
future_1000: 200.0 / (95 + 95 + 200),
|
||||
}),
|
||||
pd.Series({
|
||||
equity_1: 100.0 / (100 + 100 + 200),
|
||||
equity_133: -100.0 / (100 + 100 + 200),
|
||||
future_1000: 200.0 / (100 + 100 + 200),
|
||||
}),
|
||||
pd.Series({
|
||||
equity_1: 105.0 / (105 + 105 + 200),
|
||||
equity_133: -105.0 / (105 + 105 + 200),
|
||||
future_1000: 200.0 / (105 + 105 + 200),
|
||||
}),
|
||||
]
|
||||
|
||||
for i, expected in enumerate(expected_position_weights):
|
||||
assert_equal(daily_stats.iloc[i]['position_weights'], expected)
|
||||
|
||||
|
||||
class TestBeforeTradingStart(WithDataPortal,
|
||||
WithSimParams,
|
||||
|
||||
+25
-1
@@ -16,7 +16,7 @@ from warnings import warn
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from zipline.assets import Asset
|
||||
from zipline.assets import Asset, Future
|
||||
from zipline.utils.input_validation import expect_types
|
||||
from .utils.enum import enum
|
||||
from zipline._protocol import BarData # noqa
|
||||
@@ -136,6 +136,10 @@ class Order(Event):
|
||||
)
|
||||
|
||||
|
||||
def asset_multiplier(asset):
|
||||
return asset.multiplier if isinstance(asset, Future) else 1
|
||||
|
||||
|
||||
class Portfolio(object):
|
||||
|
||||
def __init__(self):
|
||||
@@ -169,6 +173,26 @@ class Portfolio(object):
|
||||
},
|
||||
)
|
||||
|
||||
@property
|
||||
def current_portfolio_weights(self):
|
||||
"""
|
||||
Compute each asset's weight in the portfolio by calculating its held
|
||||
value divided by the total value of all positions.
|
||||
|
||||
Each equity's value is its price times the number of shares held. Each
|
||||
futures contract's value is its unit price times number of shares held
|
||||
times the multiplier.
|
||||
"""
|
||||
position_values = pd.Series({
|
||||
asset: (
|
||||
position.last_sale_price *
|
||||
position.amount *
|
||||
asset_multiplier(asset)
|
||||
)
|
||||
for asset, position in self.positions.items()
|
||||
})
|
||||
return position_values / position_values.abs().sum()
|
||||
|
||||
|
||||
class Account(object):
|
||||
'''
|
||||
|
||||
@@ -691,6 +691,23 @@ class EmptyPositionsAlgorithm(TradingAlgorithm):
|
||||
self.record(num_positions=len(self.portfolio.positions))
|
||||
|
||||
|
||||
class TestPositionWeightsAlgorithm(TradingAlgorithm):
|
||||
"""
|
||||
An algorithm that records the weights of its portfolio holdings each day.
|
||||
"""
|
||||
def initialize(self, sids_and_amounts, *args, **kwargs):
|
||||
self.ordered = False
|
||||
self.sids_and_amounts = sids_and_amounts
|
||||
|
||||
def handle_data(self, data):
|
||||
if not self.ordered:
|
||||
for s, amount in self.sids_and_amounts:
|
||||
self.order(self.sid(s), amount)
|
||||
self.ordered = True
|
||||
|
||||
self.record(position_weights=self.portfolio.current_portfolio_weights)
|
||||
|
||||
|
||||
class InvalidOrderAlgorithm(TradingAlgorithm):
|
||||
"""
|
||||
An algorithm that tries to make various invalid order calls, verifying that
|
||||
|
||||
Reference in New Issue
Block a user