ENH: Add support for splits in zipline.

When a split is encountered, open positions and open orders
are updated accordingly.
This commit is contained in:
Jean Bredeche
2013-07-23 16:22:58 -04:00
committed by Eddie Hebert
parent 9ff588e7fc
commit 6fc077a573
6 changed files with 192 additions and 5 deletions
+35
View File
@@ -390,3 +390,38 @@ class FinanceTestCase(TestCase):
self.assertTrue(sid in oo)
order_list = oo[sid]
self.assertEqual(0, len(order_list))
def test_blotter_processes_splits(self):
sim_params = factory.create_simulation_parameters()
blotter = Blotter()
blotter.set_date(sim_params.period_start)
# set up two open limit orders with very low limit prices,
# one for sid 1 and one for sid 2
blotter.order(1, 100, 10, None, None)
blotter.order(2, 100, 10, None, None)
# send in a split for sid 2
split_event = factory.create_split(2, 0.33333,
sim_params.period_start +
timedelta(days=1))
blotter.process_split(split_event)
for sid in [1, 2]:
order_lists = blotter.open_orders[sid]
self.assertIsNotNone(order_lists)
self.assertEqual(1, len(order_lists))
aapl_order = blotter.open_orders[1][0].to_dict()
fls_order = blotter.open_orders[2][0].to_dict()
# make sure the aapl order didn't change
self.assertEqual(100, aapl_order['amount'])
self.assertEqual(10, aapl_order['limit'])
self.assertEqual(1, aapl_order['sid'])
# make sure the fls order did change
self.assertEqual(33, fls_order['amount'])
self.assertEqual(30, fls_order['limit'])
self.assertEqual(2, fls_order['sid'])
+57
View File
@@ -26,6 +26,7 @@ import itertools
import zipline.utils.factory as factory
import zipline.finance.performance as perf
from zipline.finance.slippage import Transaction, create_transaction
import zipline.utils.math_utils as zp_math
from zipline.gens.composites import date_sorted_sources
from zipline.finance.trading import SimulationParameters
@@ -86,6 +87,62 @@ def calculate_results(host, events):
return results
class TestSplitPerformance(unittest.TestCase):
def setUp(self):
self.sim_params, self.dt, self.end_dt = \
create_random_simulation_parameters()
# start with $10,000
self.sim_params.capital_base = 10e3
self.benchmark_events = benchmark_events_in_range(self.sim_params)
def test_split_long_position(self):
with trading.TradingEnvironment() as env:
events = factory.create_trade_history(
1,
[20, 20],
[100, 100],
oneday,
self.sim_params
)
# set up a long position in sid 1
# 100 shares at $20 apiece = $2000 position
events.insert(0, create_txn(events[0], 20, 100))
events.append(factory.create_split(1, 0.33333,
env.next_trading_day(events[1].dt)))
results = calculate_results(self, events)
# should have 33 shares (at $60 apiece) and $20 in cash
self.assertEqual(2, len(results))
latest_positions = results[1]['daily_perf']['positions']
self.assertEqual(1, len(latest_positions))
# check the last position to make sure it's been updated
position = latest_positions[0]
self.assertEqual(1, position['sid'])
self.assertEqual(33, position['amount'])
self.assertEqual(60, position['cost_basis'])
self.assertEqual(60, position['last_sale_price'])
# since we started with $10000, and we spent $2000 on the
# position, but then got $20 back, we should have $8020
# (or close to it) in cash.
# we won't get exactly 8020 because sometimes a split is
# denoted as a ratio like 0.3333, and we lose some digits
# of precision. thus, make sure we're pretty close.
daily_perf = results[1]['daily_perf']
self.assertTrue(
zp_math.tolerant_equals(8020,
daily_perf['ending_cash'], 1))
class TestDividendPerformance(unittest.TestCase):
def setUp(self):