-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1913 from QuantConnect/feature-1898-handling-futu…
…re-option-data-examples Add handling Future Option data examples
- Loading branch information
Showing
13 changed files
with
235 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
...rithms/03 Securities/99 Asset Classes/07 Future Options/02 Handling Data/99 Examples.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 => 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 => symbol.ID.Date); | ||
var selected = contractSymbols.Where(symbol => symbol.ID.Date == expiry) | ||
.OrderBy(symbol => 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) -> 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) -> 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) => 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 => x.Expiry); | ||
var atmCall = chain.Where(x => x.Expiry == expiry) | ||
.OrderBy(x => 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) -> 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) -> 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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.