दर्ज स्ट्रैडल के आगे के प्रीमियम बदलाव मापना
सारांश
यह उपयोगिता निर्दिष्ट सत्र अवधियों में दर्ज किए गए ऑप्शंस स्ट्रैडल के प्रीमियम में पूर्ण सापेक्ष बदलाव मापती है। यह सकारात्मक बोली और बोली से ऊपर माँग वाले युग्मित कॉल और पुट कोटेशन से वैध स्ट्रैडल प्रीमियम बनाती है, फिर दर्ज किए गए सटीक प्रतीक, स्ट्राइक और समाप्ति-तारीख को ट्रैक करती है। इससे प्रीमियम बदलाव का अर्थ बना रहता है, भले ही दैनिक पैनल बाद की तारीख पर अलग निकटतम-एट-द-मनी कॉन्ट्रैक्ट चुनता। भविष्य के अवलोकन कोटेशन-पंक्ति ऑफ़सेट के बजाय ऑप्शन चेन के ट्रेडिंग दिनों से निकले सत्र सूचकांक के अनुसार मिलाए जाते हैं, ताकि अनुपलब्ध कोटेशन अवधि को न खिसकाएँ। लक्ष्य सत्र पर दोनों पक्षों वाला कोटेशन उपलब्ध न होने पर माप null होता है। यह विधि जीवनचक्र सुरक्षित रखने वाले चेन डेटा और कॉलर द्वारा दिए गए वास्तविक चयनित कॉन्ट्रैक्ट दर्शाने वाले एंट्री पर निर्भर करती है; यह पूर्ण प्रीमियम बदलाव मापती है और स्वयं लाभप्रदता, अभ्यास परिणाम या ट्रेडिंग लागत का अनुमान नहीं लगाती।
मुख्य विचार
- प्रतीक, स्ट्राइक और समाप्ति-तारीख से पहचाने गए वास्तव में दर्ज कॉन्ट्रैक्ट का प्रीमियम बदलाव मापें।
- हर स्ट्रैडल प्रीमियम बनाने के लिए वैध कॉल और पुट कोटेशन जोड़ें।
- अनुपलब्ध कोटेशन से आगे की अवधि विकृत न हो, इसके लिए ट्रेडिंग-सत्र सूचकांक उपयोग करें।
- अनुरोधित अवधि पर कॉन्ट्रैक्ट का वैध कोटेशन न हो तो null लौटाएँ।
- केवल पूर्ण प्रीमियम बदलाव से रणनीति की लाभप्रदता स्थापित नहीं होती।
टैग
पूरा पाठ
# _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")
```स्रोत के लाइसेंस के तहत श्रेय सहित पूरा पाठ दिखाया गया है। लाइसेंस: MIT
यह सारांश मूल स्रोत के आधार पर Stratmill के शोध एजेंट ने लिखा है; यह स्रोत की प्रति नहीं है।