From 7a8697f0e5b0493c07c384075167e4b991de2b2b Mon Sep 17 00:00:00 2001 From: fawce Date: Sun, 30 Sep 2012 22:12:00 -0400 Subject: [PATCH] updated based on PR feedback. --- tests/test_transforms.py | 4 ---- zipline/algorithm.py | 6 +++--- zipline/finance/commission.py | 26 ++++++++++++++++++++++++++ zipline/finance/slippage.py | 25 +++++++++++++++---------- zipline/finance/trading.py | 12 ++++++------ zipline/gens/tradesimulation.py | 24 +++++++++++++++--------- zipline/test_algorithms.py | 24 ++++++++++++------------ zipline/utils/simfactory.py | 6 +++--- 8 files changed, 80 insertions(+), 47 deletions(-) diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 62b98b54..3e05436b 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -303,10 +303,6 @@ class BatchTransformTestCase(TestCase): # test overloaded class for test_history in [algo.history_return_price_class, algo.history_return_price_decorator]: - self.assertTrue(np.all(test_history[2].values.flatten() == range(4, 10))) - self.assertTrue(np.all(test_history[3].values.flatten() == range(4, 10))) - self.assertTrue(np.all(test_history[4].values.flatten() == range(6, 14))) - np.testing.assert_array_equal( range(4, 10), test_history[2].values.flatten() diff --git a/zipline/algorithm.py b/zipline/algorithm.py index bf696b46..23cde1d7 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -5,7 +5,7 @@ from zipline.gens.tradegens import DataFrameSource from zipline.utils.factory import create_trading_environment from zipline.gens.transform import StatefulTransform from zipline.lines import SimulatedTrading -from zipline.finance.slippage import FixedSlippage, simulate_method_factory +from zipline.finance.slippage import FixedSlippage, transact_partial from zipline.finance.commission import PerShare @@ -75,7 +75,7 @@ class TradingAlgorithm(object): transforms, self, environment, - simulate_method_factory(FixedSlippage(), PerShare(0.0)) + transact_partial(FixedSlippage(), PerShare(0.0)) ) def run(self, source, start=None, end=None): @@ -180,5 +180,5 @@ class TradingAlgorithm(object): def initialize(self, *args, **kwargs): pass - def set_simulate_override(self, slippage_callable): + def set_transact_setter(self, transact_setter): pass diff --git a/zipline/finance/commission.py b/zipline/finance/commission.py index 38883a65..442117ec 100644 --- a/zipline/finance/commission.py +++ b/zipline/finance/commission.py @@ -1,18 +1,44 @@ class PerShare(object): + """ + Calculates a commission for a transaction based on a per + share cost. + """ def __init__(self, cost=0.03): + """ + Cost parameter is the cost of a trade per-share. $0.03 + means three cents per share, which is a very conservative + (quite high) for per share costs. + """ self.cost = cost def calculate(self, transaction): + """ + returns a tuple of: + (per share commission, total transaction commission) + """ return self.cost, abs(transaction.amount * self.cost) class PerTrade(object): + """ + Calculates a commission for a transaction based on a per + trade cost. + """ def __init__(self, cost=5.0): + """ + Cost parameter is the cost of a trade, regardless of + share count. $5.00 per trade is fairly typical of + discount brokers. + """ self.cost = cost def calculate(self, transaction): + """ + returns a tuple of: + (per share commission, total transaction commission) + """ if transaction.amount == 0: return 0.0, 0.0 diff --git a/zipline/finance/slippage.py b/zipline/finance/slippage.py index 707fdd94..be6e4f5b 100644 --- a/zipline/finance/slippage.py +++ b/zipline/finance/slippage.py @@ -1,20 +1,25 @@ import pytz import math +from functools import partial + import zipline.protocol as zp +def transact_stub(slippage, commission, open_orders, events): + """ + This is intended to be wrapped in a partial, so that the + slippage and commission models can be enclosed. + """ + transaction = slippage.simulate(open_orders, events) + if transaction: + per_share, total_commission = commission.calculate(transaction) + transaction.price = transaction.price + per_share + transaction.commission = total_commission + return transaction -def simulate_method_factory(slippage, commission): - def simulate(open_orders, events): - transaction = slippage.simulate(open_orders, events) - if transaction: - per_share, total_commission = commission.calculate(transaction) - transaction.price = transaction.price + per_share - transaction.commission = total_commission - return transaction - - return simulate +def transact_partial(slippage, commission): + return partial(transact_stub, slippage, commission) def create_transaction(sid, amount, price, dt): diff --git a/zipline/finance/trading.py b/zipline/finance/trading.py index 4fc94aef..dc13bef9 100644 --- a/zipline/finance/trading.py +++ b/zipline/finance/trading.py @@ -7,7 +7,7 @@ from collections import defaultdict import zipline.protocol as zp from zipline.finance.slippage import ( VolumeShareSlippage, - simulate_method_factory + transact_partial ) from zipline.finance.commission import PerShare @@ -15,11 +15,11 @@ log = logbook.Logger('Transaction Simulator') class TransactionSimulator(object): - def __init__(self, simulate=None): - if simulate: - self.simulate = simulate + def __init__(self, transact=None): + if transact != None: + self.transact = transact else: - self.simulate = simulate_method_factory( + self.transact = transact_partial( VolumeShareSlippage(), PerShare() ) @@ -43,7 +43,7 @@ class TransactionSimulator(object): event.TRANSACTION = None # We only fill transactions on trade events. if event.type == zp.DATASOURCE_TYPE.TRADE: - event.TRANSACTION = self.simulate(event, self.open_orders) + event.TRANSACTION = self.transact(event, self.open_orders) return event diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index b9d3ccc6..70e067d1 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -52,14 +52,14 @@ class TradeSimulationClient(object): is sent to the algo. """ - def __init__(self, algo, environment, txn_sim): + def __init__(self, algo, environment, transact): self.algo = algo self.sids = algo.get_sid_filter() self.environment = environment - self.txn_sim = txn_sim + self.transact = transact - self.ordering_client = TransactionSimulator(self.txn_sim) + self.ordering_client = TransactionSimulator(self.transact) self.perf_tracker = PerformanceTracker(self.environment, self.sids) self.algo_start = self.environment.first_open @@ -130,8 +130,10 @@ class AlgorithmSimulator(object): self.algolog = Logger("AlgoLog") self.algo.set_logger(self.algolog) - # Porived user algorithm with slippage override. - self.algo.set_simulate_override(self.simulate_override) + # Provide user algorithm with a setter for the transact + # method (method that constructs transactions based on + # open orders and trade events). + self.algo.set_transact_setter(self.set_transact) # Handler for heartbeats during calls to handle_data. def log_heartbeats(beat_count, stackframe): @@ -173,13 +175,17 @@ class AlgorithmSimulator(object): record.extra['algo_dt'] = self.snapshot_dt self.processor = Processor(inject_algo_dt) - def simulate_override(self, slippage): - self.order_book.slippage = slippage + def set_transact(self, transact): + """ + Set the method that will be called to create a + transaction from open orders and trade events. + """ + self.order_book.transact = transact def order(self, sid, amount): """ Closure to pass into the user's algo to allow placing orders - into the txn_sim's dict of open orders. + into the transaction simulator's dict of open orders. """ assert sid in self.sids, "Order on invalid sid: %i" % sid order = ndict({ @@ -230,7 +236,7 @@ class AlgorithmSimulator(object): if date == 'DONE': for event in snapshot: yield event.perf_message - raise StopIteration() + raise StopIteration # We're still in the warmup period. Use the event to # update our universe, but don't yield any perf messages, diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 6974b248..ab54b15a 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -44,8 +44,8 @@ The algorithm must expose methods: self.Portfolio[sid(133)]['cost_basis'] - - set_simulate_override: method that accepts a callable. Will - be set as the value of the set_simulate_override method of + - set_transact_setter: method that accepts a callable. Will + be set as the value of the set_transact_setter method of the trading_client. This allows an algorithm to change the slippage model used to predict transactions based on orders and trade events. @@ -97,7 +97,7 @@ class TestAlgorithm(): def get_sid_filter(self): return self.sid_filter - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass @@ -138,7 +138,7 @@ class HeavyBuyAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class NoopAlgorithm(object): @@ -164,7 +164,7 @@ class NoopAlgorithm(object): def get_sid_filter(self): return [] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class ExceptionAlgorithm(object): @@ -210,7 +210,7 @@ class ExceptionAlgorithm(object): else: return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class DivByZeroAlgorithm(): @@ -240,7 +240,7 @@ class DivByZeroAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class InitializeTimeoutAlgorithm(): @@ -269,7 +269,7 @@ class InitializeTimeoutAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class TooMuchProcessingAlgorithm(): @@ -297,7 +297,7 @@ class TooMuchProcessingAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class TimeoutAlgorithm(): @@ -327,7 +327,7 @@ class TimeoutAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class TestPrintAlgorithm(): @@ -354,7 +354,7 @@ class TestPrintAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass class TestLoggingAlgorithm(): @@ -381,7 +381,7 @@ class TestLoggingAlgorithm(): def get_sid_filter(self): return [self.sid] - def set_simulate_override(self, txn_sim_callable): + def set_transact_setter(self, txn_sim_callable): pass diff --git a/zipline/utils/simfactory.py b/zipline/utils/simfactory.py index 8866c2bb..ba2d5abb 100644 --- a/zipline/utils/simfactory.py +++ b/zipline/utils/simfactory.py @@ -2,7 +2,7 @@ import zipline.utils.factory as factory from zipline.test_algorithms import TestAlgorithm from zipline.lines import SimulatedTrading -from zipline.finance.slippage import FixedSlippage, simulate_method_factory +from zipline.finance.slippage import FixedSlippage, transact_partial from zipline.finance.commission import PerShare def create_test_zipline(**config): @@ -65,7 +65,7 @@ def create_test_zipline(**config): slippage = config.get('slippage', FixedSlippage()) commission = PerShare() - sim_method = simulate_method_factory(slippage, commission) + transact_method = transact_partial(slippage, commission) #------------------- # Trade Source @@ -106,7 +106,7 @@ def create_test_zipline(**config): transforms, test_algo, trading_environment, - sim_method + transact_method ) #-------------------