TST: Adds TestingSlippage slippage model (#1679)

Allows specifying a constant number of shares filled per tick.

Also adds the WithConstantEquityMinuteBarData fixture, relocated from
internal repo.
This commit is contained in:
Andrew Daniels
2017-02-09 08:56:15 -05:00
committed by GitHub
parent e81287e8e1
commit c2560c6079
3 changed files with 127 additions and 0 deletions
+64
View File
@@ -6,12 +6,22 @@ from unittest import TestCase
from numpy import array, empty
from zipline._protocol import BarData
from zipline.finance.asset_restrictions import NoRestrictions
from zipline.finance.order import Order
from zipline.testing import (
check_arrays,
make_alternating_boolean_array,
make_cascading_boolean_array,
parameter_space,
)
from zipline.testing.fixtures import (
WithConstantEquityMinuteBarData,
WithDataPortal,
ZiplineTestCase,
)
from zipline.testing.slippage import TestingSlippage
from zipline.utils.numpy_utils import bool_dtype
@@ -109,3 +119,57 @@ class TestMakeBooleanArray(TestCase):
make_cascading_boolean_array((3, 0)),
empty((3, 0), dtype=bool_dtype),
)
class TestTestingSlippage(WithConstantEquityMinuteBarData,
WithDataPortal,
ZiplineTestCase):
ASSET_FINDER_EQUITY_SYMBOLS = ('A',)
ASSET_FINDER_EQUITY_SIDS = (1,)
@classmethod
def init_class_fixtures(cls):
super(TestTestingSlippage, cls).init_class_fixtures()
cls.asset = cls.asset_finder.retrieve_asset(1)
cls.minute, _ = (
cls.trading_calendar.open_and_close_for_session(cls.START_DATE)
)
def init_instance_fixtures(self):
super(TestTestingSlippage, self).init_instance_fixtures()
self.bar_data = BarData(
self.data_portal,
lambda: self.minute,
"minute",
self.trading_calendar,
NoRestrictions()
)
def make_order(self, amount):
return Order(
self.minute,
self.asset,
amount,
)
def test_constant_filled_per_tick(self):
filled_per_tick = 1
model = TestingSlippage(filled_per_tick)
order = self.make_order(100)
price, volume = model.process_order(self.bar_data, order)
self.assertEqual(price, self.EQUITY_MINUTE_CONSTANT_CLOSE)
self.assertEqual(volume, filled_per_tick)
def test_fill_all(self):
filled_per_tick = TestingSlippage.ALL
order_amount = 100
model = TestingSlippage(filled_per_tick)
order = self.make_order(order_amount)
price, volume = model.process_order(self.bar_data, order)
self.assertEqual(price, self.EQUITY_MINUTE_CONSTANT_CLOSE)
self.assertEqual(volume, order_amount)
+31
View File
@@ -1099,6 +1099,37 @@ class WithBcolzFutureMinuteBarReader(WithFutureMinuteBarData, WithTmpDir):
BcolzMinuteBarReader(p)
class WithConstantEquityMinuteBarData(WithEquityMinuteBarData):
EQUITY_MINUTE_CONSTANT_LOW = 3.0
EQUITY_MINUTE_CONSTANT_OPEN = 4.0
EQUITY_MINUTE_CONSTANT_CLOSE = 5.0
EQUITY_MINUTE_CONSTANT_HIGH = 6.0
EQUITY_MINUTE_CONSTANT_VOLUME = 100.0
@classmethod
def make_equity_minute_bar_data(cls):
trading_calendar = cls.trading_calendars[Equity]
sids = cls.asset_finder.equities_sids
minutes = trading_calendar.minutes_for_sessions_in_range(
cls.equity_minute_bar_days[0],
cls.equity_minute_bar_days[-1],
)
frame = pd.DataFrame(
{
'open': cls.EQUITY_MINUTE_CONSTANT_OPEN,
'high': cls.EQUITY_MINUTE_CONSTANT_HIGH,
'low': cls.EQUITY_MINUTE_CONSTANT_LOW,
'close': cls.EQUITY_MINUTE_CONSTANT_CLOSE,
'volume': cls.EQUITY_MINUTE_CONSTANT_VOLUME,
},
index=minutes,
)
return ((sid, frame) for sid in sids)
class WithAdjustmentReader(WithBcolzEquityDailyBarReader):
"""
ZiplineTestCase mixin providing cls.adjustment_reader as a class level
+32
View File
@@ -0,0 +1,32 @@
from zipline.finance.slippage import SlippageModel
from zipline.utils.sentinel import sentinel
class TestingSlippage(SlippageModel):
"""
Slippage model that fills a constant number of shares per tick, for
testing purposes.
Parameters
----------
filled_per_tick : int or TestingSlippage.ALL
The number of shares to fill on each call to process_order. If
TestingSlippage.ALL is passed, the entire order is filled.
See also
--------
zipline.finance.slippage.SlippageModel
"""
ALL = sentinel('ALL')
def __init__(self, filled_per_tick):
self.filled_per_tick = filled_per_tick
def process_order(self, data, order):
price = data.current(order.asset, "close")
if self.filled_per_tick is self.ALL:
volume = order.amount
else:
volume = self.filled_per_tick
return (price, volume)