示例#1
0
        def _get_forecast_method_buffer(system, instrument_code, this_stage):
            
            this_stage.log.msg("Calculating forecast method buffers for %s" % instrument_code,
                               instrument_code=instrument_code)
            
            buffer_size=system.config.buffer_size
            
            idm = this_stage.get_instrument_diversification_multiplier()
            instr_weights = this_stage.get_instrument_weights()
            vol_scalar = this_stage.get_volatility_scalar(
                instrument_code)

            inst_weight_this_code = instr_weights[
                instrument_code].to_frame("weight")

            inst_weight_this_code = inst_weight_this_code.reindex(
                vol_scalar.index).ffill()
            idm = idm.reindex(vol_scalar.index).ffill()

            multiplier = multiply_df_single_column(inst_weight_this_code, idm)
            average_position = multiply_df_single_column(
                vol_scalar, multiplier)
            
            buffer = average_position * buffer_size
            
            buffer.columns=["buffer"]

            return buffer
示例#2
0
        def _get_forecast_method_buffer(system, instrument_code, this_stage):

            this_stage.log.msg("Calculating forecast method buffers for %s" %
                               instrument_code,
                               instrument_code=instrument_code)

            buffer_size = system.config.buffer_size

            idm = this_stage.get_instrument_diversification_multiplier()
            instr_weights = this_stage.get_instrument_weights()
            vol_scalar = this_stage.get_volatility_scalar(instrument_code)

            inst_weight_this_code = instr_weights[instrument_code].to_frame(
                "weight")

            inst_weight_this_code = inst_weight_this_code.reindex(
                vol_scalar.index).ffill()
            idm = idm.reindex(vol_scalar.index).ffill()

            multiplier = multiply_df_single_column(inst_weight_this_code, idm)
            average_position = multiply_df_single_column(
                vol_scalar, multiplier)

            buffer = average_position * buffer_size

            buffer.columns = ["buffer"]

            return buffer
示例#3
0
    def get_instrument_scaling_factor(self, instrument_code):
        
        """
        Get instrument weight * IDM
        
        The number we multiply subsystem by to get position
        
        Used to calculate SR costs
        
        :param instrument_code: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame
        
        """
        
        idm = self.get_instrument_diversification_multiplier()
        instr_weights = self.get_instrument_weights()

        inst_weight_this_code = instr_weights[
            instrument_code].to_frame("weight")

        multiplier = multiply_df_single_column(inst_weight_this_code, idm, ffill=(True, True))

        return multiplier
示例#4
0
    def get_instrument_scaling_factor(self, instrument_code):
        """
        Get instrument weight * IDM
        
        The number we multiply subsystem by to get position
        
        Used to calculate SR costs
        
        :param instrument_code: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame
        
        """

        idm = self.get_instrument_diversification_multiplier()
        instr_weights = self.get_instrument_weights()

        inst_weight_this_code = instr_weights[instrument_code].to_frame(
            "weight")

        multiplier = multiply_df_single_column(inst_weight_this_code,
                                               idm,
                                               ffill=(True, True))

        return multiplier
示例#5
0
        def _get_instrument_currency_vol(system, instrument_code, this_stage):

            block_value = this_stage.get_block_value(instrument_code)
            daily_perc_vol = this_stage.get_price_volatility(instrument_code)

            instr_ccy_vol = multiply_df_single_column(
                block_value, daily_perc_vol, ffill=(True, False))
            instr_ccy_vol.columns = ['icv']

            return instr_ccy_vol
示例#6
0
        def _get_notional_position(system, instrument_code, this_stage):
            idm = this_stage.get_instrument_diversification_multiplier()
            instr_weights = this_stage.get_instrument_weights()
            subsys_position = this_stage.get_subsystem_position(
                instrument_code)

            inst_weight_this_code = instr_weights[
                instrument_code].to_frame("weight")

            inst_weight_this_code = inst_weight_this_code.reindex(
                subsys_position.index).ffill()
            idm = idm.reindex(subsys_position.index).ffill()

            multiplier = multiply_df_single_column(inst_weight_this_code, idm)
            notional_position = multiply_df_single_column(
                subsys_position, multiplier)
            notional_position.columns = ['pos']

            return notional_position
示例#7
0
        def _instrument_turnover(
                system, instrument_code,  this_stage, roundpositions):

            average_position_for_turnover=multiply_df_single_column( this_stage.get_volatility_scalar(instrument_code), 
                                                                     this_stage.get_instrument_scaling_factor(instrument_code),
                                                                     ffill=(True, True))
            
            positions = this_stage.get_buffered_position(instrument_code, roundpositions = roundpositions)
            
            return turnover(positions, average_position_for_turnover)
示例#8
0
        def _get_instrument_value_vol(system, instrument_code, this_stage):

            instr_ccy_vol = this_stage.get_instrument_currency_vol(
                instrument_code)
            fx_rate = this_stage.get_fx_rate(instrument_code)

            instr_value_vol = multiply_df_single_column(
                instr_ccy_vol, fx_rate, ffill=(False, True))
            instr_value_vol.columns = ['ivv']

            return instr_value_vol
示例#9
0
        def _instrument_turnover(system, instrument_code, this_stage,
                                 roundpositions):

            average_position_for_turnover = multiply_df_single_column(
                this_stage.get_volatility_scalar(instrument_code),
                this_stage.get_instrument_scaling_factor(instrument_code),
                ffill=(True, True))

            positions = this_stage.get_buffered_position(
                instrument_code, roundpositions=roundpositions)

            return turnover(positions, average_position_for_turnover)
示例#10
0
        def _get_instrument_currency_vol(system, instrument_code, this_stage):

            this_stage.log.msg("Calculating instrument currency vol for %s" % instrument_code,
                               instrument_code=instrument_code)

            block_value = this_stage.get_block_value(instrument_code)
            daily_perc_vol = this_stage.get_price_volatility(instrument_code)

            instr_ccy_vol = multiply_df_single_column(
                block_value, daily_perc_vol, ffill=(True, False))
            instr_ccy_vol.columns = ['icv']

            return instr_ccy_vol
示例#11
0
        def _get_instrument_value_vol(system, instrument_code, this_stage):

            this_stage.log.msg("Calculating instrument value vol for %s" % instrument_code,
                               instrument_code=instrument_code)

            instr_ccy_vol = this_stage.get_instrument_currency_vol(
                instrument_code)
            fx_rate = this_stage.get_fx_rate(instrument_code)

            instr_value_vol = multiply_df_single_column(
                instr_ccy_vol, fx_rate, ffill=(False, True))
            instr_value_vol.columns = ['ivv']

            return instr_value_vol
示例#12
0
        def _get_subsystem_position(system, instrument_code, this_stage):
            """
            We don't allow this to be changed in config
            """
            avg_abs_forecast = system_defaults['average_absolute_forecast']

            vol_scalar = this_stage.get_volatility_scalar(instrument_code)
            forecast = this_stage.get_combined_forecast(instrument_code)

            subsystem_position = multiply_df_single_column(
                vol_scalar, forecast, ffill=(True, False)) / avg_abs_forecast
            subsystem_position.columns = ['ss_position']

            return subsystem_position
示例#13
0
        def _get_notional_position(system, instrument_code, this_stage):

            this_stage.log.msg("Calculating notional position for %s" %
                               instrument_code,
                               instrument_code=instrument_code)

            idm = this_stage.get_instrument_diversification_multiplier()
            instr_weights = this_stage.get_instrument_weights()
            subsys_position = this_stage.get_subsystem_position(
                instrument_code)

            inst_weight_this_code = instr_weights[instrument_code].to_frame(
                "weight")

            inst_weight_this_code = inst_weight_this_code.reindex(
                subsys_position.index).ffill()
            idm = idm.reindex(subsys_position.index).ffill()

            multiplier = multiply_df_single_column(inst_weight_this_code, idm)
            notional_position = multiply_df_single_column(
                subsys_position, multiplier)
            notional_position.columns = ['pos']

            return notional_position
        def _get_scaled_forecast(
                system, instrument_code, rule_variation_name, this_stage):
            

            raw_forecast = this_stage.get_raw_forecast(
                instrument_code, rule_variation_name)
            scale = this_stage.get_forecast_scalar(
                instrument_code, rule_variation_name)

            if type(scale) is float:
                scaled_forecast = raw_forecast * scale
            else: 
                ## time series
                scaled_forecast = multiply_df_single_column(raw_forecast, scale, ffill=(False,True))

            return scaled_forecast
示例#15
0
        def _get_scaled_forecast(system, instrument_code, rule_variation_name,
                                 this_stage):

            raw_forecast = this_stage.get_raw_forecast(instrument_code,
                                                       rule_variation_name)
            scale = this_stage.get_forecast_scalar(instrument_code,
                                                   rule_variation_name)

            if type(scale) is float:
                scaled_forecast = raw_forecast * scale
            else:
                ## time series
                scaled_forecast = multiply_df_single_column(raw_forecast,
                                                            scale,
                                                            ffill=(False,
                                                                   True))

            return scaled_forecast
示例#16
0
        def _get_subsystem_position(system, instrument_code, this_stage):

            this_stage.log.msg("Calculating subsystem position for %s" % instrument_code,
                               instrument_code=instrument_code)

            """
            We don't allow this to be changed in config
            """
            avg_abs_forecast = system_defaults['average_absolute_forecast']

            vol_scalar = this_stage.get_volatility_scalar(instrument_code)
            forecast = this_stage.get_combined_forecast(instrument_code)

            subsystem_position = multiply_df_single_column(
                vol_scalar, forecast, ffill=(True, False)) / avg_abs_forecast
            subsystem_position.columns = ['ss_position']

            return subsystem_position
示例#17
0
def calc_costs(returns_data, cost_per_block, SR_cost, daily_capital):
    """
    Calculate costs
    
    :param returns_data: returns data
    :type returns_data: 4 tuple returned by pandl data function
    
    :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

    If both included use SR_cost
    
    :param daily_capital: Capital at risk each day. Used for SR calculations
    :type daily_capital: Tx1 pd.DataFrame
    
    :returns : Tx1 pd.DataFrame of costs. Minus numbers are losses
    
    """

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

    if SR_cost is not None:
        ## use SR_cost
        ann_risk = daily_capital*ROOT_BDAYS_INYEAR
        ann_cost = -SR_cost*ann_risk
        costs_instr_ccy = ann_cost/BUSINESS_DAYS_IN_YEAR
    
    elif cost_per_block is not None:
        ## use cost per blocks
        trades_in_blocks=trades['trades'].abs().resample("1B", how="sum")
        costs_instr_ccy= - trades_in_blocks*cost_per_block
        
    else:
        ## set costs to zero
        costs_instr_ccy=pd.DataFrame([0.0]*base_ccy_returns.shape[0], index=base_ccy_returns.index)
    
    costs_base_ccy=multiply_df_single_column(costs_instr_ccy, fx, ffill=(False, True))

    return (costs_base_ccy, costs_instr_ccy)
示例#18
0
    def get_forecast_scaling_factor(self, instrument_code, rule_variation_name):
        """
        Get forecast weight * FDM
        
        :param instrument_code: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame
        
        """
        
        fdm = self.get_forecast_diversification_multiplier(instrument_code)
        forecast_weights = self.get_forecast_weights(instrument_code)

        fcast_weight_this_code = forecast_weights[
            rule_variation_name].to_frame("weight")

        multiplier = multiply_df_single_column(fcast_weight_this_code, fdm, ffill=(True, True))

        return multiplier
示例#19
0
    def get_forecast_scaling_factor(self, instrument_code,
                                    rule_variation_name):
        """
        Get forecast weight * FDM
        
        :param instrument_code: instrument to value for
        :type instrument_code: str

        :returns: Tx1 pd.DataFrame
        
        """

        fdm = self.get_forecast_diversification_multiplier(instrument_code)
        forecast_weights = self.get_forecast_weights(instrument_code)

        fcast_weight_this_code = forecast_weights[
            rule_variation_name].to_frame("weight")

        multiplier = multiply_df_single_column(fcast_weight_this_code,
                                               fdm,
                                               ffill=(True, True))

        return multiplier
示例#20
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
示例#21
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
示例#22
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)
示例#23
0
def pandl_with_data(price, trades=None, marktomarket=True, positions=None,
          delayfill=True, roundpositions=False,
          get_daily_returns_volatility=None, forecast=None, fx=None,
          capital=None, ann_risk_target=None,
          value_of_price_point=1.0):
    
    """
    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)

    :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

    :returns: 5- Tuple (positions, trades, instr_ccy_returns,
                            base_ccy_returns, fx) all Tx1 pd.DataFrames

    """
    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,
                                           capital,
                                           ann_risk_target)

    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
    
    instr_ccy_returns=instr_ccy_returns.resample("1B", how="sum")
        
    fx = fx.reindex(instr_ccy_returns.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"]

    return (cum_trades, trades, instr_ccy_returns,
            base_ccy_returns, fx)