diff --git a/docs/zipline.rst b/docs/zipline.rst index ac093601..e8a3b904 100644 --- a/docs/zipline.rst +++ b/docs/zipline.rst @@ -9,14 +9,6 @@ zipline Package :undoc-members: :show-inheritance: -:mod:`component` Module ------------------------ - -.. automodule:: zipline.component - :members: - :undoc-members: - :show-inheritance: - :mod:`lines` Module ------------------- @@ -25,22 +17,6 @@ zipline Package :undoc-members: :show-inheritance: -:mod:`messaging` Module ------------------------ - -.. automodule:: zipline.messaging - :members: - :undoc-members: - :show-inheritance: - -:mod:`monitor` Module ---------------------- - -.. automodule:: zipline.monitor - :members: - :undoc-members: - :show-inheritance: - :mod:`protocol` Module ---------------------- @@ -49,66 +25,18 @@ zipline Package :undoc-members: :show-inheritance: -:mod:`protocol_utils` Module ----------------------------- +:mod:`test_algorithms` Module +----------------------------- -.. automodule:: zipline.protocol_utils +.. automodule:: zipline.test_algorithms :members: :undoc-members: :show-inheritance: -:mod:`serial` Module --------------------- - -.. automodule:: zipline.serial - :members: - :undoc-members: - :show-inheritance: - -:mod:`simulator` Module ------------------------ - -.. automodule:: zipline.simulator - :members: - :undoc-members: - :show-inheritance: - -:mod:`sources` Module +:mod:`version` Module --------------------- -.. automodule:: zipline.sources - :members: - :undoc-members: - :show-inheritance: - -:mod:`topology` Module ----------------------- - -.. automodule:: zipline.topology - :members: - :undoc-members: - :show-inheritance: - -:mod:`topos` Module -------------------- - -.. automodule:: zipline.topos - :members: - :undoc-members: - :show-inheritance: - -:mod:`util` Module ------------------- - -.. automodule:: zipline.util - :members: - :undoc-members: - :show-inheritance: - -:mod:`zmq_utils` Module ------------------------ - -.. automodule:: zipline.zmq_utils +.. automodule:: zipline.version :members: :undoc-members: :show-inheritance: @@ -118,7 +46,10 @@ Subpackages .. toctree:: + zipline.core + zipline.data zipline.finance - zipline.test - zipline.transforms + zipline.gens + zipline.optimize + zipline.utils diff --git a/docs/zipline.test.rst b/docs/zipline.test.rst deleted file mode 100644 index b3da3bbb..00000000 --- a/docs/zipline.test.rst +++ /dev/null @@ -1,83 +0,0 @@ -test Package -============ - -:mod:`algorithms` Module ------------------------- - -.. automodule:: zipline.test.algorithms - :members: - :undoc-members: - :show-inheritance: - -:mod:`client` Module --------------------- - -.. automodule:: zipline.test.client - :members: - :undoc-members: - :show-inheritance: - -:mod:`factory` Module ---------------------- - -.. automodule:: zipline.test.factory - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_finance` Module --------------------------- - -.. automodule:: zipline.test.test_finance - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_monitor` Module --------------------------- - -.. automodule:: zipline.test.test_monitor - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_perf_tracking` Module --------------------------------- - -.. automodule:: zipline.test.test_perf_tracking - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_protocol` Module ---------------------------- - -.. automodule:: zipline.test.test_protocol - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_risk` Module ------------------------ - -.. automodule:: zipline.test.test_risk - :members: - :undoc-members: - :show-inheritance: - -:mod:`test_sanity` Module -------------------------- - -.. automodule:: zipline.test.test_sanity - :members: - :undoc-members: - :show-inheritance: - -:mod:`transform` Module ------------------------ - -.. automodule:: zipline.test.transform - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/zipline.transforms.rst b/docs/zipline.transforms.rst deleted file mode 100644 index aaf57f49..00000000 --- a/docs/zipline.transforms.rst +++ /dev/null @@ -1,19 +0,0 @@ -transforms Package -================== - -:mod:`transforms` Package -------------------------- - -.. automodule:: zipline.transforms - :members: - :undoc-members: - :show-inheritance: - -:mod:`technical` Module ------------------------ - -.. automodule:: zipline.transforms.technical - :members: - :undoc-members: - :show-inheritance: - diff --git a/tests/test_exception_handling.py b/tests/test_exception_handling.py index 8e21f35b..be8117ce 100644 --- a/tests/test_exception_handling.py +++ b/tests/test_exception_handling.py @@ -4,8 +4,7 @@ from collections import defaultdict from zipline.test_algorithms import ExceptionAlgorithm, DivByZeroAlgorithm, \ InitializeTimeoutAlgorithm, TooMuchProcessingAlgorithm -from zipline.finance.trading import SIMULATION_STYLE -from zipline.core.devsimulator import AddressAllocator +from zipline.finance.slippage import FixedSlippage from zipline.lines import SimulatedTrading from zipline.gens.transform import StatefulTransform from zipline.utils.timeout import TimeoutException @@ -29,7 +28,7 @@ class ExceptionTestCase(TestCase): def setUp(self): self.zipline_test_config = { 'sid' : 133, - 'simulation_style' : SIMULATION_STYLE.FIXED_SLIPPAGE + 'slippage' : FixedSlippage() } setup_logger(self) diff --git a/tests/test_finance.py b/tests/test_finance.py index aab30554..a5250d82 100644 --- a/tests/test_finance.py +++ b/tests/test_finance.py @@ -16,11 +16,12 @@ from zipline.lines import SimulatedTrading from zipline.finance.performance import PerformanceTracker from zipline.utils.protocol_utils import ndict from zipline.finance.trading import TransactionSimulator -from zipline.utils.test_utils import \ - setup_logger, \ - teardown_logger,\ +from zipline.finance.slippage import VolumeShareSlippage +from zipline.utils.test_utils import( + setup_logger, + teardown_logger, assert_single_position - +) DEFAULT_TIMEOUT = 15 # seconds EXTENDED_TIMEOUT = 90 @@ -258,7 +259,7 @@ class FinanceTestCase(TestCase): sid = 1 trading_environment = factory.create_trading_environment() - trade_sim = TransactionSimulator([sid]) + trade_sim = TransactionSimulator() price = [10.1] * trade_count volume = [100] * trade_count start_date = trading_environment.first_open @@ -315,12 +316,9 @@ class FinanceTestCase(TestCase): for trade in generated_trades: if trade_delay: trade.dt = trade.dt + trade_delay - txn = trade_sim.apply_trade_to_open_orders(trade) - if txn: - transactions.append(txn) - trade.TRANSACTION = txn - else: - trade.TRANSACTION = None + trade_sim.update(trade) + if trade.TRANSACTION: + transactions.append(trade.TRANSACTION) tracker.process_event(trade) diff --git a/zipline/finance/slippage.py b/zipline/finance/slippage.py new file mode 100644 index 00000000..3b3570d9 --- /dev/null +++ b/zipline/finance/slippage.py @@ -0,0 +1,135 @@ +import pytz +import math +from datetime import timedelta + +import zipline.protocol as zp + +def create_transaction(sid, amount, price, dt, direction, commission): + txn = {'sid' : sid, + 'amount' : int(amount), + 'dt' : dt, + 'price' : price, + 'commission' : commission * amount * direction + } + return zp.ndict(txn) + +class VolumeShareSlippage(object): + + def __init__(self, + volume_limit=.25, + price_impact=0.1, + commission=0.03, + ttl=None): + self.volume_limit = volume_limit + self.price_impact = price_impact + self.commission = commission + if ttl: + assert isinstance(ttl, timedelta), \ + "ttl must be a datetime.timedelta" + self.ttl = ttl + else: + self.ttl = timedelta(days=1) + + def simulate(self, event, open_orders): + + if(event.volume == 0): + #there are zero volume events bc some stocks trade + #less frequently than once per minute. + return None + + if event.sid in open_orders: + orders = open_orders[event.sid] + orders = sorted(orders, key=lambda o: o.dt) + else: + return None + + dt = event.dt + total_order = 0 + simulated_amount = 0 + simulated_impact = 0.0 + direction = 1.0 + for order in orders: + + if(order.dt < event.dt): + + # orders are only good on the day they are issued + if order.dt.day < event.dt.day: + continue + + open_amount = order.amount - order.filled + + if(open_amount != 0): + direction = open_amount / math.fabs(open_amount) + else: + direction = 1 + + desired_order = total_order + open_amount + + volume_share = direction * (desired_order) / event.volume + if volume_share > self.volume_limit: + volume_share = self.volume_limit + simulated_amount = int(volume_share * event.volume * direction) + simulated_impact = (volume_share)**2 * self.price_impact * direction * event.price + + order.filled += (simulated_amount - total_order) + total_order = simulated_amount + + # we cap the volume share at configured % of a trade + if volume_share == self.volume_limit: + break + + + orders = [ x for x in orders if abs(x.amount - x.filled) > 0 and x.dt.day >= event.dt.day] + + open_orders[event.sid] = orders + + + if simulated_amount != 0: + return create_transaction( + event.sid, + simulated_amount, + event.price + simulated_impact, + dt.replace(tzinfo = pytz.utc), + direction, + self.commission + ) + +class FixedSlippage(object): + + def __init__(self, spread=0.0, commission=0.0): + """ + Use the fixed slippage model, which will just add/subtract a specified spread + spread/2 will be added on buys and subtracted on sells per share + commission will be charged per share + """ + self.spread = spread + self.commission = commission + + def simulate(self, event, open_orders): + if event.sid in open_orders: + orders = open_orders[event.sid] + orders = sorted(orders, key=lambda o: o.dt) + else: + return None + + amount = 0 + for order in orders: + amount += order.amount + + if(amount == 0): + return + + direction = amount / math.fabs(amount) + + txn = create_transaction( + event.sid, + amount, + event.price + self.spread/2.0, + event.dt, + direction, + self.commission + ) + + open_orders[event.sid] = [] + + return txn diff --git a/zipline/finance/trading.py b/zipline/finance/trading.py index a53b5f87..935154d0 100644 --- a/zipline/finance/trading.py +++ b/zipline/finance/trading.py @@ -1,33 +1,25 @@ import pytz -import math import logbook import datetime +from collections import defaultdict + import zipline.protocol as zp -from zipline.protocol import SIMULATION_STYLE +from zipline.finance.slippage import VolumeShareSlippage, FixedSlippage log = logbook.Logger('Transaction Simulator') class TransactionSimulator(object): - def __init__(self, sid_filter, style=SIMULATION_STYLE.PARTIAL_VOLUME): - self.open_orders = {} - self.txn_count = 0 - self.trade_window = datetime.timedelta(seconds=30) - self.orderTTL = datetime.timedelta(days=1) - self.commission = 0.03 + def __init__(self, slippage=None): + if slippage: + assert isinstance(slippage, (VolumeShareSlippage, FixedSlippage)) + self.slippage = slippage + else: + self.slippage = VolumeShareSlippage() - if not style or style == SIMULATION_STYLE.PARTIAL_VOLUME: - self.apply_trade_to_open_orders = self.simulate_with_partial_volume - elif style == SIMULATION_STYLE.BUY_ALL: - self.apply_trade_to_open_orders = self.simulate_buy_all - elif style == SIMULATION_STYLE.FIXED_SLIPPAGE: - self.apply_trade_to_open_orders = self.simulate_with_fixed_cost - elif style == SIMULATION_STYLE.NOOP: - self.apply_trade_to_open_orders = self.simulate_noop + self.open_orders = defaultdict(list) - for sid in sid_filter: - self.open_orders[sid] = [] def place_order(self, order): # initialized filled field. @@ -45,121 +37,9 @@ class TransactionSimulator(object): event.TRANSACTION = None # We only fill transactions on trade events. if event.type == zp.DATASOURCE_TYPE.TRADE: - event.TRANSACTION = self.apply_trade_to_open_orders(event) + event.TRANSACTION = self.slippage.simulate(event, self.open_orders) return event - def simulate_buy_all(self, event): - txn = self.create_transaction( - event.sid, - event.volume, - event.price, - event.dt, - 1 - ) - return txn - - def simulate_noop(self, event): - return None - - def simulate_with_fixed_cost(self, event): - if self.open_orders.has_key(event.sid): - orders = self.open_orders[event.sid] - orders = sorted(orders, key=lambda o: o.dt) - else: - return None - - amount = 0 - for order in orders: - amount += order.amount - - if(amount == 0): - return - - direction = amount / math.fabs(amount) - - txn = self.create_transaction( - event.sid, - amount, - event.price + 0.10, # Magic constant? - event.dt, - direction - ) - - self.open_orders[event.sid] = [] - - return txn - - def simulate_with_partial_volume(self, event): - if(event.volume == 0): - #there are zero volume events bc some stocks trade - #less frequently than once per minute. - return None - - if self.open_orders.has_key(event.sid): - orders = self.open_orders[event.sid] - orders = sorted(orders, key=lambda o: o.dt) - else: - return None - - dt = event.dt - expired = [] - total_order = 0 - simulated_amount = 0 - simulated_impact = 0.0 - direction = 1.0 - for order in orders: - - if(order.dt < event.dt): - - # orders are only good on the day they are issued - if order.dt.day < event.dt.day: - continue - - open_amount = order.amount - order.filled - - if(open_amount != 0): - direction = open_amount / math.fabs(open_amount) - else: - direction = 1 - - desired_order = total_order + open_amount - - volume_share = direction * (desired_order) / event.volume - if volume_share > .25: - volume_share = .25 - simulated_amount = int(volume_share * event.volume * direction) - simulated_impact = (volume_share)**2 * .1 * direction * event.price - - order.filled += (simulated_amount - total_order) - total_order = simulated_amount - - # we cap the volume share at 25% of a trade - if volume_share == .25: - break - - orders = [ x for x in orders if abs(x.amount - x.filled) > 0 and x.dt.day >= event.dt.day] - - self.open_orders[event.sid] = orders - - - if simulated_amount != 0: - return self.create_transaction( - event.sid, - simulated_amount, - event.price + simulated_impact, - dt.replace(tzinfo = pytz.utc), - direction - ) - - def create_transaction(self, sid, amount, price, dt, direction): - self.txn_count += 1 - txn = {'sid' : sid, - 'amount' : int(amount), - 'dt' : dt, - 'price' : price, - 'commission' : self.commission * amount * direction - } - return zp.ndict(txn) class TradingEnvironment(object): @@ -184,7 +64,7 @@ class TradingEnvironment(object): self.max_drawdown = max_drawdown assert self.period_start <= self.period_end, \ - "Period start falls after period end." + "Period start falls after period end." for bm in benchmark_returns: self.trading_days.append(bm.date) @@ -197,7 +77,7 @@ class TradingEnvironment(object): self.first_open = self.calculate_first_open() self.last_close = self.calculate_last_close() - + self.prior_day_open = self.calculate_prior_day_open() def calculate_first_open(self): diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 5627dc05..ee0ca65b 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -21,7 +21,7 @@ MAX_HEARTBEAT_INTERVALS = 15 #count class TradeSimulationClient(object): """ Generator-style class that takes the expected output of a merge, a - user algorithm, a trading environment, and a simulator style as + user algorithm, a trading environment, and a simulator slippage as arguments. Pipes the merge stream through a TransactionSimulator and a PerformanceTracker, which keep track of the current state of our algorithm's simulated universe. Results are fed to the user's @@ -52,14 +52,14 @@ class TradeSimulationClient(object): is sent to the algo. """ - def __init__(self, algo, environment, sim_style): + def __init__(self, algo, environment, slippage): self.algo = algo self.sids = algo.get_sid_filter() self.environment = environment - self.style = sim_style + self.slippage = slippage - self.ordering_client = TransactionSimulator(self.sids, style=self.style) + self.ordering_client = TransactionSimulator(self.slippage) self.perf_tracker = PerformanceTracker(self.environment, self.sids) self.algo_start = self.environment.first_open @@ -106,12 +106,12 @@ class TradeSimulationClient(object): yield message class AlgorithmSimulator(object): - + def __init__(self, order_book, algo, algo_start): - + # ========== # Algo Setup # ========== @@ -202,7 +202,7 @@ class AlgorithmSimulator(object): # simulator so that it can fill the placed order when it # receives its next message. self.order_book.place_order(order) - + def transform(self, stream_in): """ Main generator work loop. @@ -263,7 +263,7 @@ class AlgorithmSimulator(object): del event['perf_message'] self.update_universe(event) - + # Send the current state of the universe to the user's algo. self.simulate_snapshot(date) @@ -286,7 +286,7 @@ class AlgorithmSimulator(object): # Needs to be set so that we inject the proper date into algo # log/print lines. self.snapshot_dt = date - + start_tic = datetime.now() with self.heartbeat_monitor: self.algo.handle_data(self.universe) diff --git a/zipline/lines.py b/zipline/lines.py index 1bc5cc90..a3dfa9ba 100644 --- a/zipline/lines.py +++ b/zipline/lines.py @@ -61,7 +61,6 @@ before invoking simulate. """ from zipline.test_algorithms import TestAlgorithm -from zipline.finance.trading import SIMULATION_STYLE from zipline.utils import factory from zipline.gens.composites import ( @@ -69,6 +68,8 @@ from zipline.gens.composites import ( sequential_transforms ) from zipline.gens.tradesimulation import TradeSimulationClient as tsc +from zipline.finance.slippage import FixedSlippage + from logbook import Logger log = Logger('Lines') @@ -81,7 +82,7 @@ class SimulatedTrading(object): transforms, algorithm, environment, - style): + slippage): """ @sources - an iterable of iterables These iterables must yield ndicts that contain: @@ -99,16 +100,16 @@ class SimulatedTrading(object): @environment - An instance of finance.trading.TradingEnvironment - @style - protocol.SIMULATION_STYLE + @slippage - an object with a simulate method that takes a + trade event and returns a transaction """ - self.date_sorted = date_sorted_sources(*sources) self.transforms = transforms # Formerly merged_transforms. self.with_tnfms = sequential_transforms(self.date_sorted, *self.transforms) - self.trading_client = tsc(algorithm, environment, style) + self.trading_client = tsc(algorithm, environment, slippage) self.gen = self.trading_client.simulate(self.with_tnfms) def __iter__(self): @@ -135,9 +136,10 @@ class SimulatedTrading(object): - trade_source - optional parameter to specify trades, if present. If not present :py:class:`zipline.sources.SpecificEquityTrades` is the source, with daily frequency in trades. - - simulation_style: optional parameter that configures the - :py:class:`zipline.finance.trading.TransactionSimulator`. Expects - a SIMULATION_STYLE as defined in + - slippage: optional parameter that configures the + :py:class:`zipline.gens.tradingsimulation.TransactionSimulator`. Expects + an object with a simulate mehod, such as + :py:class:`zipline.gens.tradingsimulation.FixedSlippage`. :py:mod:`zipline.finance.trading` - transforms: optional parameter that provides a list of StatefulTransform objects. @@ -175,9 +177,7 @@ class SimulatedTrading(object): # trade than order trade_count = 101 - simulation_style = config.get('simulation_style') - if not simulation_style: - simulation_style = SIMULATION_STYLE.FIXED_SLIPPAGE + slippage = config.get('slippage', FixedSlippage()) #------------------- # Trade Source @@ -218,7 +218,7 @@ class SimulatedTrading(object): transforms, test_algo, trading_environment, - simulation_style, + slippage, ) #------------------- diff --git a/zipline/optimize/factory.py b/zipline/optimize/factory.py index cb0de069..8967d28a 100644 --- a/zipline/optimize/factory.py +++ b/zipline/optimize/factory.py @@ -12,7 +12,7 @@ from zipline.utils.factory import get_next_trading_dt, create_trading_environmen from zipline.finance.sources import SpecificEquityTrades from zipline.optimize.algorithms import BuySellAlgorithm from zipline.lines import SimulatedTrading -from zipline.finance.trading import SIMULATION_STYLE +from zipline.finance.slippage import FixedSlippage from copy import copy from itertools import cycle @@ -128,7 +128,7 @@ def create_predictable_zipline(config, offset=0, simulate=True): config['trade_count'] = trade_count config['trade_source'] = source config['environment'] = trading_environment - config['simulation_style'] = SIMULATION_STYLE.FIXED_SLIPPAGE + config['slippage'] = FixedSlippage() config['devel'] = True zipline = SimulatedTrading.create_test_zipline(**config) diff --git a/zipline/protocol.py b/zipline/protocol.py index a9bc0ef2..f796693f 100644 --- a/zipline/protocol.py +++ b/zipline/protocol.py @@ -24,13 +24,3 @@ FINANCE_COMPONENT = namelookup({ 'TRADING_CLIENT' : 'TRADING_CLIENT', 'PORTFOLIO_CLIENT' : 'PORTFOLIO_CLIENT', }) - - -# the simulation style enumerates the available transaction simulation -# strategies. -SIMULATION_STYLE = Enum( - 'PARTIAL_VOLUME', - 'BUY_ALL', - 'FIXED_SLIPPAGE', - 'NOOP' -) diff --git a/zipline/utils/test_utils.py b/zipline/utils/test_utils.py index 9ec7326c..65b25e8d 100644 --- a/zipline/utils/test_utils.py +++ b/zipline/utils/test_utils.py @@ -1,4 +1,3 @@ -import multiprocessing from datetime import datetime import blist from zipline.utils.date_utils import EPOCH