Skip to content

Commit

Permalink
Merge pull request #1913 from QuantConnect/feature-1898-handling-futu…
Browse files Browse the repository at this point in the history
…re-option-data-examples

Add handling Future Option data examples
  • Loading branch information
AlexCatarino authored Nov 6, 2024
2 parents 22b79f2 + 3e14f45 commit e43aa22
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
include(DOCS_RESOURCES."/securities/option-contracts.php");

$contractTypeName = "Option";
$pyContractTypeName = "option";
$chainTypeName = "OptionChains";
$csChainTypeName = "OptionChains";
$pyChainTypeName = "option_chains";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
include(DOCS_RESOURCES."/securities/futures-contracts.php");

$contractTypeName = "Future";
$pyContractTypeName = "future";
$chainTypeName = "FuturesChains";
$csChainTypeName = "FuturesChains";
$pyChainTypeName = "futures_chains";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
include(DOCS_RESOURCES."/securities/option-contracts.php");

$contractTypeName = "Option";
$pyContractTypeName = "option";
$chainTypeName = "OptionChains";
$csChainTypeName = "OptionChains";
$pyChainTypeName = "option_chains";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<p>The following examples demonstrate some common practices for handling Future Option data.</p>

<h4>Example 1: Monthly Protective Put</h4>
<p>The following algorithm shows how to perform monthly selection on individual ES Future Option contract to implement a <a href="/docs/v2/writing-algorithms/trading-and-orders/option-strategies/protective-put">protective put</a> option strategy to hedge speculation on S&P500 Future. It is a useful tool to hedge the excessive risk on leverage using Futures to trade.</p>

<div class="section-example-container">
<pre class="csharp">public class FutureOptionExampleAlgorithm : QCAlgorithm
{
private Future _underlying;

public override void Initialize()
{
// Seed the security price to ensure the underlying price is available at the initial filtering.
SetSecurityInitializer(new BrokerageModelSecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));
// Subscribe the underlying since the updated price is needed for filtering.
_underlying = AddFuture(Futures.Indices.SP500EMini,
dataMappingMode: DataMappingMode.OpenInterest,
dataNormalizationMode: DataNormalizationMode.Raw,
contractDepthOffset: 0);
// Filter the underlying continuous Futures to narrow the FOP spectrum.
_underlying.SetFilter(0, 31);

// Schedule a monthly event on selection of future-future option pair, since the portfolio rebalance on a monthly basis.
Schedule.On(
DateRules.MonthStart(_underlying.Symbol),
TimeRules.AfterMarketOpen(_underlying.Symbol, 0),
SelectionAndRebalance
);
}

private void SelectionAndRebalance()
{
// Get all available put FOP contract for the mapped underlying contract, since the trade liquidity and volatility is the highest.
var contractSymbols = OptionChain(_underlying.Mapped)
.Where(symbol =&gt; symbol.ID.OptionRight == OptionRight.Put)
.ToList();
// Select the ATM put expires the same date as the underlying. The max expiry of the FOP will expire the same time as the front month future.
var expiry = contractSymbols.Max(symbol =&gt; symbol.ID.Date);
var selected = contractSymbols.Where(symbol =&gt; symbol.ID.Date == expiry)
.OrderBy(symbol =&gt; Math.Abs(symbol.ID.StrikePrice - Securities[_underlying.Mapped].Price))
.First();
// Request the FOP contract data for trading.
var contract = AddFutureOptionContract(selected);

// A Protective Put consists of long a lot of the underlying, and long a put contract.
MarketOrder(_underlying.Mapped, contract.SymbolProperties.ContractMultiplier);
MarketOrder(contract.Symbol, 1);
}
}</pre>
<pre class="python">class FutureOptionExampleAlgorithm(QCAlgorithm):
def initialize(self) -&gt; None:
# Seed the security price to ensure the underlying price is available at the initial filtering.
self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
# Subscribe the underlying since the updated price is needed for filtering.
self.underlying = self.add_future(Futures.Indices.SP_500_E_MINI,
data_mapping_mode=DataMappingMode.OPEN_INTEREST,
data_normalization_mode=DataNormalizationMode.RAW,
contract_depth_offset=0)
# Filter the underlying continuous Futures to narrow the FOP spectrum.
self.underlying.set_filter(0, 31)

# Schedule a monthly event on selection of future-future option pair, since the portfolio rebalance on a monthly basis.
self.schedule.on(
self.date_rules.month_start(self.underlying.symbol),
self.time_rules.after_market_open(self.underlying.symbol, 0),
self.selection_and_rebalance
)

def selection_and_rebalance(self) -&gt; None:
# Get all available put FOP contract for the mapped underlying contract, since the trade liquidity and volatility is the highest.
contract_symbols = self.option_chain(self.underlying.mapped)
contract_symbols = [symbol for symbol in contract_symbols if symbol.id.option_right == OptionRight.PUT]
# Select the ATM put expires the same date as the underlying. The max expiry of the FOP will expire the same time as the front month future.
expiry = max(symbol.id.date for symbol in contract_symbols)
filtered_symbols = [symbol for symbol in contract_symbols if symbol.id.date == expiry]
selected = sorted(filtered_symbols, key=lambda symbol: abs(symbol.id.strike_price - self.securities[self.underlying.mapped].price))[0]
# Request the FOP contract data for trading.
contract = self.add_future_option_contract(selected)

# A Protective Put consists of long a lot of the underlying, and long a put contract.
self.market_order(self.underlying.mapped, contract.symbol_properties.contract_multiplier)
self.market_order(contract.symbol, 1)</pre>
</div>

<h4>Example 2: Weekly Covered Call</h4>
<p>The below example demonstrates a weekly-renewing <a href="/docs/v2/writing-algorithms/trading-and-orders/option-strategies/covered-call">covered call</a> strategy to collect credit of selling the option. It filters the ATM call contract that expires within the current week at week start using <code class="csharp">SetFilter</code><code class="python">set_filter</code> filtering function.</p>
<div class="section-example-container">
<pre class="csharp">public class FutureOptionExampleAlgorithm : QCAlgorithm
{
private Future _underlying;

public override void Initialize()
{
// Subscribe the underlying since the updated price is needed for filtering.
_underlying = AddFuture(Futures.Indices.SP500EMini,
extendedMarketHours: true,
dataMappingMode: DataMappingMode.OpenInterest,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
contractDepthOffset: 0);
// Filter the underlying continuous Futures to narrow the FOP spectrum.
_underlying.SetFilter(0, 182);
// Filter for the current-week-expiring calls to formulate a covered call that expires at the end of week.
AddFutureOption(_underlying.Symbol, (u) =&gt; u.IncludeWeeklys().CallsOnly().Expiration(0, 5));
}

public override void OnData(Slice slice)
{
// Create canonical symbol for the mapped future contract, since option chains are mapped by canonical symbol.
var symbol = QuantConnect.Symbol.CreateCanonicalOption(_underlying.Mapped);

// Get option chain data for the mapped future, as both the underlying and FOP have the highest liquidity among all other contracts.
if (!Portfolio.Invested &&
slice.OptionChains.TryGetValue(symbol, out var chain))
{
// Obtain the ATM call that expires at the end of week, such that both underlying and the FOP expires the same time.
var expiry = chain.Max(x =&gt; x.Expiry);
var atmCall = chain.Where(x =&gt; x.Expiry == expiry)
.OrderBy(x =&gt; Math.Abs(x.Strike - x.UnderlyingLastPrice))
.First();

// Use abstraction method to order a covered call to avoid manual error.
var optionStrategy = OptionStrategies.CoveredCall(symbol, atmCall.Strike, expiry);
Buy(optionStrategy, 1);
}
}
}</pre>
<pre class="python">class FutureOptionExampleAlgorithm(QCAlgorithm):
def initialize(self) -&gt; None:
# Subscribe the underlying since the updated price is needed for filtering.
self.underlying = self.add_future(Futures.Indices.SP_500_E_MINI,
extended_market_hours=True,
data_mapping_mode=DataMappingMode.OPEN_INTEREST,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
contract_depth_offset=0)
# Filter the underlying continuous Futures to narrow the FOP spectrum.
self.underlying.set_filter(0, 182)
# Filter for the current-week-expiring calls to formulate a covered call that expires at the end of week.
self.add_future_option(self.underlying.symbol, lambda u: u.include_weeklys().calls_only().expiration(0, 5))

def on_data(self, slice: Slice) -&gt; None:
# Create canonical symbol for the mapped future contract, since option chains are mapped by canonical symbol.
symbol = Symbol.create_canonical_option(self.underlying.mapped)

# Get option chain data for the mapped future, as both the underlying and FOP have the highest liquidity among all other contracts.
chain = slice.option_chains.get(symbol)
if not self.portfolio.invested and chain:
# Obtain the ATM call that expires at the end of week, such that both underlying and the FOP expires the same time.
expiry = max(x.expiry for x in chain)
atm_call = sorted([x for x in chain if x.expiry == expiry],
key=lambda x: abs(x.strike - x.underlying_last_price))[0]

# Use abstraction method to order a covered call to avoid manual error.
option_strategy = OptionStrategies.covered_call(symbol, atm_call.strike,expiry)
self.buy(option_strategy, 1)</pre>
</div>

<p>Note that since both the underlying Future and the Future Option are expiring on the same day and are cash-settling in most cases, Lean can exercise the Future Option into account cash automatically at expiry and we do not need to handle the option exercise/assignment event.</p>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
include(DOCS_RESOURCES."/securities/option-contracts.php");

$contractTypeName = "Option";
$pyContractTypeName = "option";
$chainTypeName = "OptionChains";
$csChainTypeName = "OptionChains";
$pyChainTypeName = "option_chains";
Expand Down
8 changes: 8 additions & 0 deletions Resources/securities/future-chains.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@
<div class='section-example-container'>
<pre class='csharp'>public override void OnData(Slice slice)
{
// Try to get the FutureChain using the canonical symbol
if (slice.FuturesChains.TryGetValue(<?=$cSharpMemberName?>.Canonical, out var chain))
{
// Get all contracts if the FutureChain contains any member
var contracts = chain.Contracts;
}
}
</pre>
<pre class='python'>def on_data(self, slice: Slice) -> None:
# Try to get the FutureChain using the canonical symbol (None if no FutureChain return)
chain = slice.futures_chains.get(<?=$pythonMemberName?>.canonical)
if chain:
# Get all contracts if the FutureChain contains any member
contracts = chain.contracts</pre>
</div>

<p>You can also loop through the <code class="csharp">FuturesChains</code><code class="python">futures_chains</code> property to get each <code>FuturesChain</code>.</p>
<div class='section-example-container'>
<pre class='csharp'>public override void OnData(Slice slice)
{
// Iterate all received Canonical Symbol-FutureChain key-value pairs
foreach (var kvp in slice.FuturesChains)
{
var continuousContractSymbol = kvp.Key;
Expand All @@ -30,8 +35,10 @@
}
}

// Using this overload will only handle any FutureChains object received
public void OnData(FuturesChains futuresChains)
{
// Iterate all received Canonical Symbol-FutureChain key-value pairs
foreach (var kvp in futuresChains)
{
var continuousContractSymbol = kvp.Key;
Expand All @@ -40,6 +47,7 @@
}
}</pre>
<pre class='python'>def on_data(self, slice: Slice) -> None:
# Iterate all received Canonical Symbol-FutureChain key-value pairs
for continuous_contract_symbol, chain in slice.futures_chains.items():
contracts = chain.contracts</pre>
</div>
Expand Down
13 changes: 10 additions & 3 deletions Resources/securities/futures-contracts.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,36 @@
<div class='section-example-container'>
<pre class='csharp'>public override void OnData(Slice slice)
{
// Try to get the FutureChain using the canonical symbol
if (slice.FuturesChains.TryGetValue(<?=$cSharpMemberName?>.Canonical, out var chain))
{
// Get individual contract data
if (chain.Contracts.TryGetValue(<?=$cSharpMemberName?>, out var contract))
{
var price = contract.LastPrice;
}
}
}

// // Using this overload will only handle any FutureChains object received
public void OnData(FuturesChains futuresChains)
{
// Try to get the FutureChain using the canonical symbol
if (futuresChains.TryGetValue(<?=$cSharpMemberName?>.Canonical, out var chain))
{
// Get individual contract data
if (chain.Contracts.TryGetValue(<?=$cSharpMemberName?>, out var contract))
{
var price = contract.LastPrice;
}
}
}</pre>
<pre class='python'>def on_data(self, slice: Slice) -> None:
chain = slice.FuturesChains.get(<?=$pythonMemberName?>.Canonical)
# Try to get the FutureChain using the canonical symbol
chain = slice.future_chains.get(<?=$pythonMemberName?>.canonical)
if chain:
contract = chain.Contracts.get(<?=$pythonMemberName?>)
# Get individual contract data (None if not contained)
contract = chain.contracts.get(<?=$pythonMemberName?>)
if contract:
price = contract.LastPrice</pre>
price = contract.last_price</pre>
</div>
11 changes: 10 additions & 1 deletion Resources/securities/open-interest.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<p>Open interest is the number of outstanding contracts that haven't been settled. It provides a measure of investor interest and the market liquidity, so it's a popular metric to use for contract selection. Open interest is calculated once per day. To get the latest open interest value, use the <code class="csharp">OpenInterest</code><code class="python">open_interest</code> property of the <code><?=$contractTypeName?></code> or <code class="csharp"><?=$contractTypeName?>Contract</code><code class="python"><?=$contractTypeName?>Contract</code>.</p>
<p>Open interest is the number of outstanding contracts that haven't been settled. It provides a measure of investor interest and the market liquidity, so it's a popular metric to use for contract selection. Open interest is calculated once per day. To get the latest open interest value, use the <code class="csharp">OpenInterest</code><code class="python">open_interest</code> property of the <code><?=$contractTypeName?></code> or <code class="csharp"><?=$contractTypeName?>Contract</code><code class="python"><?=$pyContractTypeName?>_contract</code>.</p>

<div class='section-example-container'>
<pre class='csharp'>public override void OnData(Slice slice)
{
// Try to get the <?=$chainTypeName?> using the canonical symbol
if (slice.<?=$chainTypeName?>.TryGetValue(_contractSymbol.Canonical, out var chain))
{
// Get individual contract data
if (chain.Contracts.TryGetValue(_contractSymbol, out var contract))
{
// Get the open interest of the selected contracts
var openInterest = contract.OpenInterest;
}
}
Expand All @@ -15,19 +18,25 @@
<? if ($chainTypeName != "FuturesChains") { ?>
public void OnData(<?=$csChainTypeName?> <?=$variableName?>)
{
// Try to get the <?=$chainTypeName?> using the canonical symbol
if (<?=$variableName?>.TryGetValue(_contractSymbol.Canonical, out var chain))
{
// Get individual contract data
if (chain.Contracts.TryGetValue(_contractSymbol, out var contract))
{
// Get the open interest of the selected contracts
var openInterest = contract.OpenInterest;
}
}
}
<? } ?></pre>
<pre class='python'>def on_data(self, slice: Slice) -> None:
# Try to get the <?=$pyChainTypeName?> using the canonical symbol
chain = slice.<?=$pyChainTypeName?>.get(self._contract_symbol.canonical)
if chain:
# Get individual contract data
contract = chain.contracts.get(self._contract_symbol)
if contract:
# Get the open interest of the selected contracts
open_interest = contract.open_interest</pre>
</div>
Loading

0 comments on commit e43aa22

Please sign in to comment.