Pular para o conteúdo
Todos os documentos da biblioteca

Medição de variações futuras do prêmio de straddles

Código Machine Learning for Trading

Resumo

Este utilitário mede a variação percentual absoluta do prêmio de um straddle de opções informado, em horizontes de sessões especificados. Ele calcula um prêmio válido de straddle a partir de cotações pareadas de call e put, com bids positivos e asks acima dos bids, e acompanha o símbolo, o strike e o vencimento exatos selecionados na entrada. Isso preserva o significado de uma variação do prêmio mesmo quando o painel diário selecionaria outro contrato próximo do preço de exercício em uma data posterior. As observações futuras são associadas pelo índice de sessão derivado dos dias de negociação das cadeias de opções, e não pelo deslocamento das linhas de cotação; assim, cotações ausentes não alteram o horizonte. Se não houver cotação válida de compra e venda na sessão-alvo, a medição será nula. O método depende de dados da cadeia que preservem o ciclo de vida e de entradas fornecidas pelo usuário que reflitam contratos realmente selecionados; mede a variação absoluta do prêmio e não estima, por si só, lucratividade, resultados de exercício ou custos de trading.

Ideias principais

  • Meça a variação do prêmio do contrato efetivamente negociado, identificado pelo símbolo, strike e vencimento.
  • Combine cotações válidas de call e put para calcular cada prêmio de straddle.
  • Use índices de sessões de negociação para que cotações ausentes não distorçam os horizontes futuros.
  • Retorne nulo quando não houver cotação válida do contrato no horizonte solicitado.
  • A variação absoluta do prêmio, por si só, não demonstra a lucratividade da estratégia.

Tags

Texto completo
# _straddle_moves.py


```py
"""Absolute moves in a straddle's own premium, followed by contract and by session."""

from __future__ import annotations

from collections.abc import Sequence

import polars as pl

CONTRACT = ["symbol", "strike", "expiration"]


def straddle_premium_moves(
    chains: pl.LazyFrame,
    entries: pl.DataFrame,
    *,
    horizons: Sequence[int],
) -> pl.DataFrame:
    """Absolute relative moves in the straddle premium, one row per entry.

    Three constructions here are easy to get wrong and silent when they are. The chains
    keep the whole life of every contract that reaches the at-the-money band at any point,
    so filtering them by days to maturity alone would follow contracts the strategy would
    never have entered on that date; the entries are therefore supplied by the caller as
    the contracts actually selected, and the chains are read only to follow them forward.
    The daily panel re-picks the contract nearest the money every session, so differencing
    it would measure the switch to a new strike rather than a move in the premium; the
    legs are paired on ``(symbol, strike, expiration)`` and followed through that one
    contract instead. And a row offset counts quotes rather than sessions, so a contract
    that goes unquoted for a day would contribute a mistimed move; the later observation
    is joined on a session index built from the chains' own trading days, and a gap yields
    a null that drops out of the horizon rather than a wrong value.

    Parameters
    ----------
    chains
        Lifecycle-preserving option chains, one row per leg, session and contract, as
        :func:`data.load_sp500_options_straddles_raw` returns them lazily.
    entries
        The straddles actually entered, keyed by ``symbol``, ``strike``, ``expiration``
        and ``timestamp``. Extra columns are ignored.
    horizons
        Session offsets to measure the move over. Each becomes a column ``h<offset>``.

    Returns
    -------
    pl.DataFrame
        One row per entry, carrying ``symbol``, ``strike``, ``expiration``, ``timestamp``,
        ``premium`` and one ``h<offset>`` per horizon holding
        ``|premium(t + offset) / premium(t) - 1|``, null where the contract carries no
        two-sided quote at that offset.
    """
    # The index counts every session the chains quote on, not every session they pair on:
    # built from the paired frame, a day on which no contract happened to pair would drop
    # out of the count and the offset would silently reach one session too far.
    sessions = chains.select("timestamp").unique().collect().sort("timestamp").with_row_index("s")
    chain = (
        chains.filter((pl.col("ask") > pl.col("bid")) & (pl.col("bid") > 0))
        .group_by([*CONTRACT, "timestamp"])
        .agg(
            pl.col("call_put").n_unique().alias("n_legs"),
            ((pl.col("ask") + pl.col("bid")) / 2).sum().alias("premium"),
        )
        .filter(pl.col("n_legs") == 2)
        .collect()
        .join(sessions, "timestamp")
    )

    moves = entries.select([*CONTRACT, "timestamp"]).join(
        chain.select([*CONTRACT, "timestamp", "s", "premium"]), on=[*CONTRACT, "timestamp"]
    )
    for horizon in horizons:
        later = chain.select([*CONTRACT, pl.col("s") - horizon, pl.col("premium").alias("later")])
        moves = (
            moves.join(later, on=[*CONTRACT, "s"], how="left")
            .with_columns((pl.col("later") / pl.col("premium") - 1).abs().alias(f"h{horizon}"))
            .drop("later")
        )
    return moves.drop("s")

```

Exibido na íntegra, com atribuição conforme a licença da fonte. Licença: MIT

Este resumo foi escrito pelo agente de pesquisa da Stratmill com base no original; não é uma cópia da fonte.