Skip to content

Commit

Permalink
Improves Futures Examples
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexCatarino committed Nov 8, 2024
1 parent 54f93d8 commit 2e1fbfc
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h4>Example 1: Rollover</h4>
var future = AddFuture(Futures.Indices.SP500EMini, extendedMarketHours: true);
_future = future.Symbol;
// We only want to hold position of the front month contract.
future.SetFilter((u) =&gt; u.OnlyApplyFilterAtMarketOpen().FrontMonth());
future.SetFilter(u =&gt; u.FrontMonth());
}

public override void OnSecuritiesChanged(SecurityChanges changes)
Expand Down Expand Up @@ -40,7 +40,7 @@ <h4>Example 1: Rollover</h4>
future = self.add_future(Futures.Indices.SP_500_E_MINI, extended_market_hours=True)
self._future = future.symbol
# We only want to hold position of the front month contract.
future.set_filter(lambda u: u.only_apply_filter_at_market_open().front_month())
future.set_filter(lambda u: u.front_month())

def on_securities_changed(self, changes: SecurityChanges) -&gt; None:
# Liquidate if expired (or not being the front month contract anymore) and exit universe.
Expand All @@ -60,7 +60,7 @@ <h4>Example 2: Continuous Future Indicator</h4>
<div class="section-example-container">
<pre class="csharp">public class FutureExampleAlgorithm : QCAlgorithm
{
private dynamic _future;
private Future _future;

public override void Initialize()
{
Expand All @@ -69,20 +69,20 @@ <h4>Example 2: Continuous Future Indicator</h4>
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
extendedMarketHours: true);
// We only want to hold position of the front month contract.
(_future as Future).SetFilter((u) =&gt; u.OnlyApplyFilterAtMarketOpen().FrontMonth());
_future.SetFilter(u =&gt; u.FrontMonth());
// Create a 252-day EMA indicator as a trend estimator.
_future.ema = EMA(_future.Symbol, 252, Resolution.Daily);
((dynamic)_future).ema = EMA(_future.Symbol, 252, Resolution.Daily);
// Warm up the EMA indicator to make it readily available.
WarmUpIndicator((Symbol)_future.Symbol, (ExponentialMovingAverage)_future.ema);
WarmUpIndicator(_future.Symbol, _future.Get&lt;ExponentialMovingAverage&gt;("ema"));
}

public override void OnData(Slice slice)
{
// Ensure the TradeBar data is available for the Future. Only use updated price data to update the indicator and make trading decision.
if (slice.Bars.ContainsKey(_future.Symbol))
if (slice.Bars.TryGetValue(_future.Symbol, out var bar))
{
// Buy the mapped contract if the trend is estimated to go up (price above EMA).
if (_future.ema.Current.Value &gt;= slice.Bars[_future.Symbol].Close)
if (_future.Get&lt;ExponentialMovingAverage&gt;("ema") >= bar.Close)
{
SetHoldings(_future.Mapped, 0.1m);
}
Expand All @@ -91,7 +91,7 @@ <h4>Example 2: Continuous Future Indicator</h4>
{
SetHoldings(_future.Mapped, -0.1m);
}
}
}
}

public override void OnSecuritiesChanged(SecurityChanges changes)
Expand All @@ -110,17 +110,18 @@ <h4>Example 2: Continuous Future Indicator</h4>
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
extended_market_hours=True)
# We only want to hold position of the front month contract.
self._future.set_filter(lambda u: u.only_apply_filter_at_market_open().front_month())
self._future.set_filter(lambda u: u.front_month())
# Create a 252-day EMA indicator as a trend estimator.
self._future.ema = self.ema(self._future.symbol, 252, Resolution.DAILY)
# Warm up the EMA indicator to make it readily available.
self.warm_up_indicator(self._future.symbol, self._future.ema)

def on_data(self, slice: Slice) -&gt; None:
# Ensure the TradeBar data is available for the Future. Only use updated price data to update the indicator and make trading decision.
if self._future.symbol in slice.bars:
bar = slice.bars.get(self._future.symbol)
if bar:
# Buy the mapped contract if the trend is estimated to go up (price above EMA).
if self._future.ema.current.value &gt;= slice.bars[self._future.symbol].close:
if self._future.ema.current.value >= bar.close:
self.set_holdings(self._future.mapped, 0.1)
# Short the mapped contract if the trend is estimated to go down (price below EMA).
else:
Expand All @@ -145,7 +146,7 @@ <h4>Example 3: Contango</h4>
var future = AddFuture(Futures.Metals.MicroGold, extendedMarketHours: true);
_future = future.Symbol;
// Limit the expiration to within 6 months, as the longer the expiration, the higher the price uncertainty.
future.SetFilter((u) =&gt; u.OnlyApplyFilterAtMarketOpen().Expiration(0, 183));
future.SetFilter((u) =&gt; u.Expiration(0, 183));
}

public override void OnData(Slice slice)
Expand Down Expand Up @@ -183,7 +184,7 @@ <h4>Example 3: Contango</h4>
future = self.add_future(Futures.Metals.MICRO_GOLD, extended_market_hours=True)
self._future = future.symbol
# Limit the expiration to within 6 months, as the longer the expiration, the higher the price uncertainty.
future.set_filter(lambda u: u.only_apply_filter_at_market_open().expiration(0, 183))
future.set_filter(lambda u: u.expiration(0, 183))

def on_data(self, slice: Slice) -&gt; None:
# Get Future chain only for the selected Future contract.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ <h4>Example 1: Add the Front-Month Contract</h4>
# Get all the contracts that are currently tradable.
contract_symbols = self.future_chain_provider.get_future_contract_list(self._future.symbol, self.time)
# Get the contract with the closest expiry.
self._contract_symbol = sorted(contract_symbols, key=lambda x: x.id.date)[0]
self._contract_symbol = min(contract_symbols, key=lambda x: x.id.date)
# Add the Futures contract.
self.add_future_contract(self._contract_symbol)
# Plot the contract price.
self.plot_indicator("Price", self.identity(self._contract_symbol, Resolution.DAILY))
self.set_end_date(self._contract_symbol.id.date)</pre>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,30 @@ <h4>Example 1: Call Spread</h4>
_underlying.SetFilter(0, 182);
// Use CallSpread filter to obtain the 2 best-matched contracts that forms a call spread.
// It simplifies from further filtering and reduce computation on redundant subscription.
AddFutureOption(_underlying.Symbol, (u) =&gt; u.IncludeWeeklys().CallSpread(5, 5, -5));
AddFutureOption(_underlying.Symbol, (u) =&gt; u.CallSpread(5, 5, -5));
}

public override void OnData(Slice slice)
{
if (Portfolio.Invested)
return;

// Create canonical symbol for the mapped future contract, since we need that to access the option chain.
var symbol = QuantConnect.Symbol.CreateCanonicalOption(_underlying.Mapped);

// Get option chain data for the mapped future only.
if (!Portfolio.Invested &&
slice.OptionChains.TryGetValue(symbol, out var chain))
{
// It requires 2 contracts with different strikes to form a call spread, so we make sure at least 2 contracts are present.
if (chain.Count() &lt; 2)
{
return;
}
// It requires 2 contracts with different strikes to form a call spread, so we make sure at least 2 contracts are present.
if (!slice.OptionChains.TryGetValue(symbol, out var chain) || chain.Count() &lt; 2)
return;

// Separate the contracts by strike, as we need to access their strike.
var itmStrike = chain.Min(x =&gt; x.Strike);
var otmStrike = chain.Max(x =&gt; x.Strike);
// Separate the contracts by strike, as we need to access their strike.
var expiry = chain.Min(x =&gt; x.Expiry);
var itmStrike = chain.Min(x =&gt; x.Strike);
var otmStrike = chain.Max(x =&gt; x.Strike);

// Use abstraction method to order a bull call spread to avoid manual error.
var optionStrategy = OptionStrategies.BullCallSpread(symbol, itmStrike, otmStrike, chain.First().Expiry);
Buy(optionStrategy, 1);
}
// Use abstraction method to order a bull call spread to avoid manual error.
var optionStrategy = OptionStrategies.BullCallSpread(symbol, itmStrike, otmStrike, expiry);
Buy(optionStrategy, 1);
}
}</pre>
<pre class="python">class FutureOptionAlgorithm(QCAlgorithm):
Expand All @@ -57,27 +55,29 @@ <h4>Example 1: Call Spread</h4>
self.underlying.set_filter(0, 182)
# Use CallSpread filter to obtain the 2 best-matched contracts that forms a call spread.
# It simplifies from further filtering and reduce computation on redundant subscription.
self.add_future_option(self.underlying.symbol, lambda u: u.include_weeklys().call_spread(5, 5, -5))
self.add_future_option(self.underlying.symbol, lambda u: u.call_spread(5, 5, -5))

def on_data(self, slice: Slice) -&gt; None:
if self.portfolio.invested:
return
# Create canonical symbol for the mapped future contract, since we need that to access the option chain.
symbol = Symbol.create_canonical_option(self.underlying.mapped)

# Get option chain data for the mapped future only.
# It requires 2 contracts with different strikes to form a call spread, so we make sure at least 2 contracts are present.
chain = slice.option_chains.get(symbol)
if not self.portfolio.invested and chain:
# It requires 2 contracts with different strikes to form a call spread, so we make sure at least 2 contracts are present.
if len(list(chain)) &lt; 2:
return
if not chain or len(list(chain)) &lt; 2:
return

# Separate the contracts by strike, as we need to access their strike.
sorted_by_strike = sorted([x.strike for x in chain])
itm_strike = sorted_by_strike[0]
otm_strike = sorted_by_strike[-1]
# Separate the contracts by strike, as we need to access their strike.
expiry = min([x.expiry for x in chain])
sorted_by_strike = sorted([x.strike for x in chain])
itm_strike = sorted_by_strike[0]
otm_strike = sorted_by_strike[-1]

# Use abstraction method to order a bull call spread to avoid manual error.
option_strategy = OptionStrategies.bull_call_spread(symbol, itm_strike, otm_strike, otm_call.expiry)
self.buy(option_strategy, 1)</pre>
# Use abstraction method to order a bull call spread to avoid manual error.
option_strategy = OptionStrategies.bull_call_spread(symbol, itm_strike, otm_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 @@ -29,16 +29,14 @@ <h4>Example 2: Contracts Expiring Within One Week</h4>
public override void Initialize()
{
var future = AddFuture("ES");
future.SetFilter(futureFilterUniverse => futureFilterUniverse.Expiration(0, 7).OnlyApplyFilterAtMarketOpen());
future.SetFilter(futureFilterUniverse => futureFilterUniverse.Expiration(0, 7));
}
}</pre>
<pre class="python">class WeeklyFuturesAlgorithm(QCAlgorithm):

def initialize(self):
future = self.add_future("ES")
future.set_filter(
lambda future_filter_universe: future_filter_universe.expiration(0, 7).only_apply_filter_at_market_open()
)</pre>
future.set_filter(lambda future_filter_universe: future_filter_universe.expiration(0, 7))</pre>
</div>

<h4>Other Examples</h4>
Expand Down

0 comments on commit 2e1fbfc

Please sign in to comment.