示例#1
0
    def test_divide_df_single_column(self):
        x = pd.DataFrame(dict(a=[2.0, 7.0, -7.0, -7.00, 3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        y = pd.DataFrame(dict(b=[2.0, 3.5, 2.0, -3.5, -3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        ans = list(divide_df_single_column(x, y).iloc[:, 0])
        self.assertEqual(ans, [1., 2., -3.5, 2., -1.])

        x = pd.DataFrame(dict(a=[2.0, np.nan, -7.0, np.nan, 3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        y = pd.DataFrame(dict(b=[2.0, 3.5, np.nan, np.nan, -3.5]),
                         pd.date_range(pd.datetime(2015, 1, 2), periods=5))

        ans = list(divide_df_single_column(x, y).iloc[:, 0])

        self.assertTrue(np.isnan(ans[0]))
        self.assertTrue(np.isnan(ans[1]))
        self.assertTrue(np.isnan(ans[3]))

        self.assertEqual(ans[2], -2.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(True, False)).iloc[:, 0])
        self.assertEqual(ans[1], 1.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(False, True)).iloc[:, 0])
        self.assertEqual(ans[4], 1.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(True, True)).iloc[:, 0])
        self.assertEqual(list(ans)[1:], [1., -2., -2.0, 1., -1.])
示例#2
0
    def test_divide_df_single_column(self):
        x = pd.DataFrame(dict(a=[2.0, 7.0, -7.0, -7.00, 3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        y = pd.DataFrame(dict(b=[2.0, 3.5, 2.0, -3.5, -3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        ans = list(divide_df_single_column(x, y).iloc[:, 0])
        self.assertEqual(ans, [1., 2., -3.5, 2., -1.])

        x = pd.DataFrame(dict(a=[2.0, np.nan, -7.0, np.nan, 3.5]),
                         pd.date_range(pd.datetime(2015, 1, 1), periods=5))
        y = pd.DataFrame(dict(b=[2.0, 3.5, np.nan, np.nan, -3.5]),
                         pd.date_range(pd.datetime(2015, 1, 2), periods=5))
        
        ans = list(divide_df_single_column(x, y).iloc[:, 0])
        

        self.assertTrue(np.isnan(ans[0]))
        self.assertTrue(np.isnan(ans[1]))
        self.assertTrue(np.isnan(ans[3]))

        self.assertEqual(ans[2], -2.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(True, False)).iloc[:, 0])
        self.assertEqual(ans[1], 1.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(False, True)).iloc[:, 0])
        self.assertEqual(ans[4], 1.0)

        ans = list(divide_df_single_column(
            x, y, ffill=(True, True)).iloc[:, 0])
        self.assertEqual(list(ans)[1:], [1., -2., -2.0, 1., -1.])
示例#3
0
def carry(daily_ann_roll, vol, smooth_days=90):
    """
    Calculate raw carry forecast, given annualised roll and volatility series (which must match)

    Assumes that everything is daily data

    :param daily_ann_roll: The annualised roll
    :type daily_ann_roll: pd.DataFrame (assumed Tx1)

    :param vol: The daily price unit volatility (NOT % vol)
    :type vol: pd.DataFrame (assumed Tx1)

    >>> from systems.provided.example.testdata import get_test_object_futures
    >>> from systems.basesystem import System
    >>> (rawdata, data, config)=get_test_object_futures()
    >>> system=System( [rawdata], data, config)
    >>>
    >>> carry(rawdata.daily_annualised_roll("EDOLLAR"), rawdata.daily_returns_volatility("EDOLLAR")).tail(2)
                annualised_roll_daily
    2015-04-21               0.350892
    2015-04-22               0.350892
    """

    ann_stdev = vol * ROOT_BDAYS_INYEAR
    raw_carry = divide_df_single_column(daily_ann_roll, ann_stdev, ffill=(False, True))
    smooth_carry = pd.ewma(raw_carry, smooth_days)

    return smooth_carry
示例#4
0
def calc_ewmac_forecast(price, Lfast, Lslow=None):
    """
    Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback

    """
    # price: This is the stitched price series
    # We can't use the price of the contract we're trading, or the volatility will be jumpy
    # And we'll miss out on the rolldown. See
    # http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html

    price=price.resample("1B", how="last")

    if Lslow is None:
        Lslow = 4 * Lfast

    # We don't need to calculate the decay parameter, just use the span
    # directly

    fast_ewma = pd.ewma(price, span=Lfast)
    slow_ewma = pd.ewma(price, span=Lslow)
    raw_ewmac = fast_ewma - slow_ewma

    vol = robust_vol_calc(price.diff())

    return divide_df_single_column(raw_ewmac, vol)
示例#5
0
def ewmac_forecast_with_defaults(price, Lfast=32, Lslow=128):
    """
    Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback

    Assumes that 'price' is daily data

    This version recalculates the price volatility, and does not do capping or scaling

    :param price: The price or other series to use (assumed Tx1)
    :type price: pd.DataFrame
    :param Lfast: Lookback for fast in days
    :type Lfast: int
    :param Lslow: Lookback for slow in days
    :type Lslow: int

    :returns: pd.DataFrame -- unscaled, uncapped forecast


    """
    # price: This is the stitched price series
    # We can't use the price of the contract we're trading, or the volatility will be jumpy
    # And we'll miss out on the rolldown. See
    # http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html

    # We don't need to calculate the decay parameter, just use the span
    # directly

    fast_ewma = pd.ewma(price, span=Lfast)
    slow_ewma = pd.ewma(price, span=Lslow)
    raw_ewmac = fast_ewma - slow_ewma

    vol = robust_vol_calc(price.diff())

    return divide_df_single_column(raw_ewmac, vol, ffill=(False, True))
示例#6
0
def get_scatter_data_for_code_vol(system,
                                  instrument_code,
                                  rule_name,
                                  return_period=5,
                                  days=64):

    denom_price = system.rawdata.daily_denominator_price(instrument_code)
    x = system.rawdata.daily_returns(instrument_code)
    vol = robust_vol_calc(x, days)
    perc_vol = 100.0 * divide_df_single_column(vol, denom_price.shift(1))

    volavg = pd.rolling_median(perc_vol, 1250, min_periods=10)
    vol_qq = (perc_vol - volavg) / volavg

    ## work out return for the N days after the forecast

    norm_data = system.accounts.pandl_for_instrument_forecast(
        instrument_code, rule_name)

    (vol_qq, norm_data) = align_to_joint(vol_qq,
                                         norm_data,
                                         ffill=(True, False))

    period_returns = pd.rolling_sum(norm_data, return_period, min_periods=1)

    ex_post_returns = period_returns.shift(-return_period)
    lagged_vol = vol_qq.shift(1)

    return (list(ex_post_returns.iloc[:, 0].values),
            list(lagged_vol.iloc[:, 0].values))
示例#7
0
文件: rules.py 项目: minhpascal/kod
def ewmac_forecast_with_defaults(price, Lfast=32, Lslow=128):
    """
    Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback

    Assumes that 'price' is daily data

    This version recalculates the price volatility, and does not do capping or scaling

    :param price: The price or other series to use (assumed Tx1)
    :type price: pd.DataFrame
    :param Lfast: Lookback for fast in days
    :type Lfast: int
    :param Lslow: Lookback for slow in days
    :type Lslow: int

    :returns: pd.DataFrame -- unscaled, uncapped forecast


    """
    # price: This is the stitched price series
    # We can't use the price of the contract we're trading, or the volatility will be jumpy
    # And we'll miss out on the rolldown. See
    # http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html

    # We don't need to calculate the decay parameter, just use the span
    # directly

    fast_ewma = pd.ewma(price, span=Lfast)
    slow_ewma = pd.ewma(price, span=Lslow)
    raw_ewmac = fast_ewma - slow_ewma

    vol = robust_vol_calc(price.diff())

    return divide_df_single_column(raw_ewmac, vol, ffill=(False, True))
示例#8
0
def calc_ewmac_forecast(price, Lfast, Lslow=None):
    """
    Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback

    """
    # price: This is the stitched price series
    # We can't use the price of the contract we're trading, or the volatility will be jumpy
    # And we'll miss out on the rolldown. See
    # http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html

    price = price.resample("1B", how="last")

    if Lslow is None:
        Lslow = 4 * Lfast

    # We don't need to calculate the decay parameter, just use the span
    # directly

    fast_ewma = pd.ewma(price, span=Lfast)
    slow_ewma = pd.ewma(price, span=Lslow)
    raw_ewmac = fast_ewma - slow_ewma

    vol = robust_vol_calc(price.diff())

    return divide_df_single_column(raw_ewmac, vol)
def get_scatter_data_for_code_vol(
        system, instrument_code, rule_name, return_period=5, days=64):

    denom_price = system.rawdata.daily_denominator_price(instrument_code)
    x = system.rawdata.daily_returns(instrument_code)
    vol = robust_vol_calc(x, days)
    perc_vol = 100.0 * divide_df_single_column(vol, denom_price.shift(1))

    volavg = pd.rolling_median(perc_vol, 1250, min_periods=10)
    vol_qq = (perc_vol - volavg) / volavg

    # work out return for the N days after the forecast

    norm_data = system.accounts.pandl_for_instrument_forecast(
        instrument_code, rule_name)

    (vol_qq, norm_data) = align_to_joint(
        vol_qq, norm_data, ffill=(True, False))

    period_returns = pd.rolling_sum(norm_data, return_period, min_periods=1)

    ex_post_returns = period_returns.shift(-return_period)
    lagged_vol = vol_qq.shift(1)

    return (list(ex_post_returns.iloc[:, 0].values), list(
        lagged_vol.iloc[:, 0].values))
示例#10
0
    def _get_fx_cross(self, currency1, currency2):
        """
        Get the FX rate between two currencies, using crosses with DEFAULT_CURRENCY if neccessary

        :param instrument_code: instrument to value for
        :type instrument_code: str

        :param base_currency: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame


        """

        # try and get from raw data
        fx_rate_series = self._get_fx_data(currency1, currency2)

        if fx_rate_series is None:
            # missing; have to get get cross rates
            default_currency = self._get_default_currency()
            currency1_vs_default = self._get_fx_data(
                currency1, default_currency).resample("1B", how="last")
            currency2_vs_default = self._get_fx_data(
                currency2, default_currency).resample("1B", how="last")

            fx_rate_series = divide_df_single_column(currency1_vs_default,
                                                     currency2_vs_default,
                                                     ffill=(True, True))

            fx_rate_series.columns = ['fx']

        return fx_rate_series
示例#11
0
    def _get_fx_cross(self, currency1, currency2):
        """
        Get the FX rate between two currencies, using crosses with DEFAULT_CURRENCY if neccessary

        :param instrument_code: instrument to value for
        :type instrument_code: str

        :param base_currency: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame


        """

        # try and get from raw data
        fx_rate_series = self._get_fx_data(currency1, currency2)

        if fx_rate_series is None:
            # missing; have to get get cross rates
            default_currency = self._get_default_currency()
            currency1_vs_default = self._get_fx_data(
                currency1, default_currency).resample("1B", how="last")
            currency2_vs_default = self._get_fx_data(
                currency2, default_currency).resample("1B", how="last")

            fx_rate_series=divide_df_single_column(currency1_vs_default, currency2_vs_default, ffill=(True, True))
            
            fx_rate_series.columns=['fx']

        return fx_rate_series
示例#12
0
 def _norm_returns(system, instrument_code, this_stage):
     returnvol = this_stage.daily_returns_volatility(
         instrument_code).shift(1)
     dailyreturns = this_stage.daily_returns(instrument_code)
     norm_return = divide_df_single_column(dailyreturns, returnvol)
     norm_return.columns = ["norm_return"]
     return norm_return
示例#13
0
文件: rawdata.py 项目: minhpascal/kod
        def _get_daily_percentage_volatility(
                system, instrument_code, this_stage):
            denom_price = this_stage.daily_denominator_price(instrument_code)
            return_vol = this_stage.daily_returns_volatility(instrument_code)
            perc_vol = 100.0 * \
                divide_df_single_column(return_vol, denom_price.shift(1))

            return perc_vol
示例#14
0
        def _calc_annualised_roll(system, instrument_code, this_subsystem):
            rolldiffs = this_subsystem.roll_differentials(instrument_code)
            rawrollvalues = this_subsystem.raw_futures_roll(instrument_code)

            annroll = divide_df_single_column(rawrollvalues, rolldiffs)
            annroll.columns = ['annualised_roll']

            return annroll
示例#15
0
        def _calc_annualised_roll(system, instrument_code, this_subsystem):
            rolldiffs = this_subsystem.roll_differentials(instrument_code)
            rawrollvalues = this_subsystem.raw_futures_roll(instrument_code)

            annroll = divide_df_single_column(rawrollvalues, rolldiffs)
            annroll.columns = ['annualised_roll']

            return annroll
示例#16
0
文件: rawdata.py 项目: Sayan-Paul/kod
        def _get_daily_percentage_volatility(
                system, instrument_code, this_stage):
            denom_price = this_stage.daily_denominator_price(instrument_code)
            return_vol = this_stage.daily_returns_volatility(instrument_code)
            perc_vol = 100.0 * \
                divide_df_single_column(return_vol, denom_price.shift(1))

            return perc_vol
示例#17
0
文件: rawdata.py 项目: Sayan-Paul/kod
        def _norm_returns(system, instrument_code, this_stage):
            this_stage.log.msg("Calculating normalised prices for %s" % instrument_code, instrument_code=instrument_code)

            returnvol = this_stage.daily_returns_volatility(
                instrument_code).shift(1)
            dailyreturns = this_stage.daily_returns(instrument_code)
            norm_return = divide_df_single_column(dailyreturns, returnvol)
            norm_return.columns = ["norm_return"]
            return norm_return
示例#18
0
文件: rawdata.py 项目: minhpascal/kod
        def _norm_returns(system, instrument_code, this_stage):
            this_stage.log.msg("Calculating normalised prices for %s" % instrument_code, instrument_code=instrument_code)

            returnvol = this_stage.daily_returns_volatility(
                instrument_code).shift(1)
            dailyreturns = this_stage.daily_returns(instrument_code)
            norm_return = divide_df_single_column(dailyreturns, returnvol)
            norm_return.columns = ["norm_return"]
            return norm_return
示例#19
0
     def _get_price_volatility(system, instrument_code):
         if hasattr(system, "rawdata"):
             daily_perc_vol = system.rawdata.get_daily_percentage_volatility(
                 instrument_code)
         else:
             price = system.data.get_instrument_price(instrument_code)
             price = price.resample("1B", how="last")
             return_vol = robust_vol_calc(price.diff())
             daily_perc_vol = 100.0 * divide_df_single_column(return_vol, price)
 
         return daily_perc_vol
示例#20
0
        def _get_price_volatility(system, instrument_code, this_stage_notused):
            if hasattr(system, "rawdata"):
                daily_perc_vol = system.rawdata.get_daily_percentage_volatility(
                    instrument_code)
            else:
                price = system.data.daily_prices(instrument_code)
                return_vol = robust_vol_calc(price.diff())
                daily_perc_vol = 100.0 * \
                    divide_df_single_column(return_vol, price)

            return daily_perc_vol
示例#21
0
def ewmac(price, vol, Lfast, Lslow):
    """
    Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback

    Assumes that 'price' is daily data

    This version recalculates the price volatility, and does not do capping or scaling

    :param price: The price or other series to use (assumed Tx1)
    :type price: pd.DataFrame

    :param vol: The daily price unit volatility (NOT % vol)
    :type vol: pd.DataFrame (assumed Tx1)

    :param Lfast: Lookback for fast in days
    :type Lfast: int

    :param Lslow: Lookback for slow in days
    :type Lslow: int

    :returns: pd.DataFrame -- unscaled, uncapped forecast


    >>> from systems.provided.example.testdata import get_test_object_futures
    >>> from systems.basesystem import System
    >>> (rawdata, data, config)=get_test_object_futures()
    >>> system=System( [rawdata], data, config)
    >>>
    >>> ewmac(rawdata.get_instrument_price("EDOLLAR"), rawdata.daily_returns_volatility("EDOLLAR"), 64, 256).tail(2)
                   price
    2015-04-21  6.623348
    2015-04-22  6.468900
    """
    # price: This is the stitched price series
    # We can't use the price of the contract we're trading, or the volatility will be jumpy
    # And we'll miss out on the rolldown. See
    # http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html

    # We don't need to calculate the decay parameter, just use the span
    # directly

    fast_ewma = pd.ewma(price, span=Lfast)
    slow_ewma = pd.ewma(price, span=Lslow)
    raw_ewmac = fast_ewma - slow_ewma

    return divide_df_single_column(raw_ewmac, vol, ffill=(False, True))
示例#22
0
def get_positions_from_forecasts(price, get_daily_returns_volatility, forecast,
                                 fx, value_of_price_point, capital,
                                 ann_risk_target, **kwargs):
    """
    Work out position using forecast, volatility, fx, value_of_price_point
    (this will be for an arbitrary daily risk target)

    If volatility is not provided, work out from price (uses a standard method
    so may differ from precise system p&l)

    :param price: price series
    :type price: Tx1 pd.DataFrame

    :param get_daily_returns_volatility: series of volatility estimates. NOT %
    volatility, price difference vol
    :type get_daily_returns_volatility: Tx1 pd.DataFrame  or None

    :param forecast: series of forecasts, needed to work out positions
    :type forecast: Tx1 pd.DataFrame

    :param fx: series of fx rates from instrument currency to base currency, to
    work out p&l in base currency
    :type fx: Tx1 pd.DataFrame

    :param value_of_price_point: value of one unit movement in price
    :type value_of_price_point: float

    **kwargs: passed to vol calculation

    :returns: Tx1 pd dataframe of positions

    """
    if forecast is None:
        raise Exception(
            "If you don't provide a series of trades or positions, I need a "
            "forecast")

    if get_daily_returns_volatility is None:
        get_daily_returns_volatility = robust_vol_calc(price.diff(), **kwargs)

    """
    Herein the proof why this position calculation is correct (see chapters
    5-11 of 'systematic trading' book)

    Position = forecast x instrument weight x instrument_div_mult x vol_scalar / 10.0
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x instr value volatility)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x instr ccy volatility x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x block value x % price volatility x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x underlying price x 0.01 x value of price move x 100 x price change volatility/(underlying price) x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x value of price move x price change volatility x fx rate)

    Making some arbitrary assumptions (one instrument, 100% of capital, daily target DAILY_CAPITAL):

             = forecast x 1.0 x 1.0 x DAILY_CAPITAL / (10.0 x value of price move x price diff volatility x fx rate)
             = forecast x  multiplier / (value of price move x price change volatility x fx rate)
    """
    (Unused_capital, daily_capital) = resolve_capital(forecast, capital, ann_risk_target)
    multiplier = daily_capital * 1.0 * 1.0 / 10.0
    fx = fx.reindex(get_daily_returns_volatility.index, method="ffill")

    denominator = (value_of_price_point *
                   multiply_df_single_column(get_daily_returns_volatility,
                                             fx,
                                             ffill=(False, True)))

    numerator = multiply_df_single_column(forecast, multiplier, ffill=(False,True))

    position = divide_df_single_column(numerator,
                                       denominator,
                                       ffill=(True, True))
    position.columns = ['position']
    return position
示例#23
0
def pandl(price=None, trades=None, marktomarket=True, positions=None,
          delayfill=True, roundpositions=False,
          get_daily_returns_volatility=None, forecast=None, fx=None,
          value_of_price_point=1.0, return_all=False, capital=None):
    """
    Calculate pandl for an individual position

    If marktomarket=True, and trades is provided, calculate pandl both at
    open/close and mark to market in between

    If trades is not provided, work out using positions. If delayfill is True,
           assume we get filled at the next price after the trade

           If roundpositions is True when working out trades from positions,
           then round; otherwise assume we trade fractional lots

        If positions are not provided, work out position using forecast and
        volatility (this will be for an arbitrary daily risk target)

        If volatility is not provided, work out from price

    If fx is not provided, assume fx rate is 1.0 and work out p&l in currency
        of instrument

    If value_of_price_point is not provided, assume is 1.0 (block size is value
    of 1 price point, eg 100 if you're buying 100 shares for one instrument
    block)

    If capital is provided (eithier as a float, or dataframe) then % returns
    will be calculated.

    If capital is zero will use default values

    :param price: price series
    :type price: Tx1 pd.DataFrame

    :param trades: set of trades done
    :type trades: Tx1 pd.DataFrame or None

    :param marktomarket: Should we mark to market, or just use traded prices?
    :type marktomarket: bool

    :param positions: series of positions
    :type positions: Tx1 pd.DataFrame  or None

    :param delayfill: If calculating trades, should we round positions first?
    :type delayfill: bool

    :param roundpositions: If calculating trades, should we round positions
        first?
    :type roundpositions: bool

    :param get_daily_returns_volatility: series of volatility estimates, used
        for calculation positions
    :type get_daily_returns_volatility: Tx1 pd.DataFrame  or None

    :param forecast: series of forecasts, needed to work out positions
    :type forecast: Tx1 pd.DataFrame  or None

    :param fx: series of fx rates from instrument currency to base currency, to
        work out p&l in base currency
    :type fx: Tx1 pd.DataFrame  or None

    :param value_of_price_point: value of one unit movement in price
    :type value_of_price_point: float

    :param roundpositions: If calculating trades, should we round positions first?
    :type roundpositions: bool

    :param capital: notional capital. If None not used. Works out % returns. If
        0.0 uses default
    :type capital: None, 0.0, float or Tx1 timeseries

    :returns: if return_all : 4- Tuple (positions, trades, instr_ccy_returns,
                            base_ccy_returns) all Tx1 pd.DataFrames is "": Tx1
                            accountCurve

    """
    if price is None:
        raise Exception("Can't work p&l without price")

    if fx is None:
        # assume it's 1.0
        fx = pd.Series([1.0] * len(price.index),
                       index=price.index).to_frame("fx")

    if trades is None:
        trades = get_trades_from_positions(price,
                                           positions,
                                           delayfill,
                                           roundpositions,
                                           get_daily_returns_volatility,
                                           forecast,
                                           fx,
                                           value_of_price_point)

    if marktomarket:
        # want to have both kinds of price
        prices_to_use = pd.concat(
            [price, trades.fill_price], axis=1, join='outer')

        # Where no fill price available, use price
        prices_to_use = prices_to_use.fillna(axis=1, method="ffill")

        prices_to_use = prices_to_use.fill_price.to_frame("price")

        # alight trades

        trades_to_use = trades.reindex(
            prices_to_use.index, fill_value=0.0).trades.to_frame("trades")

    else:
        # only calculate p&l on trades, using fills
        trades_to_use = trades.trades.to_frame("trades")
        prices_to_use = trades.fill_price.to_frame("price").ffill()

    cum_trades = trades_to_use.cumsum().ffill()
    price_returns = prices_to_use.diff()

    instr_ccy_returns = multiply_df_single_column(
        cum_trades.shift(1), price_returns) * value_of_price_point
    fx = fx.reindex(trades_to_use.index, method="ffill")
    base_ccy_returns = multiply_df_single_column(instr_ccy_returns, fx)

    instr_ccy_returns.columns = ["pandl_ccy"]
    base_ccy_returns.columns = ["pandl_base"]
    cum_trades.columns = ["cum_trades"]

    if return_all:
        return (cum_trades, trades, instr_ccy_returns,
                base_ccy_returns, capital)
    else:
        if capital is not None:
            if isinstance(capital, float):
                if capital == 0.0:
                    # use default. Good for forecasts when no meaningful
                    # capital
                    capital = CAPITAL
                base_ccy_returns = base_ccy_returns / capital
            else:
                # time series
                capital = capital.reindex(
                    base_ccy_returns.index, method="ffill")
                base_ccy_returns = divide_df_single_column(
                    base_ccy_returns, capital)

        return accountCurve(base_ccy_returns)
示例#24
0
def pandl(price=None,
          trades=None,
          marktomarket=True,
          positions=None,
          delayfill=True,
          roundpositions=False,
          get_daily_returns_volatility=None,
          forecast=None,
          fx=None,
          value_of_price_point=1.0,
          return_all=False,
          capital=None):
    """
    Calculate pandl for an individual position

    If marktomarket=True, and trades is provided, calculate pandl both at
    open/close and mark to market in between

    If trades is not provided, work out using positions. If delayfill is True,
           assume we get filled at the next price after the trade

           If roundpositions is True when working out trades from positions,
           then round; otherwise assume we trade fractional lots

        If positions are not provided, work out position using forecast and
        volatility (this will be for an arbitrary daily risk target)

        If volatility is not provided, work out from price

    If fx is not provided, assume fx rate is 1.0 and work out p&l in currency
        of instrument

    If value_of_price_point is not provided, assume is 1.0 (block size is value
    of 1 price point, eg 100 if you're buying 100 shares for one instrument
    block)

    If capital is provided (eithier as a float, or dataframe) then % returns
    will be calculated.

    If capital is zero will use default values

    :param price: price series
    :type price: Tx1 pd.DataFrame

    :param trades: set of trades done
    :type trades: Tx1 pd.DataFrame or None

    :param marktomarket: Should we mark to market, or just use traded prices?
    :type marktomarket: bool

    :param positions: series of positions
    :type positions: Tx1 pd.DataFrame  or None

    :param delayfill: If calculating trades, should we round positions first?
    :type delayfill: bool

    :param roundpositions: If calculating trades, should we round positions
        first?
    :type roundpositions: bool

    :param get_daily_returns_volatility: series of volatility estimates, used
        for calculation positions
    :type get_daily_returns_volatility: Tx1 pd.DataFrame  or None

    :param forecast: series of forecasts, needed to work out positions
    :type forecast: Tx1 pd.DataFrame  or None

    :param fx: series of fx rates from instrument currency to base currency, to
        work out p&l in base currency
    :type fx: Tx1 pd.DataFrame  or None

    :param value_of_price_point: value of one unit movement in price
    :type value_of_price_point: float

    :param roundpositions: If calculating trades, should we round positions first?
    :type roundpositions: bool

    :param capital: notional capital. If None not used. Works out % returns. If
        0.0 uses default
    :type capital: None, 0.0, float or Tx1 timeseries

    :returns: if return_all : 4- Tuple (positions, trades, instr_ccy_returns,
                            base_ccy_returns) all Tx1 pd.DataFrames is "": Tx1
                            accountCurve

    """
    if price is None:
        raise Exception("Can't work p&l without price")

    if fx is None:
        # assume it's 1.0
        fx = pd.Series([1.0] * len(price.index),
                       index=price.index).to_frame("fx")

    if trades is None:
        trades = get_trades_from_positions(price, positions, delayfill,
                                           roundpositions,
                                           get_daily_returns_volatility,
                                           forecast, fx, value_of_price_point)

    if marktomarket:
        # want to have both kinds of price
        prices_to_use = pd.concat([price, trades.fill_price],
                                  axis=1,
                                  join='outer')

        # Where no fill price available, use price
        prices_to_use = prices_to_use.fillna(axis=1, method="ffill")

        prices_to_use = prices_to_use.fill_price.to_frame("price")

        # alight trades

        trades_to_use = trades.reindex(
            prices_to_use.index, fill_value=0.0).trades.to_frame("trades")

    else:
        # only calculate p&l on trades, using fills
        trades_to_use = trades.trades.to_frame("trades")
        prices_to_use = trades.fill_price.to_frame("price").ffill()

    cum_trades = trades_to_use.cumsum().ffill()
    price_returns = prices_to_use.diff()

    instr_ccy_returns = multiply_df_single_column(
        cum_trades.shift(1), price_returns) * value_of_price_point
    fx = fx.reindex(trades_to_use.index, method="ffill")
    base_ccy_returns = multiply_df_single_column(instr_ccy_returns, fx)

    instr_ccy_returns.columns = ["pandl_ccy"]
    base_ccy_returns.columns = ["pandl_base"]
    cum_trades.columns = ["cum_trades"]

    if return_all:
        return (cum_trades, trades, instr_ccy_returns, base_ccy_returns,
                capital)
    else:
        if capital is not None:
            if isinstance(capital, float):
                if capital == 0.0:
                    # use default. Good for forecasts when no meaningful
                    # capital
                    capital = CAPITAL
                base_ccy_returns = base_ccy_returns / capital
            else:
                # time series
                capital = capital.reindex(base_ccy_returns.index,
                                          method="ffill")
                base_ccy_returns = divide_df_single_column(
                    base_ccy_returns, capital)

        return accountCurve(base_ccy_returns)
示例#25
0
    def __init__(self, price,  percentage=False, cost_per_block=None, SR_cost=None, 
                 capital=None, ann_risk_target=None, 
                 **kwargs):
        """
        Create an account curve; from which many lovely statistics can be gathered
        
        
        We create by passing **kwargs which will be used by the pandl function
        
        :param percentage: Return % returns, or base currency if False
        :type percentage: bool
        
        :param cost_per_block: Cost in local currency units per instrument block 
        :type cost_per_block: float
        
        :param SR_cost: Cost in annualised Sharpe Ratio units (0.01 = 0.01 SR)
        :type SR_cost: float
        
        Note if both are included then cost_per_block will be disregarded
        
        :param capital: Capital at risk. Used for % returns, and calculating daily risk for SR costs  
        :type capital: float or Tx1 
        
        :param ann_risk_target: Annual risk target, as % of capital. Used to calculate daily risk for SR costs
        :type ann_risk_target: float
        
        **kwargs  passed to profit and loss calculation
         (price, trades, marktomarket, positions,
          delayfill, roundpositions,
          get_daily_returns_volatility, forecast, fx,
          value_of_price_point)
        
        """

        returns_data=pandl_with_data(price,  capital=capital, ann_risk_target=ann_risk_target, **kwargs)

        (cum_trades, trades, instr_ccy_returns,
            base_ccy_returns, fx)=returns_data
            
        gross_returns=base_ccy_returns

        ## always returns a time series
        (capital, daily_capital)=resolve_capital(gross_returns, capital, ann_risk_target)

        (costs_base_ccy, costs_instr_ccy)=calc_costs(returns_data, cost_per_block, SR_cost, daily_capital)

        net_returns=add_df_single_column(gross_returns,costs_base_ccy) ## costs are negative returns
        
        perc_gross_returns = divide_df_single_column(
            gross_returns, capital)
        
        perc_costs=divide_df_single_column(
            costs_base_ccy, capital)

        perc_net_returns=add_df_single_column(perc_gross_returns, perc_costs)

        if percentage:
            super().__init__(perc_gross_returns, perc_net_returns, perc_costs)
        else:
            super().__init__(gross_returns, net_returns, costs_base_ccy)
            
        setattr(self, "cum_trades", cum_trades)
        setattr(self, "trades", trades)
        setattr(self, "instr_ccy_returns", instr_ccy_returns)
        setattr(self, "base_ccy_returns", base_ccy_returns)
        setattr(self, "fx", fx)
        
        setattr(self, "capital", capital)
        setattr(self, "daily_capital", daily_capital)
        
        setattr(self, "costs_instr_ccy", costs_instr_ccy)
        setattr(self, "costs_base_ccy", costs_base_ccy)
            
        setattr(self, "ccy_returns", 
                accountCurveSingle(gross_returns, net_returns, costs_base_ccy))
        setattr(self, "perc_returns", 
                accountCurveSingle(perc_gross_returns, perc_net_returns, perc_costs))
示例#26
0
def get_positions_from_forecasts(price, get_daily_returns_volatility, forecast,
                                 fx, value_of_price_point, **kwargs):
    """
    Work out position using forecast, volatility, fx, value_of_price_point
    (this will be for an arbitrary daily risk target)

    If volatility is not provided, work out from price (uses a standard method
    so may differ from precise system p&l)

    :param price: price series
    :type price: Tx1 pd.DataFrame

    :param get_daily_returns_volatility: series of volatility estimates. NOT %
    volatility, price difference vol
    :type get_daily_returns_volatility: Tx1 pd.DataFrame  or None

    :param forecast: series of forecasts, needed to work out positions
    :type forecast: Tx1 pd.DataFrame

    :param fx: series of fx rates from instrument currency to base currency, to
    work out p&l in base currency
    :type fx: Tx1 pd.DataFrame

    :param value_of_price_point: value of one unit movement in price
    :type value_of_price_point: float

    **kwargs: passed to vol calculation

    :returns: Tx1 pd dataframe of positions

    """
    if forecast is None:
        raise Exception(
            "If you don't provide a series of trades or positions, I need a "
            "forecast")

    if get_daily_returns_volatility is None:
        get_daily_returns_volatility = robust_vol_calc(price.diff(), **kwargs)
    """
    Herein the proof why this position calculation is correct (see chapters
    5-11 of 'systematic trading' book)

    Position = forecast x instrument weight x instrument_div_mult x vol_scalar / 10.0
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x instr value volatility)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x instr ccy volatility x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x block value x % price volatility x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x underlying price x 0.01 x value of price move x 100 x price diff volatility/(underlying price) x fx rate)
             = forecast x instrument weight x instrument_div_mult x daily cash vol target / (10.0 x value of price move x price change volatility x fx rate)

    Making some arbitrary assumptions (one instrument, 100% of capital, daily target DAILY_CAPITAL):

             = forecast x 1.0 x 1.0 x DAILY_CAPITAL / (10.0 x value of price move x price diff volatility x fx rate)
             = forecast x  multiplier / (value of price move x price change volatility x fx rate)
    """
    multiplier = DAILY_CAPITAL * 1.0 * 1.0 / 10.0
    fx = fx.reindex(get_daily_returns_volatility.index, method="ffill")

    denominator = (value_of_price_point * multiply_df_single_column(
        get_daily_returns_volatility, fx, ffill=(False, True)))

    position = divide_df_single_column(forecast * multiplier,
                                       denominator,
                                       ffill=(True, True))
    position.columns = ['position']
    return position