Adds dividends to performance tracking.

Algorithm returns and the risk calculations that depend on them now include
cash dividends. This commit does _not_ provide an API for user algorithms to
access dividends.

PerformanceTracker expects the dividend data to arrive as events, similar to
the way that Trades arrive. Dividends are expected to have adjusted payment
amounts that are inline with adjusted trades.

PerformanceTracker maintains state of all the unpaid dividends in the position
objects held in PerformancePeriod. Dividend objects contain all the relevant
dates (declared, ex, payment) as well as net and gross amounts. Dividends are
removed from the list as they are paid. Cash flow is not incremented until the
payment day. This creates the possibility of a dividend being owed but not
paid or realized before the end of a test. For example, a dividend with an
ex_date of today may have a pay date 2 weeks in the future. Right now the
algorithm does not receive any credit for unpaid dividends.

Tests cover buying/selling around the ex_date and payment_date, and checking
that the performance calculated is as expected.
This commit is contained in:
fawce
2013-02-04 17:00:14 -05:00
committed by Eddie Hebert
parent 372a714eb8
commit 817ed88e38
5 changed files with 530 additions and 84 deletions
+377 -56
View File
@@ -17,7 +17,6 @@ import collections
import unittest
from nose_parameterized import parameterized
import random
import datetime
import pytz
import itertools
@@ -28,55 +27,377 @@ import zipline.finance.performance as perf
from zipline.utils.protocol_utils import ndict
from zipline.gens.composites import date_sorted_sources
from zipline.finance.trading import TradingEnvironment
from zipline.utils.factory import create_random_trading_environment
class TestPerformance(unittest.TestCase):
onesec = datetime.timedelta(seconds=1)
oneday = datetime.timedelta(days=1)
tradingday = datetime.timedelta(hours=6, minutes=30)
class TestDividendPerformance(unittest.TestCase):
def setUp(self):
self.onesec = datetime.timedelta(seconds=1)
self.oneday = datetime.timedelta(days=1)
self.tradingday = datetime.timedelta(hours=6, minutes=30)
self.trading_environment, self.dt, self.end_dt = self.create_env()
self.trading_environment, self.dt, self.end_dt = \
create_random_trading_environment()
def create_env(self, start_dt=None):
benchmark_returns, treasury_curves = \
factory.load_market_data()
self.trading_environment.capital_base = 10e3
if not start_dt:
for n in range(100):
random_index = random.randint(
0,
len(treasury_curves)
)
start_dt = treasury_curves.keys()[random_index]
end_dt = start_dt + datetime.timedelta(days=365)
now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
if end_dt <= now:
break
else:
end_dt = start_dt + datetime.timedelta(days=365)
now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
assert end_dt <= now, """
failed to find a date suitable daterange after 100 attempts. please double
check treasury and benchmark data in findb, and re-run the test."""
assert start_dt < end_dt, "start_dt must be less than end_dt"
trading_environment = TradingEnvironment(
benchmark_returns,
treasury_curves,
period_start=start_dt,
period_end=end_dt
def test_long_position_receives_dividend(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
return trading_environment, start_dt, end_dt
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[2].dt
)
events.insert(1, dividend)
txn = factory.create_txn(1, 10.0, 100, self.dt+oneday)
events[2].TRANSACTION = txn
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(results[0]['daily_perf']['period_open'], events[0].dt)
self.assertEqual(
results[-1]['daily_perf']['period_open'],
events[-1].dt
)
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0.0, 0.0, 0.1, 0.1, 0.1])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0.0, 0.0, 0.10, 0.0, 0.0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, -1000, 1000, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, -1000, 0, 0, 0])
def test_post_ex_long_position_receives_no_dividend(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[2].dt
)
events.insert(1, dividend)
txn = factory.create_txn(1, 10.0, 100, events[3].dt)
events[3].TRANSACTION = txn
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0, 0, 0, 0, 0])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0, 0, 0, 0, 0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, 0, -1000, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, 0, -1000, -1000, -1000])
def test_selling_before_dividend_payment_still_gets_paid(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[3].dt
)
buy_txn = factory.create_txn(1, 10.0, 100, events[1].dt)
events[1].TRANSACTION = buy_txn
sell_txn = factory.create_txn(1, 10.0, -100, events[2].dt)
events[2].TRANSACTION = sell_txn
events.insert(1, dividend)
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0, 0, 0, 0.1, 0.1])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0, 0, 0, 0.1, 0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, -1000, 1000, 1000, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, -1000, 0, 1000, 1000])
def test_buy_and_sell_before_ex(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10, 10],
[100, 100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[3].dt,
events[4].dt,
events[5].dt
)
buy_txn = factory.create_txn(1, 10.0, 100, events[1].dt)
events[1].TRANSACTION = buy_txn
sell_txn = factory.create_txn(1, 10.0, -100, events[2].dt)
events[2].TRANSACTION = sell_txn
events.insert(1, dividend)
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 6)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0, 0, 0, 0, 0, 0])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0, 0, 0, 0, 0, 0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, -1000, 1000, 0, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, -1000, 0, 0, 0, 0])
def test_ending_before_pay_date(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[-1].dt + 10*oneday
)
buy_txn = factory.create_txn(1, 10.0, 100, events[1].dt)
events[1].TRANSACTION = buy_txn
events.insert(1, dividend)
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0, 0, 0, 0.0, 0.0])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0, 0, 0, 0, 0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, -1000, 0, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(
cumulative_cash_flows,
[0, -1000, -1000, -1000, -1000]
)
def test_short_position_receives_no_dividend(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[2].dt
)
events.insert(1, dividend)
txn = factory.create_txn(1, 10.0, -100, self.dt+oneday)
events[2].TRANSACTION = txn
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0.0, 0.0, 0.0, 0.0, 0.0])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0.0, 0.0, 0.0, 0.0, 0.0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, 1000, 0, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, 1000, 1000, 1000, 1000])
def test_no_position_receives_no_dividend(self):
#post some trades in the market
events = factory.create_trade_history(
1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.trading_environment
)
dividend = factory.create_dividend(
1,
10.00,
events[0].dt,
events[1].dt,
events[2].dt
)
events.insert(1, dividend)
perf_tracker = perf.PerformanceTracker(self.trading_environment)
transformed_events = list(perf_tracker.transform(
((event.dt, [event]) for event in events))
)
#flatten the list of events
results = []
for te in transformed_events:
for event in te[1]:
for message in event.perf_messages:
results.append(message)
perf_messages, risk = perf_tracker.handle_simulation_end()
results.append(perf_messages[0])
self.assertEqual(len(results), 5)
cumulative_returns = \
[event['cumulative_perf']['returns'] for event in results]
self.assertEqual(cumulative_returns, [0.0, 0.0, 0.0, 0.0, 0.0])
daily_returns = [event['daily_perf']['returns'] for event in results]
self.assertEqual(daily_returns, [0.0, 0.0, 0.0, 0.0, 0.0])
cash_flows = [event['daily_perf']['capital_used'] for event in results]
self.assertEqual(cash_flows, [0, 0, 0, 0, 0])
cumulative_cash_flows = \
[event['cumulative_perf']['capital_used'] for event in results]
self.assertEqual(cumulative_cash_flows, [0, 0, 0, 0, 0])
class TestPositionPerformance(unittest.TestCase):
def setUp(self):
self.trading_environment, self.dt, self.end_dt = \
create_random_trading_environment()
def test_long_position(self):
"""
@@ -88,11 +409,11 @@ check treasury and benchmark data in findb, and re-run the test."""
1,
[10, 10, 10, 11],
[100, 100, 100, 100],
self.onesec,
onesec,
self.trading_environment
)
txn = factory.create_txn(1, 10.0, 100, self.dt + self.onesec)
txn = factory.create_txn(1, 10.0, 100, self.dt + onesec)
pp = perf.PerformancePeriod(1000.0)
pp.execute_transaction(txn)
@@ -102,7 +423,7 @@ check treasury and benchmark data in findb, and re-run the test."""
pp.calculate_performance()
self.assertEqual(
pp.period_capital_used,
pp.period_cash_flow,
-1 * txn.price * txn.amount,
"capital used should be equal to the opposite of the transaction \
cost of sole txn in test"
@@ -157,13 +478,13 @@ single short-sale transaction"""
1,
[10, 10, 10, 11, 10, 9],
[100, 100, 100, 100, 100, 100],
self.onesec,
onesec,
self.trading_environment
)
trades_1 = trades[:-2]
txn = factory.create_txn(1, 10.0, -100, self.dt + self.onesec)
txn = factory.create_txn(1, 10.0, -100, self.dt + onesec)
pp = perf.PerformancePeriod(1000.0)
pp.execute_transaction(txn)
@@ -173,7 +494,7 @@ single short-sale transaction"""
pp.calculate_performance()
self.assertEqual(
pp.period_capital_used,
pp.period_cash_flow,
-1 * txn.price * txn.amount,
"capital used should be equal to the opposite of the transaction\
cost of sole txn in test"
@@ -230,7 +551,7 @@ single short-sale transaction"""
pp.calculate_performance()
self.assertEqual(
pp.period_capital_used,
pp.period_cash_flow,
0,
"capital used should be zero, there were no transactions in \
performance period"
@@ -292,7 +613,7 @@ single short-sale transaction"""
ppTotal.calculate_performance()
self.assertEqual(
ppTotal.period_capital_used,
ppTotal.period_cash_flow,
-1 * txn.price * txn.amount,
"capital used should be equal to the opposite of the transaction \
cost of sole txn in test"
@@ -347,7 +668,7 @@ trade after cover"""
1,
[10, 10, 10, 11, 9, 8, 7, 8, 9, 10],
[100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
self.onesec,
onesec,
self.trading_environment
)
@@ -355,10 +676,10 @@ trade after cover"""
1,
10.0,
-100,
self.dt + self.onesec
self.dt + onesec
)
cover_txn = factory.create_txn(1, 7.0, 100, self.dt + self.onesec * 6)
cover_txn = factory.create_txn(1, 7.0, 100, self.dt + onesec * 6)
pp = perf.PerformancePeriod(1000.0)
pp.execute_transaction(short_txn)
@@ -373,7 +694,7 @@ trade after cover"""
cover_txn_cost = cover_txn.price * cover_txn.amount
self.assertEqual(
pp.period_capital_used,
pp.period_cash_flow,
-1 * short_txn_cost - cover_txn_cost,
"capital used should be equal to the net transaction costs"
)
@@ -426,7 +747,7 @@ shares in position"
1,
[10, 11, 11, 12],
[100, 100, 100, 100],
self.onesec,
onesec,
self.trading_environment
)
@@ -434,7 +755,7 @@ shares in position"
1,
[10, 11, 11, 12],
[100, 100, 100, 100],
self.onesec,
onesec,
self.trading_environment
)
@@ -470,13 +791,13 @@ shares in position"
1,
10.0,
-100,
self.dt + self.onesec * 4)
self.dt + onesec * 4)
down_tick = factory.create_trade(
1,
10.0,
100,
trades[-1].dt + self.onesec)
trades[-1].dt + onesec)
pp.rollover()
+1
View File
@@ -302,6 +302,7 @@ class TestBatchTransform(TestCase):
"First three iterations should return None." + "\n" +
"i.e. no returned values until window is full'" +
"%s" % (algo.history_return_price_decorator,))
# After three Nones, the next value should be a data frame
self.assertTrue(isinstance(
algo.history_return_price_class[wl],
+104 -27
View File
@@ -90,8 +90,9 @@ omitted).
| ending_value | the total market value of the positions held at the |
| | end of the period |
+---------------+------------------------------------------------------+
| capital_used | the net capital consumed (positive means spent) by |
| | buying and selling securities in the period |
| cash_flow | the cash flow in the period (negative means spent) |
| | from buying and selling securities in the period. |
| | Includes dividend payments in the period as well. |
+---------------+------------------------------------------------------+
| starting_value| the total market value of the positions held at the |
| | start of the period |
@@ -212,13 +213,15 @@ class PerformanceTracker(object):
new_snapshot = []
for event in snapshot:
event.perf_messages = self.process_event(event)
event.portfolio = self.get_portfolio()
messages = self.process_event(event)
if messages is not None:
event.perf_messages = messages
event.portfolio = self.get_portfolio()
del event['TRANSACTION']
new_snapshot.append(event)
new_snapshot.append(event)
yield date, new_snapshot
if len(new_snapshot) > 0:
yield date, new_snapshot
def get_portfolio(self):
return self.cumulative_performance.as_portfolio()
@@ -241,21 +244,31 @@ class PerformanceTracker(object):
def process_event(self, event):
messages = []
messages = None
self.event_count += 1
while event.dt > self.market_close:
messages.append(self.handle_market_close())
if event.type == zp.DATASOURCE_TYPE.TRADE:
messages = []
while event.dt > self.market_close:
messages.append(self.handle_market_close())
if event.TRANSACTION:
self.txn_count += 1
self.cumulative_performance.execute_transaction(event.TRANSACTION)
self.todays_performance.execute_transaction(event.TRANSACTION)
if event.TRANSACTION:
self.txn_count += 1
self.cumulative_performance.execute_transaction(
event.TRANSACTION
)
self.todays_performance.execute_transaction(event.TRANSACTION)
#update last sale
self.cumulative_performance.update_last_sale(event)
self.todays_performance.update_last_sale(event)
#update last sale
self.cumulative_performance.update_last_sale(event)
self.todays_performance.update_last_sale(event)
del event['TRANSACTION']
elif event.type == zp.DATASOURCE_TYPE.DIVIDEND:
# TODO: confirm with @ehebert that positions objects
# are shared. (and that it is ok).
self.cumulative_performance.add_dividend(event)
self.todays_performance.add_dividend(event)
#calculate performance as of last trade
self.cumulative_performance.calculate_performance()
@@ -267,6 +280,10 @@ class PerformanceTracker(object):
# add the return results from today to the list of DailyReturn objects.
todays_date = self.market_close.replace(hour=0, minute=0, second=0)
self.cumulative_performance.update_dividends(todays_date)
self.todays_performance.update_dividends(todays_date)
todays_return_obj = risk.DailyReturn(
todays_date,
self.todays_performance.returns
@@ -322,7 +339,6 @@ Last successful date: %s" % self.market_open)
# not trigger an end of day, so we trigger the final
# market close(s) here
perf_messages = []
while self.last_close > self.market_close:
perf_messages.append(self.handle_market_close())
@@ -352,6 +368,33 @@ class Position(object):
self.cost_basis = 0.0 # per share
self.last_sale_price = 0.0
self.last_sale_date = 0.0
self.dividends = []
def update_dividends(self, dt):
# TODO: should I have asserts for the dt to be at
# midnight?
payment = 0.0
unpaid_dividends = []
for dividend in self.dividends:
if dt == dividend.ex_date:
# if we own shares at midnight of the div_ex date
# we are entitled to the dividend.
dividend.amount_on_ex_date = self.amount
dividend.payment = self.amount * dividend.net_amount
if dt == dividend.pay_date:
# if it is the payment date, include this
# dividend's actual payment (calculated on
# ex_date)
payment += dividend.payment
else:
unpaid_dividends.append(dividend)
self.dividends = unpaid_dividends
return payment
def add_dividend(self, dividend):
self.dividends.append(dividend)
def update(self, txn):
if(self.sid != txn.sid):
@@ -408,7 +451,7 @@ class PerformancePeriod(object):
self.period_close = period_close
self.ending_value = 0.0
self.period_capital_used = 0.0
self.period_cash_flow = 0.0
self.pnl = 0.0
#sid => position object
self.positions = positiondict()
@@ -439,7 +482,7 @@ class PerformancePeriod(object):
def rollover(self):
self.starting_value = self.ending_value
self.starting_cash = self.ending_cash
self.period_capital_used = 0.0
self.period_cash_flow = 0.0
self.pnl = 0.0
self.processed_transactions = []
self.cumulative_capital_used = 0.0
@@ -457,11 +500,40 @@ class PerformancePeriod(object):
self._position_last_sale_prices, [0])
return index
def add_dividend(self, div):
# The dividend is received on midnight of the dividend
# declared date. We calculate the dividends based on the amount of
# stock owned on midnight of the ex dividend date. However, the cash
# is not dispersed until the payment date, which is
# included in the event.
self.positions[div.sid].add_dividend(div)
def update_dividends(self, todays_date):
"""
Check the payment date and ex date against today's date
to detrmine if we are owed a dividend payment or if the
payment has been disbursed.
"""
cash_payments = 0.0
for sid, pos in self.positions.iteritems():
cash_payments += pos.update_dividends(todays_date)
if cash_payments > 0.0:
# credit our cash balance with the dividend payments
self.period_cash_flow += cash_payments
# debit our cumulative cash spent with the dividend
# payments
self.cumulative_capital_used -= cash_payments
# recalculate performance, including the dividend
# paymtents
self.calculate_performance()
def calculate_performance(self):
self.ending_value = self.calculate_positions_value()
total_at_start = self.starting_cash + self.starting_value
self.ending_cash = self.starting_cash + self.period_capital_used
self.ending_cash = self.starting_cash + self.period_cash_flow
total_at_end = self.ending_cash + self.ending_value
self.pnl = total_at_end - total_at_start
@@ -478,7 +550,7 @@ class PerformancePeriod(object):
index = self.index_for_position(txn.sid)
self._position_amounts[index] = position.amount
self.period_capital_used += -1 * txn.price * txn.amount
self.period_cash_flow += -1 * txn.price * txn.amount
# Max Leverage
# ---------------
@@ -522,7 +594,9 @@ class PerformancePeriod(object):
def __core_dict(self):
rval = {
'ending_value': self.ending_value,
'capital_used': self.period_capital_used,
# this field is renamed to capital_used for backward
# compatibility.
'capital_used': self.period_cash_flow,
'starting_value': self.starting_value,
'starting_cash': self.starting_cash,
'ending_cash': self.ending_cash,
@@ -567,7 +641,9 @@ class PerformancePeriod(object):
# as_portfolio is called in an inner loop,
# so repeated object creation becomes too expensive
portfolio = self._portfolio_store
portfolio.capital_used = self.period_capital_used,
# maintaining the old name for the portfolio field for
# backward compatibility
portfolio.capital_used = self.period_cash_flow
portfolio.starting_cash = self.starting_cash
portfolio.portfolio_value = self.ending_cash + self.ending_value
portfolio.pnl = self.pnl
@@ -583,6 +659,7 @@ class PerformancePeriod(object):
positions = self._positions_store
for sid, pos in self.positions.iteritems():
if sid not in positions:
positions[sid] = zp.Position(sid)
position = positions[sid]
@@ -595,8 +672,8 @@ class PerformancePeriod(object):
def get_positions_list(self):
positions = []
for sid, pos in self.positions.iteritems():
cur = pos.to_dict()
positions.append(cur)
if pos.amount != 0:
positions.append(pos.to_dict())
return positions
+1
View File
@@ -71,6 +71,7 @@ def create_trade(sid, price, amount, datetime, source_id="test_factory"):
trade.low = price * .95
trade.high = price * 1.05
trade.volume = amount
trade.TRANSACTION = None
return trade
+47 -1
View File
@@ -30,7 +30,7 @@ from datetime import datetime, timedelta
import zipline.finance.risk as risk
from zipline.utils.date_utils import tuple_to_date
from zipline.protocol import Event
from zipline.protocol import Event, DATASOURCE_TYPE
from zipline.sources import (SpecificEquityTrades,
DataFrameSource,
DataPanelSource)
@@ -119,6 +119,38 @@ def create_trading_environment(year=2006, start=None, end=None,
return trading_environment
def create_random_trading_environment():
benchmark_returns, treasury_curves = load_market_data()
for n in range(100):
random_index = random.randint(
0,
len(treasury_curves)
)
start_dt = treasury_curves.keys()[random_index]
end_dt = start_dt + timedelta(days=365)
now = datetime.utcnow().replace(tzinfo=pytz.utc)
if end_dt <= now:
break
assert end_dt <= now, """
failed to find a suitable daterange after 100 attempts. please double
check treasury and benchmark data in findb, and re-run the test."""
trading_environment = TradingEnvironment(
benchmark_returns,
treasury_curves,
period_start=start_dt,
period_end=end_dt
)
return trading_environment, start_dt, end_dt
def get_next_trading_dt(current, interval, trading_calendar):
next = current
while True:
@@ -143,6 +175,20 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar,
return trades
def create_dividend(sid, payment, declared_date, ex_date, pay_date):
div = Event({
'sid': sid,
'gross_amount': payment,
'net_amount': payment,
'dt': declared_date.replace(hour=0, minute=0, second=0),
'ex_date': ex_date.replace(hour=0, minute=0, second=0),
'pay_date': pay_date.replace(hour=0, minute=0, second=0),
'type': DATASOURCE_TYPE.DIVIDEND
})
return div
def create_txn(sid, price, amount, datetime):
txn = Event({
'sid': sid,