class_docstr: Constructs continuous forwards time series total return indices from underlying forwards contracts.
  
      
class_docstr_tok: ["Constructs","continuous","forwards","time","series","total","return","indices","from","underlying","forwards","contracts","."]
code: def construct_total_return_index(
      self,
      cross_fx,
      forwards_market_df,
      fx_forwards_trading_tenor=None,
      roll_days_before=None,
      roll_event=None,
      roll_months=None,
      fx_forwards_tenor_for_interpolation=None,
      cum_index=None,
      output_calculation_fields=None,
      field=None,
  ):
      if not (isinstance(cross_fx, list)):
          cross_fx = [cross_fx]
  
      if fx_forwards_trading_tenor is None:
          fx_forwards_trading_tenor = self._fx_forwards_trading_tenor
      if roll_days_before is None:
          roll_days_before = self._roll_days_before
      if roll_event is None:
          roll_event = self._roll_event
      if roll_months is None:
          roll_months = self._roll_months
      if fx_forwards_tenor_for_interpolation is None:
          fx_forwards_tenor_for_interpolation = (
              self._fx_forwards_tenor_for_interpolation
          )
      if cum_index is None:
          cum_index = self._cum_index
      if field is None:
          field = self._field
  
      total_return_index_df_agg = []
  
      # Remove columns where there is no data (because these points typically aren't quoted)
      forwards_market_df = forwards_market_df.dropna(how="all", axis=1)
  
      fx_forwards_pricer = FXForwardsPricer()
  
      def get_roll_date(horizon_d, delivery_d, asset_hols, month_adj=1):
          if roll_event == "month-end":
              roll_d = horizon_d + CustomBusinessMonthEnd(
                  roll_months + month_adj, holidays=asset_hols
              )
          elif roll_event == "delivery-date":
              roll_d = delivery_d
  
          return roll_d - CustomBusinessDay(
              n=roll_days_before, holidays=asset_hols
          )
  
      for cross in cross_fx:
          # Eg. if we specify USDUSD
          if cross[0:3] == cross[3:6]:
              total_return_index_df_agg.append(
                  pd.DataFrame(
                      100,
                      index=forwards_market_df.index,
                      columns=[cross + "-forward-tot.close"],
                  )
              )
          else:
              # Is the FX cross in the correct convention
              old_cross = cross
              cross = FXConv().correct_notation(cross)
  
              horizon_date = forwards_market_df.index
  
              delivery_date = []
              roll_date = []
  
              new_trade = np.full(len(horizon_date), False, dtype=bool)
  
              asset_holidays = self._calendar.get_holidays(cal=cross)
  
              # Get first delivery date
              delivery_date.append(
                  self._calendar.get_delivery_date_from_horizon_date(
                      horizon_date[0],
                      fx_forwards_trading_tenor,
                      cal=cross,
                      asset_class="fx",
                  )[0]
              )
  
              # For first month want it to expire within that month (for consistency), hence month_adj=0 ONLY here
              roll_date.append(
                  get_roll_date(
                      horizon_date[0],
                      delivery_date[0],
                      asset_holidays,
                      month_adj=0,
                  )
              )
  
              # New trade => entry at beginning AND on every roll
              new_trade[0] = True
  
              # Get all the delivery dates and roll dates
              # At each "roll/trade" day we need to reset them for the new contract
              for i in range(1, len(horizon_date)):
                  # If the horizon date has reached the roll date (from yesterday), we're done, and we have a
                  # new roll/trade
                  if (horizon_date[i] - roll_date[i - 1]).days == 0:
                      new_trade[i] = True
                  # else:
                  #    new_trade[i] = False
  
                  # If we're entering a new trade/contract, we need to get new delivery and roll dates
                  if new_trade[i]:
                      delivery_date.append(
                          self._calendar.get_delivery_date_from_horizon_date(
                              horizon_date[i],
                              fx_forwards_trading_tenor,
                              cal=cross,
                              asset_class="fx",
                          )[
                              0
                          ]
                      )
  
                      roll_date.append(
                          get_roll_date(
                              horizon_date[i],
                              delivery_date[i],
                              asset_holidays,
                          )
                      )
                  else:
                      # Otherwise use previous delivery and roll dates, because we're still holding same contract
                      delivery_date.append(delivery_date[i - 1])
                      roll_date.append(roll_date[i - 1])
  
              interpolated_forward = fx_forwards_pricer.price_instrument(
                  cross,
                  horizon_date,
                  delivery_date,
                  market_df=forwards_market_df,
                  fx_forwards_tenor_for_interpolation=fx_forwards_tenor_for_interpolation,
              )[
                  cross + "-interpolated-outright-forward." + field
              ].values
  
              # To record MTM prices
              mtm = np.copy(interpolated_forward)
  
              # Note: may need to add discount factor when marking to market forwards?
  
              # Special case: for very first trading day
              # mtm[0] = interpolated_forward[0]
  
              # On rolling dates, MTM will be the previous forward contract (interpolated)
              # otherwise it will be the current forward contract
              for i in range(1, len(horizon_date)):
                  if new_trade[i]:
                      mtm[i] = fx_forwards_pricer.price_instrument(
                          cross,
                          horizon_date[i],
                          delivery_date[i - 1],
                          market_df=forwards_market_df,
                          fx_forwards_tenor_for_interpolation=fx_forwards_tenor_for_interpolation,
                      )[
                          cross
                          + "-interpolated-outright-forward."
                          + field
                      ].values
                  # else:
                  #    mtm[i] = interpolated_forward[i]
  
              # Eg. if we asked for USDEUR, we first constructed spot/forwards for EURUSD
              # and then need to invert it
              if old_cross != cross:
                  mtm = 1.0 / mtm
                  interpolated_forward = 1.0 / interpolated_forward
  
              forward_rets = (
                  mtm / np.roll(interpolated_forward, 1) - 1.0
              )
              forward_rets[0] = 0
  
              if cum_index == "mult":
                  cum_rets = 100 * np.cumprod(1.0 + forward_rets)
              elif cum_index == "add":
                  cum_rets = 100 + 100 * np.cumsum(forward_rets)
  
              total_return_index_df = pd.DataFrame(
                  index=horizon_date,
                  columns=[cross + "-forward-tot." + field],
              )
              total_return_index_df[
                  cross + "-forward-tot." + field
              ] = cum_rets
  
              if output_calculation_fields:
                  total_return_index_df[
                      cross + "-interpolated-outright-forward." + field
                  ] = interpolated_forward
                  total_return_index_df[cross + "-mtm.close"] = mtm
                  total_return_index_df[
                      cross + "-roll.close"
                  ] = new_trade
                  total_return_index_df[
                      cross + ".roll-date"
                  ] = roll_date
                  total_return_index_df[
                      cross + ".delivery-date"
                  ] = delivery_date
                  total_return_index_df[
                      cross + "-forward-return." + field
                  ] = forward_rets
  
              total_return_index_df_agg.append(total_return_index_df)
  
      return self._calculations.join(
          total_return_index_df_agg, how="outer"
      )
  
file_path: finmarketpy/finmarketpy/curve/fxforwardscurve.py
imports: import numpy
  import pandas
  from pandas.tseries.offsets import CustomBusinessDay, CustomBusinessMonthEnd
  from findatapy.market import Market, MarketDataRequest
  from findatapy.timeseries import Calculations, Calendar, Filter
  from findatapy.util.dataconstants import DataConstants
  from findatapy.util.fxconv import FXConv
  from finmarketpy.curve.rates.fxforwardspricer import FXForwardsPricer
  from finmarketpy.util.marketconstants import MarketConstants
label_desc: Use image node_id 7 for calling the FXForwardsCurve obj's underlying member method code with example usage: obj.construct_total_return_index(cross_fx, forwards_market_df, fx_forwards_trading_tenor, roll_days_before, roll_event, roll_months, fx_forwards_tenor_for_interpolation, cum_index, output_calculation_fields, field) and returns: self, unknown