forked from quantopian/zipline
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_commissions.py
255 lines (194 loc) · 8.54 KB
/
test_commissions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
from datetime import timedelta
from textwrap import dedent
from zipline import TradingAlgorithm
from zipline.finance.commission import PerTrade, PerShare, PerDollar
from zipline.finance.order import Order
from zipline.finance.transaction import Transaction
from zipline.testing import ZiplineTestCase, trades_by_sid_to_dfs
from zipline.testing.fixtures import (
WithAssetFinder,
WithSimParams,
WithDataPortal
)
from zipline.utils import factory
class CommissionUnitTests(WithAssetFinder, ZiplineTestCase):
ASSET_FINDER_EQUITY_SIDS = 1, 2
def generate_order_and_txns(self):
asset1 = self.asset_finder.retrieve_asset(1)
# one order
order = Order(dt=None, sid=asset1, amount=500)
# three fills
txn1 = Transaction(sid=asset1, amount=230, dt=None,
price=100, order_id=order.id)
txn2 = Transaction(sid=asset1, amount=170, dt=None,
price=101, order_id=order.id)
txn3 = Transaction(sid=asset1, amount=100, dt=None,
price=102, order_id=order.id)
return order, [txn1, txn2, txn3]
def test_per_trade(self):
model = PerTrade(cost=10)
order, txns = self.generate_order_and_txns()
self.assertEqual(10, model.calculate(order, txns[0]))
order.commission = 10
self.assertEqual(0, model.calculate(order, txns[1]))
self.assertEqual(0, model.calculate(order, txns[2]))
def test_per_share_no_minimum(self):
model = PerShare(cost=0.0075, min_trade_cost=None)
order, txns = self.generate_order_and_txns()
# make sure each commission is pro-rated
self.assertAlmostEqual(1.725, model.calculate(order, txns[0]))
self.assertAlmostEqual(1.275, model.calculate(order, txns[1]))
self.assertAlmostEqual(0.75, model.calculate(order, txns[2]))
def verify_per_share_commissions(self, model, commission_totals):
order, txns = self.generate_order_and_txns()
for i, commission_total in enumerate(commission_totals):
order.commission += model.calculate(order, txns[i])
self.assertAlmostEqual(commission_total, order.commission)
order.filled += txns[i].amount
def test_per_share_with_minimum(self):
# minimum is met by the first trade
self.verify_per_share_commissions(
PerShare(cost=0.0075, min_trade_cost=1),
[1.725, 3, 3.75]
)
# minimum is met by the second trade
self.verify_per_share_commissions(
PerShare(cost=0.0075, min_trade_cost=2.5),
[2.5, 3, 3.75]
)
# minimum is met by the third trade
self.verify_per_share_commissions(
PerShare(cost=0.0075, min_trade_cost=3.5),
[3.5, 3.5, 3.75]
)
# minimum is not met by any of the trades
self.verify_per_share_commissions(
PerShare(cost=0.0075, min_trade_cost=5.5),
[5.5, 5.5, 5.5]
)
def test_per_dollar(self):
model = PerDollar(cost=0.0015)
order, txns = self.generate_order_and_txns()
# make sure each commission is pro-rated
self.assertAlmostEqual(34.5, model.calculate(order, txns[0]))
self.assertAlmostEqual(25.755, model.calculate(order, txns[1]))
self.assertAlmostEqual(15.3, model.calculate(order, txns[2]))
class CommissionAlgorithmTests(WithDataPortal, WithSimParams, ZiplineTestCase):
# make sure order commissions are properly incremented
sidint, = ASSET_FINDER_EQUITY_SIDS = (133,)
code = dedent(
"""
from zipline.api import (
sid, order, set_slippage, slippage, FixedSlippage,
set_commission, commission
)
def initialize(context):
# for these tests, let us take out the entire bar with no price
# impact
set_slippage(slippage.VolumeShareSlippage(1.0, 0))
{0}
context.ordered = False
def handle_data(context, data):
if not context.ordered:
order(sid(133), {1})
context.ordered = True
""",
)
@classmethod
def make_equity_daily_bar_data(cls):
num_days = len(cls.sim_params.sessions)
return trades_by_sid_to_dfs(
{
cls.sidint: factory.create_trade_history(
cls.sidint,
[10.0] * num_days,
[100.0] * num_days,
timedelta(days=1),
cls.sim_params,
trading_calendar=cls.trading_calendar,
),
},
index=cls.sim_params.sessions,
)
def get_results(self, algo_code):
algo = TradingAlgorithm(
script=algo_code,
env=self.env,
sim_params=self.sim_params
)
return algo.run(self.data_portal)
def test_per_trade(self):
results = self.get_results(
self.code.format("set_commission(commission.PerTrade(1))", 300)
)
# should be 3 fills at 100 shares apiece
# one order split among 3 days, each copy of the order should have a
# commission of one dollar
for orders in results.orders[1:4]:
self.assertEqual(1, orders[0]["commission"])
self.verify_capital_used(results, [-1001, -1000, -1000])
def test_per_share_no_minimum(self):
results = self.get_results(
self.code.format("set_commission(commission.PerShare(0.05, None))",
300)
)
# should be 3 fills at 100 shares apiece
# one order split among 3 days, each fill generates an additional
# 100 * 0.05 = $5 in commission
for i, orders in enumerate(results.orders[1:4]):
self.assertEqual((i + 1) * 5, orders[0]["commission"])
self.verify_capital_used(results, [-1005, -1005, -1005])
def test_per_share_with_minimum(self):
# minimum hit by first trade
results = self.get_results(
self.code.format("set_commission(commission.PerShare(0.05, 3))",
300)
)
# commissions should be 5, 10, 15
for i, orders in enumerate(results.orders[1:4]):
self.assertEqual((i + 1) * 5, orders[0]["commission"])
self.verify_capital_used(results, [-1005, -1005, -1005])
# minimum hit by second trade
results = self.get_results(
self.code.format("set_commission(commission.PerShare(0.05, 8))",
300)
)
# commissions should be 8, 10, 15
self.assertEqual(8, results.orders[1][0]["commission"])
self.assertEqual(10, results.orders[2][0]["commission"])
self.assertEqual(15, results.orders[3][0]["commission"])
self.verify_capital_used(results, [-1008, -1002, -1005])
# minimum hit by third trade
results = self.get_results(
self.code.format("set_commission(commission.PerShare(0.05, 12))",
300)
)
# commissions should be 12, 12, 15
self.assertEqual(12, results.orders[1][0]["commission"])
self.assertEqual(12, results.orders[2][0]["commission"])
self.assertEqual(15, results.orders[3][0]["commission"])
self.verify_capital_used(results, [-1012, -1000, -1003])
# minimum never hit
results = self.get_results(
self.code.format("set_commission(commission.PerShare(0.05, 18))",
300)
)
# commissions should be 18, 18, 18
self.assertEqual(18, results.orders[1][0]["commission"])
self.assertEqual(18, results.orders[2][0]["commission"])
self.assertEqual(18, results.orders[3][0]["commission"])
self.verify_capital_used(results, [-1018, -1000, -1000])
def test_per_dollar(self):
results = self.get_results(
self.code.format("set_commission(commission.PerDollar(0.01))", 300)
)
# should be 3 fills at 100 shares apiece, each fill is worth $1k, so
# incremental commission of $1000 * 0.01 = $10
# commissions should be $10, $20, $30
for i, orders in enumerate(results.orders[1:4]):
self.assertEqual((i + 1) * 10, orders[0]["commission"])
self.verify_capital_used(results, [-1010, -1010, -1010])
def verify_capital_used(self, results, values):
self.assertEqual(values[0], results.capital_used[1])
self.assertEqual(values[1], results.capital_used[2])
self.assertEqual(values[2], results.capital_used[3])