def test_expiring_cache(self):
        expiry_1 = Timestamp('2014')
        before_1 = expiry_1 - Timedelta('1 minute')
        after_1 = expiry_1 + Timedelta('1 minute')

        expiry_2 = Timestamp('2015')
        after_2 = expiry_1 + Timedelta('1 minute')

        expiry_3 = Timestamp('2016')

        cache = ExpiringCache()

        cache.set('foo', 1, expiry_1)
        cache.set('bar', 2, expiry_2)

        self.assertEqual(cache.get('foo', before_1), 1)
        # Unwrap on expiry is allowed.
        self.assertEqual(cache.get('foo', expiry_1), 1)

        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('foo', after_1))
        self.assertEqual(e.exception.args, ('foo', ))

        # Should raise same KeyError after deletion.
        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('foo', before_1))
        self.assertEqual(e.exception.args, ('foo', ))

        # Second value should still exist.
        self.assertEqual(cache.get('bar', after_2), 2)

        # Should raise similar KeyError on non-existent key.
        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('baz', expiry_3))
        self.assertEqual(e.exception.args, ('baz', ))
Beispiel #2
0
    def test_expiring_cache(self):
        expiry_1 = Timestamp('2014')
        before_1 = expiry_1 - Timedelta('1 minute')
        after_1 = expiry_1 + Timedelta('1 minute')

        expiry_2 = Timestamp('2015')
        after_2 = expiry_1 + Timedelta('1 minute')

        expiry_3 = Timestamp('2016')

        cache = ExpiringCache()

        cache.set('foo', 1, expiry_1)
        cache.set('bar', 2, expiry_2)

        self.assertEqual(cache.get('foo', before_1), 1)
        # Unwrap on expiry is allowed.
        self.assertEqual(cache.get('foo', expiry_1), 1)

        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('foo', after_1))
        self.assertEqual(e.exception.args, ('foo',))

        # Should raise same KeyError after deletion.
        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('foo', before_1))
        self.assertEqual(e.exception.args, ('foo',))

        # Second value should still exist.
        self.assertEqual(cache.get('bar', after_2), 2)

        # Should raise similar KeyError on non-existent key.
        with self.assertRaises(KeyError) as e:
            self.assertEqual(cache.get('baz', expiry_3))
        self.assertEqual(e.exception.args, ('baz',))
Beispiel #3
0
 def __init__(self,
              trading_calendar,
              reader,
              equity_adjustment_reader,
              asset_finder,
              roll_finders=None,
              sid_cache_size=1000,
              prefetch_length=0):
     self.trading_calendar = trading_calendar
     self._asset_finder = asset_finder
     self._reader = reader
     self._adjustment_readers = {}
     if equity_adjustment_reader is not None:
         self._adjustment_readers[Equity] = \
             HistoryCompatibleUSEquityAdjustmentReader(
                 equity_adjustment_reader)
     if roll_finders:
         self._adjustment_readers[ContinuousFuture] =\
             ContinuousFutureAdjustmentReader(trading_calendar,
                                              asset_finder,
                                              reader,
                                              roll_finders,
                                              self._frequency)
     self._window_blocks = {
         field: ExpiringCache(LRU(sid_cache_size))
         for field in self.FIELDS
     }
     self._prefetch_length = prefetch_length
Beispiel #4
0
 def __init__(self):
     super(MarketImpactBase, self).__init__()
     self._window_data_cache = ExpiringCache()
Beispiel #5
0
class MarketImpactBase(SlippageModel):
    """
    Base class for slippage models which compute a simulated price impact
    according to a history lookback.
    """

    NO_DATA_VOLATILITY_SLIPPAGE_IMPACT = 10.0 / 10000

    def __init__(self):
        super(MarketImpactBase, self).__init__()
        self._window_data_cache = ExpiringCache()

    @abstractmethod
    def get_txn_volume(self, data, order):
        """
        Return the number of shares we would like to order in this minute.

        Parameters
        ----------
        data : BarData
        order : Order

        Return
        ------
        int : the number of shares
        """
        raise NotImplementedError('get_txn_volume')

    @abstractmethod
    def get_simulated_impact(self, order, current_price, current_volume,
                             txn_volume, mean_volume, volatility):
        """
        Calculate simulated price impact.

        Parameters
        ----------
        order : The order being processed.
        current_price : Current price of the asset being ordered.
        current_volume : Volume of the asset being ordered for the current bar.
        txn_volume : Number of shares/contracts being ordered.
        mean_volume : Trailing ADV of the asset.
        volatility : Annualized daily volatility of volume.

        Return
        ------
        int : impact on the current price.
        """
        raise NotImplementedError('get_simulated_impact')

    def process_order(self, data, order):
        if order.open_amount == 0:
            return None, None

        minute_data = data.current(order.asset, ['volume', 'high', 'low'])
        mean_volume, volatility = self._get_window_data(data, order.asset, 20)

        # Price to use is the average of the minute bar's open and close.
        price = np.mean([minute_data['high'], minute_data['low']])

        volume = minute_data['volume']
        if not volume:
            return None, None

        txn_volume = int(
            min(self.get_txn_volume(data, order), abs(order.open_amount)))

        # If the computed transaction volume is zero or a decimal value, 'int'
        # will round it down to zero. In that case just bail.
        if txn_volume == 0:
            return None, None

        if mean_volume == 0 or np.isnan(volatility):
            # If this is the first day the contract exists or there is no
            # volume history, default to a conservative estimate of impact.
            simulated_impact = price * self.NO_DATA_VOLATILITY_SLIPPAGE_IMPACT
        else:
            simulated_impact = self.get_simulated_impact(
                order=order,
                current_price=price,
                current_volume=volume,
                txn_volume=txn_volume,
                mean_volume=mean_volume,
                volatility=volatility,
            )

        impacted_price = \
            price + math.copysign(simulated_impact, order.direction)

        if fill_price_worse_than_limit_price(impacted_price, order):
            return None, None

        return impacted_price, math.copysign(txn_volume, order.direction)

    def _get_window_data(self, data, asset, window_length):
        """
        Internal utility method to return the trailing mean volume over the
        past 'window_length' days, and volatility of close prices for a
        specific asset.

        Parameters
        ----------
        data : The BarData from which to fetch the daily windows.
        asset : The Asset whose data we are fetching.
        window_length : Number of days of history used to calculate the mean
            volume and close price volatility.

        Returns
        -------
        (mean volume, volatility)
        """
        try:
            values = self._window_data_cache.get(asset, data.current_session)
        except KeyError:
            try:
                # Add a day because we want 'window_length' complete days,
                # excluding the current day.
                volume_history = data.history(
                    asset,
                    'volume',
                    window_length + 1,
                    '1d',
                )
                close_history = data.history(
                    asset,
                    'close',
                    window_length + 1,
                    '1d',
                )
            except HistoryWindowStartsBeforeData:
                # If there is not enough data to do a full history call, return
                # values as if there was no data.
                return 0, np.NaN

            # Exclude the first value of the percent change array because it is
            # always just NaN.
            close_volatility = close_history[:-1].pct_change()[1:].std(
                skipna=False, )
            values = {
                'volume': volume_history[:-1].mean(),
                'close': close_volatility * SQRT_252,
            }
            self._window_data_cache.set(asset, values, data.current_session)

        return values['volume'], values['close']
Beispiel #6
0
 def __init__(self):
     super(MarketImpactBase, self).__init__()
     self._window_data_cache = ExpiringCache()
Beispiel #7
0
class MarketImpactBase(SlippageModel):
    """
    Base class for slippage models which compute a simulated price impact
    according to a history lookback.
    """

    NO_DATA_VOLATILITY_SLIPPAGE_IMPACT = 10.0 / 10000

    def __init__(self):
        super(MarketImpactBase, self).__init__()
        self._window_data_cache = ExpiringCache()

    @abstractmethod
    def get_txn_volume(self, data, order):
        """
        Return the number of shares we would like to order in this minute.

        Parameters
        ----------
        data : BarData
        order : Order

        Return
        ------
        int : the number of shares
        """
        raise NotImplementedError('get_txn_volume')

    @abstractmethod
    def get_simulated_impact(self,
                             order,
                             current_price,
                             current_volume,
                             txn_volume,
                             mean_volume,
                             volatility):
        """
        Calculate simulated price impact.

        Parameters
        ----------
        order : The order being processed.
        current_price : Current price of the asset being ordered.
        current_volume : Volume of the asset being ordered for the current bar.
        txn_volume : Number of shares/contracts being ordered.
        mean_volume : Trailing ADV of the asset.
        volatility : Annualized daily volatility of volume.

        Return
        ------
        int : impact on the current price.
        """
        raise NotImplementedError('get_simulated_impact')

    def process_order(self, data, order):
        if order.open_amount == 0:
            return None, None

        minute_data = data.current(order.asset, ['volume', 'high', 'low'])
        mean_volume, volatility = self._get_window_data(data, order.asset, 20)

        # Price to use is the average of the minute bar's open and close.
        price = np.mean([minute_data['high'], minute_data['low']])

        volume = minute_data['volume']
        if not volume:
            return None, None

        txn_volume = int(
            min(self.get_txn_volume(data, order), abs(order.open_amount))
        )

        # If the computed transaction volume is zero or a decimal value, 'int'
        # will round it down to zero. In that case just bail.
        if txn_volume == 0:
            return None, None

        if mean_volume == 0 or np.isnan(volatility):
            # If this is the first day the contract exists or there is no
            # volume history, default to a conservative estimate of impact.
            simulated_impact = price * self.NO_DATA_VOLATILITY_SLIPPAGE_IMPACT
        else:
            simulated_impact = self.get_simulated_impact(
                order=order,
                current_price=price,
                current_volume=volume,
                txn_volume=txn_volume,
                mean_volume=mean_volume,
                volatility=volatility,
            )

        impacted_price = \
            price + math.copysign(simulated_impact, order.direction)

        if fill_price_worse_than_limit_price(impacted_price, order):
            return None, None

        return impacted_price, math.copysign(txn_volume, order.direction)

    def _get_window_data(self, data, asset, window_length):
        """
        Internal utility method to return the trailing mean volume over the
        past 'window_length' days, and volatility of close prices for a
        specific asset.

        Parameters
        ----------
        data : The BarData from which to fetch the daily windows.
        asset : The Asset whose data we are fetching.
        window_length : Number of days of history used to calculate the mean
            volume and close price volatility.

        Returns
        -------
        (mean volume, volatility)
        """
        try:
            values = self._window_data_cache.get(asset, data.current_session)
        except KeyError:
            try:
                # Add a day because we want 'window_length' complete days,
                # excluding the current day.
                volume_history = data.history(
                    asset, 'volume', window_length + 1, '1d',
                )
                close_history = data.history(
                    asset, 'close', window_length + 1, '1d',
                )
            except HistoryWindowStartsBeforeData:
                # If there is not enough data to do a full history call, return
                # values as if there was no data.
                return 0, np.NaN

            # Exclude the first value of the percent change array because it is
            # always just NaN.
            close_volatility = close_history[:-1].pct_change()[1:].std(
                skipna=False,
            )
            values = {
                'volume': volume_history[:-1].mean(),
                'close': close_volatility * SQRT_252,
            }
            self._window_data_cache.set(asset, values, data.current_session)

        return values['volume'], values['close']