Skip to content

Cliquet

This module contains examples of equity cliquet contracts.

Accumulator dataclass

Bases: EventsMixin

In an Accumulator the asset returns are calculated over a series of consecutive periods. Each return is subject to a local floor and cap, then accumulated by adding, and finally the accumulated payoff is subject to a global floor.

Parameters:

Name Type Description Default
ccy str

the currency of the bond.

required
asset_name str

the name of the underlying asset.

required
fix_dates List[datetime]

the fixing times of the cliquet.

required
global_floor float

the global floor of the cliquet.

required
local_floor float

the local floor of the cliquet.

required
local_cap float

the local cap of the cliquet.

required
notional float

the notional of the cliquet.

100.0
track str

an optional identifier for the contract.

''

Examples:

>>> fix_dates = pd.bdate_range(datetime(2021, 12, 31), datetime(2024, 12, 31), freq="2BQE")
>>> Accumulator("USD", "SPX", fix_dates, 0.0, -0.03, 0.05).print_events()
      time  op  quantity   unit track
12/31/2021 NaN       0.0  start   NaN
06/30/2022 NaN       0.0 addfix   NaN
12/30/2022 NaN       0.0 addfix   NaN
06/30/2023 NaN       0.0 addfix   NaN
12/29/2023 NaN       0.0 addfix   NaN
06/28/2024 NaN       0.0 addfix   NaN
12/31/2024 NaN       0.0 addfix   NaN
12/31/2024   >       0.0    USD
12/31/2024   +     100.0    ACC
Source code in qablet_contracts\eq\cliquet.py
@dataclass
class Accumulator(EventsMixin):
    """In an **Accumulator** the asset returns are calculated over a series of consecutive periods.
    Each return is subject to a local floor and cap, then accumulated by adding, and finally the
    accumulated payoff is subject to a global floor.

    Args:
        ccy: the currency of the bond.
        asset_name: the name of the underlying asset.
        fix_dates: the fixing times of the cliquet.
        global_floor: the global floor of the cliquet.
        local_floor: the local floor of the cliquet.
        local_cap: the local cap of the cliquet.
        notional: the notional of the cliquet.
        track: an optional identifier for the contract.

    Examples:
        >>> fix_dates = pd.bdate_range(datetime(2021, 12, 31), datetime(2024, 12, 31), freq="2BQE")
        >>> Accumulator("USD", "SPX", fix_dates, 0.0, -0.03, 0.05).print_events()
              time  op  quantity   unit track
        12/31/2021 NaN       0.0  start   NaN
        06/30/2022 NaN       0.0 addfix   NaN
        12/30/2022 NaN       0.0 addfix   NaN
        06/30/2023 NaN       0.0 addfix   NaN
        12/29/2023 NaN       0.0 addfix   NaN
        06/28/2024 NaN       0.0 addfix   NaN
        12/31/2024 NaN       0.0 addfix   NaN
        12/31/2024   >       0.0    USD
        12/31/2024   +     100.0    ACC
    """

    ccy: str
    asset_name: str
    fix_dates: List[datetime]
    global_floor: float
    local_floor: float
    local_cap: float
    notional: float = 100.0
    track: str = ""
    state: dict = field(default_factory=dict)

    def events(self):
        maturity = self.fix_dates[-1]

        events = [
            {
                "track": None,
                "time": self.fix_dates[0],
                "op": None,
                "quantity": 0,
                "unit": "start",  # start accumulator
            }
        ]
        for fixing_time in self.fix_dates[1:]:
            events.append(
                {
                    "track": None,
                    "time": fixing_time,
                    "op": None,
                    "quantity": 0,
                    "unit": "addfix",  # update accumulator
                }
            )
        events.append(
            {
                "track": self.track,
                "time": maturity,
                "op": ">",  # global floor
                "quantity": self.global_floor,
                "unit": self.ccy,
            }
        )
        events.append(
            {
                "track": self.track,
                "time": maturity,
                "op": "+",  # pay the accumulated amount
                "quantity": self.notional,
                "unit": "ACC",
            }
        )
        return events

    def expressions(self):
        last_acc = self.state.get("ACC", 0.0)

        if "S_PREV" in self.state:

            def accumulator_init_fn(inputs):
                [s] = inputs
                return [last_acc, self.state["S_PREV"]]  # [ACC, S_PREV]
        else:

            def accumulator_init_fn(inputs):
                [s] = inputs
                return [last_acc, s]  # [ACC, S_PREV]

        def accumulator_update_fn(inputs):
            [s, s_prev, a] = inputs

            ret = s / s_prev - 1.0  # ret = S / S_PREV - 1
            ret = np.maximum(self.local_floor, ret)
            ret = np.minimum(self.local_cap, ret)

            return [a + ret, s]  # [ACC, S_PREV]

        return {
            "start": {
                "type": "snapper",
                "inp": [self.asset_name],
                "fn": accumulator_init_fn,
                "out": ["ACC", "S_PREV"],
            },
            "addfix": {
                "type": "snapper",
                "inp": [self.asset_name, "S_PREV", "ACC"],
                "fn": accumulator_update_fn,
                "out": ["ACC", "S_PREV"],
            },
        }

In an Accumulator the payoff depends on the asset price on several fixing dates \(T_0 < T_1 < ... T_N\).

If the returns are given by

\[ r_i = \frac{S_{T_{i+1}}-S_{T_i}}{S_{T_i}} \]

Then contract has a single payoff at \(T_N\), given by

\[ \max \left( floor_G, \Sigma_0^{N-1} \max(\min(r_i, cap_L), floor_L) \right) \]