Skip to content

Commit

Permalink
Merge d745f82 into 96ec1fd
Browse files Browse the repository at this point in the history
  • Loading branch information
jbredeche committed May 23, 2016
2 parents 96ec1fd + d745f82 commit 91c6812
Show file tree
Hide file tree
Showing 9 changed files with 413 additions and 267 deletions.
Binary file modified tests/resources/example_data.tar.gz
Binary file not shown.
8 changes: 7 additions & 1 deletion tests/resources/rebuild_example_data
Expand Up @@ -11,9 +11,11 @@ import pandas as pd
from zipline import examples, run_algorithm
from zipline.testing import test_resource_path, tmp_dir
from zipline.utils.cache import dataframe_cache
from zipline.data.bundles import register

banner = """
Please verify that the new perfomance is more correct than the old performance.
Please verify that the new performance is more correct than the old
performance.
To do this, please inspect `new` and `old` which are mappings from the name of
the example to the results.
Expand All @@ -37,6 +39,9 @@ def main(ctx):
"""Rebuild the perf data for test_examples
"""
example_path = test_resource_path('example_data.tar.gz')

register('test', lambda *args: None)

with tmp_dir() as d:
with tarfile.open(example_path) as tar:
tar.extractall(d.path)
Expand Down Expand Up @@ -67,6 +72,7 @@ def main(ctx):
environ={
'ZIPLINE_ROOT': d.getpath('example_data/root'),
},
capital_base=1e7,
**mod._test_args()
)

Expand Down
45 changes: 32 additions & 13 deletions tests/test_algorithm.py
Expand Up @@ -19,6 +19,7 @@
from unittest import TestCase, skip

import logbook
import toolz
from logbook import TestHandler, WARNING
from mock import MagicMock
from nose_parameterized import parameterized
Expand Down Expand Up @@ -1488,14 +1489,18 @@ def handle_data(context, data):
self.assertEqual(len(all_txns), 1)
txn = all_txns[0]

self.assertEqual(100.0, txn["commission"])
expected_spread = 0.05
expected_commish = 0.10
expected_price = test_algo.recorded_vars["price"] - expected_spread \
- expected_commish
expected_price = test_algo.recorded_vars["price"] - expected_spread

self.assertEqual(expected_price, txn['price'])

# make sure that the $100 commission was applied to our cash
# the txn was for -1000 shares at 9.95, means -9.95k. our capital_used
# for that day was therefore 9.95k, but after the $100 commission,
# it should be 9.85k.
self.assertEqual(9850, results.capital_used[1])
self.assertEqual(100, results["orders"][1][0]["commission"])

@parameterized.expand(
[
('no_minimum_commission', 0,),
Expand Down Expand Up @@ -1543,7 +1548,6 @@ def handle_data(context, data):
sim_params=self.sim_params,
env=self.env,
)
set_algo_instance(test_algo)
trades = factory.create_daily_trade_source(
[0], self.sim_params, self.env)
data_portal = create_data_portal_from_trade_history(
Expand All @@ -1555,13 +1559,28 @@ def handle_data(context, data):
for val in sublist]

self.assertEqual(len(all_txns), 67)
first_txn = all_txns[0]
# all_orders are all the incremental versions of the
# orders as each new fill comes in.
all_orders = list(toolz.concat(results['orders']))

if minimum_commission == 0:
commish = first_txn["amount"] * 0.02
self.assertEqual(commish, first_txn["commission"])
# for each incremental version of each order, the commission
# should be its filled amount * 0.02
for order_ in all_orders:
self.assertAlmostEqual(
order_["filled"] * 0.02,
order_["commission"]
)
else:
self.assertEqual(minimum_commission, first_txn["commission"])
# the commission should be at least the min_trade_cost
for order_ in all_orders:
if order_["filled"] > 0:
self.assertAlmostEqual(
max(order_["filled"] * 0.02, minimum_commission),
order_["commission"]
)
else:
self.assertEqual(0, order_["commission"])
finally:
tempdir.cleanup()

Expand Down Expand Up @@ -3046,7 +3065,7 @@ def test_daily_delisted_equities(self,
self.assertDictContainsSubset(
{
'amount': order_size,
'commission': 0.0,
'commission': None,
'dt': self.test_days[1],
'price': initial_fill_prices[sid],
'sid': sid,
Expand Down Expand Up @@ -3143,7 +3162,7 @@ def orders_for_date(date):
self.assertDictContainsSubset(
{
'amount': 10,
'commission': None,
'commission': 0,
'created': first_asset_end_date,
'dt': first_asset_end_date,
'sid': assets[0],
Expand All @@ -3158,7 +3177,7 @@ def orders_for_date(date):
self.assertDictContainsSubset(
{
'amount': 10,
'commission': None,
'commission': 0,
'created': first_asset_end_date,
'dt': first_asset_auto_close_date,
'sid': assets[0],
Expand Down Expand Up @@ -3252,7 +3271,7 @@ def test_minutely_delisted_equities(self):
self.assertDictContainsSubset(
{
'amount': order_size,
'commission': 0.0,
'commission': None,
'dt': backtest_minutes[1],
'price': initial_fill_prices[sid],
'sid': sid,
Expand Down
255 changes: 255 additions & 0 deletions tests/test_commissions.py
@@ -0,0 +1,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_daily_bar_data(cls):
num_days = len(cls.sim_params.trading_days)

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,
cls.env,
),
},
index=cls.sim_params.trading_days,
)

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])

0 comments on commit 91c6812

Please sign in to comment.