mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-12 11:50:11 +08:00
DEV: Re-implement commission models to return correct results in the case of multiple fills.
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user