DEV: Re-implement commission models to return correct results in the case of multiple fills.

This commit is contained in:
Jean Bredeche
2016-05-23 21:19:06 -04:00
parent 3ca294d494
commit 39bf1dbd7b
9 changed files with 413 additions and 267 deletions
-178
View File
@@ -41,7 +41,6 @@ from zipline.finance.transaction import create_transaction
import zipline.utils.math_utils as zp_math
from zipline.finance.blotter import Order
from zipline.finance.commission import PerShare, PerTrade, PerDollar
from zipline.finance.performance.position import Position
from zipline.utils.factory import create_simulation_parameters
from zipline.utils.serialization_utils import (
@@ -392,183 +391,6 @@ class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
(i, perf_kind, perf_result['returns']))
class TestCommissionEvents(WithSimParams, WithTmpDir, ZiplineTestCase):
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
END_DATE = pd.Timestamp('2006-01-09', tz='utc')
ASSET_FINDER_EQUITY_SIDS = 0, 1, 133
SIM_PARAMS_CAPITAL_BASE = 10e3
@classmethod
def init_class_fixtures(cls):
super(TestCommissionEvents, cls).init_class_fixtures()
cls.asset1 = cls.env.asset_finder.retrieve_asset(1)
def test_commission_event(self):
trade_events = factory.create_trade_history(
self.asset1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.sim_params,
env=self.env
)
# Test commission models and validate result
# Expected commission amounts:
# PerShare commission: 1.00, 1.00, 1.50 = $3.50
# PerTrade commission: 5.00, 5.00, 5.00 = $15.00
# PerDollar commission: 1.50, 3.00, 4.50 = $9.00
# Total commission = $3.50 + $15.00 + $9.00 = $27.50
data_portal = create_data_portal_from_trade_history(
self.env,
self.tmpdir,
self.sim_params,
{1: trade_events},
)
# Create 3 transactions: 50, 100, 150 shares traded @ $20
first_trade = trade_events[0]
transactions = [create_txn(first_trade.sid, first_trade.dt, 20, i)
for i in [50, 100, 150]]
# Create commission models and validate that produce expected
# commissions.
models = [PerShare(cost=0.01, min_trade_cost=1.00),
PerTrade(cost=5.00),
PerDollar(cost=0.0015)]
expected_results = [3.50, 15.0, 9.0]
for model, expected in zip(models, expected_results):
total_commission = 0
for trade in transactions:
total_commission += model.calculate(trade)[1]
self.assertEqual(total_commission, expected)
# Verify that commission events are handled correctly by
# PerformanceTracker.
commissions = {}
cash_adj_dt = trade_events[0].dt
cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt)
commissions[cash_adj_dt] = [cash_adjustment]
# Insert a purchase order.
txns = [create_txn(first_trade.sid, first_trade.dt, 20, 1)]
results = calculate_results(self.sim_params,
self.env,
data_portal,
txns=txns,
commissions=commissions)
# Validate that we lost 320 dollars from our cash pool.
self.assertEqual(results[-1]['cumulative_perf']['ending_cash'],
9680, "Should have lost 320 from cash pool.")
# Validate that the cost basis of our position changed.
self.assertEqual(results[-1]['daily_perf']['positions']
[0]['cost_basis'], 320.0)
# Validate that the account attributes were updated.
account = results[1]['account']
self.assertEqual(float('inf'), account['day_trades_remaining'])
np.testing.assert_allclose(0.001, account['leverage'], rtol=1e-3,
atol=1e-4)
np.testing.assert_allclose(9680, account['regt_equity'], rtol=1e-3)
self.assertEqual(float('inf'), account['regt_margin'])
np.testing.assert_allclose(9680, account['available_funds'],
rtol=1e-3)
self.assertEqual(0, account['maintenance_margin_requirement'])
np.testing.assert_allclose(9690,
account['equity_with_loan'], rtol=1e-3)
self.assertEqual(float('inf'), account['buying_power'])
self.assertEqual(0, account['initial_margin_requirement'])
np.testing.assert_allclose(9680, account['excess_liquidity'],
rtol=1e-3)
np.testing.assert_allclose(9680, account['settled_cash'],
rtol=1e-3)
np.testing.assert_allclose(9690, account['net_liquidation'],
rtol=1e-3)
np.testing.assert_allclose(0.999, account['cushion'], rtol=1e-3)
np.testing.assert_allclose(10, account['total_positions_value'],
rtol=1e-3)
self.assertEqual(0, account['accrued_interest'])
def test_commission_zero_position(self):
"""
Ensure no div-by-zero errors.
"""
events = factory.create_trade_history(
self.asset1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.sim_params,
env=self.env
)
data_portal = create_data_portal_from_trade_history(
self.env,
self.tmpdir,
self.sim_params,
{1: events},
)
# Buy and sell the same sid so that we have a zero position by the
# time of events[3].
txns = [
create_txn(self.asset1, events[0].dt, 20, 1),
create_txn(self.asset1, events[0].dt, 20, -1)
]
# Add a cash adjustment at the time of event[3].
cash_adj_dt = events[3].dt
commissions = {}
cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt)
commissions[cash_adj_dt] = [cash_adjustment]
results = calculate_results(self.sim_params,
self.env,
data_portal,
txns=txns,
commissions=commissions)
# Validate that we lost 300 dollars from our cash pool.
self.assertEqual(results[-1]['cumulative_perf']['ending_cash'],
9700)
def test_commission_no_position(self):
"""
Ensure no position-not-found or sid-not-found errors.
"""
events = factory.create_trade_history(
self.asset1,
[10, 10, 10, 10, 10],
[100, 100, 100, 100, 100],
oneday,
self.sim_params,
env=self.env
)
data_portal = create_data_portal_from_trade_history(
self.env,
self.tmpdir,
self.sim_params,
{1: events},
)
# Add a cash adjustment at the time of event[3].
cash_adj_dt = events[3].dt
commissions = {}
cash_adjustment = factory.create_commission(self.asset1,
300.0, cash_adj_dt)
commissions[cash_adj_dt] = [cash_adjustment]
results = calculate_results(self.sim_params,
self.env,
data_portal,
commissions=commissions)
# Validate that we lost 300 dollars from our cash pool.
self.assertEqual(results[-1]['cumulative_perf']['ending_cash'],
9700)
class TestDividendPerformance(WithSimParams,
WithInstanceTmpDir,
ZiplineTestCase):