From 7f674f70bbcc16ace530d8e4b56b201c6bd801fb Mon Sep 17 00:00:00 2001
From: LouisSzeto To add a universe of Future contracts, in the The following algorithm shows how to request continuous Futures data of E-Mini S&P 500, with a universe filter to obtain only the contracts expiring within 6 months. To access the data of the continuous Future contract and mapped contract, you need to call the Initialize
initialize
method, call the AddFuture
add_future
method. This method returns an Future
object, which has a SetFilter
set_filter
method you can call to filter the set of tradable contract down to just the contracts you want.Example: Continous Future Filtering And Rollover
+Symbol
symbol
and Mapped
mapped
properties of the Future
object respectively, while accessing all contracts in the filtered Future chain using FuturesChains
future_chains
of the Slice
object.
+
+To rollover the old mapped contract to the new one, you can refer to the below making use of the OnSymbolChangedEvents
on_symbol_changed_events
event handler.
public class BasicFutureAlgorithm : QCAlgorithm { diff --git a/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html new file mode 100644 index 0000000000..8e24cca936 --- /dev/null +++ b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html @@ -0,0 +1,199 @@ +The following examples demonstrate some common practices for requesting Futures universe data.
+ +Example 1: Rollover
+Future contracts expire monthly or quarterly in most cases. Hence, if we hold Future position in month or quarter end, we must consider rolling over to the mapped contract. The following algorithm shows how to buy and roll over to the next front month Future contract. We make use of the universe selection filter to select the front month contract and order the next mapped contract during the previous one expires.
+++ +private Symbol _future; + +public override void Initialize() +{ + // Add subscription of ES Futures + var future = AddFuture(Futures.Indices.SP500EMini, extendedMarketHours: true); + _future = future.Symbol; + // Filter the universe to front month contract + future.SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); +} + +public override void OnSecuritiesChanged(SecurityChanges changes) +{ + // Liquidate if expired and exit universe + foreach (var removed in changes.RemovedSecurities) + { + Liquidate(removed.Symbol); + } + + // Roll position to newly mapped contract + foreach (var added in changes.AddedSecurities) + { + if (!added.Symbol.IsCanonical()) + { + LimitOrder(added.Symbol, 1m, Securities[_future].Price); + } + } +}+def initialize(self) -> None: + # Add subscription of ES Futures + future = self.add_future(Futures.Indices.SP_500_E_MINI, extended_market_hours=True) + self._future = future.symbol + # Filter the universe to front month contract + future.set_filter(lambda u: u.only_apply_filter_at_market_open().front_month()) + +def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate if expired and exit universe + for removed in changes.removed_securities: + self.liquidate(removed.symbol) + + # Roll position to newly mapped contract + for added in changes.added_securities: + if not added.symbol.is_canonical(): + self.limit_order(added.symbol, 1, self.securities[self._future].price)+Example 2: Continuous Future Indicator
+One of the major applications of Continuous Future is to obtain smooth price series to feed into indicators. This can ensure the indicator gets the correct price data that is comparable to the current mapped Future contract. In this example, we demonstrate a 252-day Exponential Moving Average indicator update using continuous ES contract data.
+++ +private dynamic _future; + +public override void Initialize() +{ + // Add subscription of ES Futures with backward ratio normalization for continuous contract + _future = AddFuture(Futures.Indices.SP500EMini, + dataNormalizationMode: DataNormalizationMode.BackwardsRatio, + extendedMarketHours: true); + // Filter the universe to front month contract + (_future as Future).SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); + // Create 252-day EMA indicator + _future.ema = EMA(_future.Symbol, 252, Resolution.Daily); + // Warm up indicator + WarmUpIndicator((Symbol)_future.Symbol, (ExponentialMovingAverage)_future.ema); +} + +public override void OnData(Slice slice) +{ + if (!slice.Bars.ContainsKey(_future.Symbol)) + { + return; + } + + // Long the mapped contract if the current price above EMA + if (_future.ema.Current.Value >= slice.Bars[_future.Symbol].Close) + { + SetHoldings(_future.Mapped, 0.1m); + } + // Short otherwise + else + { + SetHoldings(_future.Mapped, -0.1m); + } +} + +public override void OnSecuritiesChanged(SecurityChanges changes) +{ + // Liquidate if expired and exit universe + foreach (var removed in changes.RemovedSecurities) + { + Liquidate(removed.Symbol); + } +}+def initialize(self) -> None: + # Add subscription of ES Futures + self._future = self.add_future(Futures.Indices.SP_500_E_MINI, + data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, + extended_market_hours=True) + # Filter the universe to front month contract + self._future.set_filter(lambda u: u.only_apply_filter_at_market_open().front_month()) + # Create 252-day EMA indicator + self._future.ema = self.ema(self._future.symbol, 252, Resolution.DAILY) + # Warm up indicator + self.warm_up_indicator(self._future.symbol, self._future.ema) + +def on_data(self, slice: Slice) -> None: + if not self._future.symbol in slice.bars: + return + + # Long the mapped contract if the current price above EMA + if self._future.ema.current.value >= slice.bars[self._future.symbol].close: + self.set_holdings(self._future.mapped, 0.1) + # Short otherwise + else: + self.set_holdings(self._future.mapped, -0.1) + +def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate if expired and exit universe + for removed in changes.removed_securities: + self.liquidate(removed.symbol)+Example 3: Contango
+In Future trading, contango refers to the far-to-expiry Future contract price is higher than the spot price due to various reasons, such as storage fee and insurance of the commodities. The following example shows a contango trading by shorting the far contract that the price is above a threshold compared to the front month contract price and buying the front month contract to earn the premium in between.
++\ No newline at end of file From 64258152280194bca769aac50e7c3652456d829a Mon Sep 17 00:00:00 2001 From: LouisSzetoprivate Symbol _future; + +public override void Initialize() +{ + // Add subscription of micro gold Futures + var future = AddFuture(Futures.Metals.MicroGold, extendedMarketHours: true); + _future = future.Symbol; + // Filter the universe to contracts expires within 3 months + future.SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().Expiration(0, 95)); +} + +public override void OnData(Slice slice) +{ + // Get Future chain + if (!Portfolio.Invested && slice.FutureChains.TryGetValue(_future, out var chain)) + { + if (chain.Count() < 2) return; + + // Get the far and near contract + var farContract = chain.MaxBy(x => x.Expiry); + var nearContract = chain.MinBy(x => x.Expiry); + + // Check if the far contract price is 1% higher than the near one + if (farContract.BidPrice >= nearContract.AskPrice * 1.01m) + { + // If so, short the far contract and buy the near one to earn the premium + MarketOrder(farContract.Symbol, -1); + MarketOrder(nearContract.Symbol, 1); + } + } +} + +public override void OnSecuritiesChanged(SecurityChanges changes) +{ + // Liquidate all positions if a contract expired and exit universe + foreach (var removed in changes.RemovedSecurities) + { + Liquidate(); + } +}+def initialize(self) -> None: + # Add subscription of micro gold Futures + future = self.add_future(Futures.Metals.MICRO_GOLD, extended_market_hours=True) + self._future = future.symbol + # Filter the universe to contracts expires within 3 months + future.set_filter(lambda u: u.only_apply_filter_at_market_open().expiration(0, 95)) + +def on_data(self, slice: Slice) -> None: + # Get Future chain + chain = slice.future_chains.get(self._future) + if not self.portfolio.invested and chain: + if len(list(chain)) < 2: + return + + # Get the far and near contract + sorted_by_expiry = sorted(chain, key=lambda x: x.expiry) + far_contract = sorted_by_expiry[-1] + near_contract = sorted_by_expiry[0] + + # Check if the far contract price is 1% higher than the near one + if far_contract.bid_price >= near_contract.ask_price * 1.01: + # If so, short the far contract and buy the near one to earn the premium + self.market_order(far_contract.symbol, -1) + self.market_order(near_contract.symbol, 1) + +def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate all positions if a contract expired and exit universe + for removed in changes.removed_securities: + self.liquidate()+Date: Wed, 18 Sep 2024 19:52:27 +0800 Subject: [PATCH 2/3] update comments --- .../01 Universes/99 Examples.html | 110 +++++++++--------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html index 8e24cca936..311333870b 100644 --- a/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html +++ b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html @@ -7,45 +7,47 @@ Example 1: Rollover
public override void Initialize() { - // Add subscription of ES Futures var future = AddFuture(Futures.Indices.SP500EMini, extendedMarketHours: true); _future = future.Symbol; - // Filter the universe to front month contract + // We only want to hold position of the front month contract future.SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); } public override void OnSecuritiesChanged(SecurityChanges changes) { - // Liquidate if expired and exit universe + // Liquidate if expired (or not being the front month contract anymore) and exit universe foreach (var removed in changes.RemovedSecurities) { Liquidate(removed.Symbol); } - // Roll position to newly mapped contract foreach (var added in changes.AddedSecurities) { + // Make sure the newly added contract is an actual mapped tradable contract if (!added.Symbol.IsCanonical()) { + // Roll over by ordering the same quantity + // Use limit order since market on open order is not supported on Future and avoid extreme quote filling LimitOrder(added.Symbol, 1m, Securities[_future].Price); } } }
def initialize(self) -> None: - # Add subscription of ES Futures future = self.add_future(Futures.Indices.SP_500_E_MINI, extended_market_hours=True) self._future = future.symbol - # Filter the universe to front month contract + # 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()) def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate if expired and exit universe + # Liquidate if expired (or not being the front month contract anymore) and exit universe for removed in changes.removed_securities: self.liquidate(removed.symbol) - # Roll position to newly mapped contract for added in changes.added_securities: + # Make sure the newly added contract is an actual mapped tradable contract if not added.symbol.is_canonical(): + # Roll over by ordering the same quantity + # Use limit order since market on open order is not supported on Future and avoid extreme quote filling self.limit_order(added.symbol, 1, self.securities[self._future].price)
def initialize(self) -> None: - # Add subscription of ES Futures + # Use backward ratio normalization for continuous contract to feed smooth, comparable price series to the indicator self._future = self.add_future(Futures.Indices.SP_500_E_MINI, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, extended_market_hours=True) - # Filter the universe to front month contract + # 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()) - # Create 252-day EMA indicator + # Create a 252-day EMA indicator as a trend estimator self._future.ema = self.ema(self._future.symbol, 252, Resolution.DAILY) - # Warm up indicator + # 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) -> None: - if not self._future.symbol in slice.bars: - return - - # Long the mapped contract if the current price above EMA - if self._future.ema.current.value >= slice.bars[self._future.symbol].close: - self.set_holdings(self._future.mapped, 0.1) - # Short otherwise - else: - self.set_holdings(self._future.mapped, -0.1) + # 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: + # Buy the mapped contract if the trend is estimated to go up (price above EMA) + if self._future.ema.current.value >= slice.bars[self._future.symbol].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: + self.set_holdings(self._future.mapped, -0.1) def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate if expired and exit universe + # Liquidate if expired (or not being the front month contract anymore) and exit universe for removed in changes.removed_securities: self.liquidate(removed.symbol)@@ -131,28 +131,27 @@
def initialize(self) -> None: - # Add subscription of micro gold Futures + # Allow extended market hours trade, which is common for Future future = self.add_future(Futures.Metals.MICRO_GOLD, extended_market_hours=True) self._future = future.symbol - # Filter the universe to contracts expires within 3 months - future.set_filter(lambda u: u.only_apply_filter_at_market_open().expiration(0, 95)) + # 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)) def on_data(self, slice: Slice) -> None: - # Get Future chain + # Get Future chain only for the selected Future contract chain = slice.future_chains.get(self._future) if not self.portfolio.invested and chain: + # It takes 2 contracts with different expiries to form a horizontal spread arbitration to earn price difference in contango if len(list(chain)) < 2: return - - # Get the far and near contract sorted_by_expiry = sorted(chain, key=lambda x: x.expiry) far_contract = sorted_by_expiry[-1] near_contract = sorted_by_expiry[0] # Check if the far contract price is 1% higher than the near one + # If so, short the far contract and buy the near one to earn the horizontal spread premium if far_contract.bid_price >= near_contract.ask_price * 1.01: - # If so, short the far contract and buy the near one to earn the premium self.market_order(far_contract.symbol, -1) self.market_order(near_contract.symbol, 1) def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate all positions if a contract expired and exit universe + # Liquidate if expired (or not being the front month contract anymore) and exit universe for removed in changes.removed_securities: self.liquidate()\ No newline at end of file From f85b91b0c1d327b86872bb34f5bcaa1ab1187562 Mon Sep 17 00:00:00 2001 From: Louis Szeto <56447733+LouisSzeto@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:39:28 +0800 Subject: [PATCH 3/3] Update 99 Examples.html --- .../01 Universes/99 Examples.html | 318 +++++++++--------- 1 file changed, 165 insertions(+), 153 deletions(-) diff --git a/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html index 311333870b..73342366b8 100644 --- a/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html +++ b/03 Writing Algorithms/03 Securities/99 Asset Classes/06 Futures/01 Requesting Data/01 Universes/99 Examples.html @@ -3,195 +3,207 @@
Future contracts expire monthly or quarterly in most cases. Hence, if we hold Future position in month or quarter end, we must consider rolling over to the mapped contract. The following algorithm shows how to buy and roll over to the next front month Future contract. We make use of the universe selection filter to select the front month contract and order the next mapped contract during the previous one expires.
private Symbol _future; - -public override void Initialize() +public class FutureExampleAlgorithm : QCAlgorithm { - 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) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); -} + private Symbol _future; -public override void OnSecuritiesChanged(SecurityChanges changes) -{ - // Liquidate if expired (or not being the front month contract anymore) and exit universe - foreach (var removed in changes.RemovedSecurities) + public override void Initialize() { - Liquidate(removed.Symbol); + 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) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); } - foreach (var added in changes.AddedSecurities) + public override void OnSecuritiesChanged(SecurityChanges changes) { - // Make sure the newly added contract is an actual mapped tradable contract - if (!added.Symbol.IsCanonical()) + // Liquidate if expired (or not being the front month contract anymore) and exit universe. + foreach (var removed in changes.RemovedSecurities) { - // Roll over by ordering the same quantity - // Use limit order since market on open order is not supported on Future and avoid extreme quote filling - LimitOrder(added.Symbol, 1m, Securities[_future].Price); + Liquidate(removed.Symbol); + } + + foreach (var added in changes.AddedSecurities) + { + // Make sure the newly added contract is an actual mapped tradable contract. + if (!added.Symbol.IsCanonical()) + { + // Roll over by ordering the same quantity. + // Use limit order since market on open order is not supported on Future and avoid extreme quote filling. + LimitOrder(added.Symbol, 1m, Securities[_future].Price); + } } } }-def initialize(self) -> None: - 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()) - -def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate if expired (or not being the front month contract anymore) and exit universe - for removed in changes.removed_securities: - self.liquidate(removed.symbol) +class FutureExampleAlgorithm(QCAlgorithm): + def initialize(self) -> None: + 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()) - for added in changes.added_securities: - # Make sure the newly added contract is an actual mapped tradable contract - if not added.symbol.is_canonical(): - # Roll over by ordering the same quantity - # Use limit order since market on open order is not supported on Future and avoid extreme quote filling - self.limit_order(added.symbol, 1, self.securities[self._future].price)+ def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate if expired (or not being the front month contract anymore) and exit universe. + for removed in changes.removed_securities: + self.liquidate(removed.symbol) + + for added in changes.added_securities: + # Make sure the newly added contract is an actual mapped tradable contract. + if not added.symbol.is_canonical(): + # Roll over by ordering the same quantity. + # Use limit order since market on open order is not supported on Future and avoid extreme quote filling. + self.limit_order(added.symbol, 1, self.securities[self._future].price)
One of the major applications of Continuous Future is to obtain smooth price series to feed into indicators. This can ensure the indicator gets the correct price data that is comparable to the current mapped Future contract. In this example, we demonstrate a 252-day Exponential Moving Average indicator update using continuous ES contract data.
private dynamic _future; - -public override void Initialize() +public class FutureExampleAlgorithm : QCAlgorithm { - // Use backward ratio normalization for continuous contract to feed smooth, comparable price series to the indicator - _future = AddFuture(Futures.Indices.SP500EMini, - dataNormalizationMode: DataNormalizationMode.BackwardsRatio, - extendedMarketHours: true); - // We only want to hold position of the front month contract - (_future as Future).SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); - // Create a 252-day EMA indicator as a trend estimator - _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); -} + private dynamic _future; -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)) + public override void Initialize() { - // Buy the mapped contract if the trend is estimated to go up (price above EMA) - if (_future.ema.Current.Value >= slice.Bars[_future.Symbol].Close) - { - SetHoldings(_future.Mapped, 0.1m); - } - // Short the mapped contract if the trend is estimated to go down (price below EMA) - else + // Use backward ratio normalization for continuous contract to feed smooth, comparable price series to the indicator. + _future = AddFuture(Futures.Indices.SP500EMini, + dataNormalizationMode: DataNormalizationMode.BackwardsRatio, + extendedMarketHours: true); + // We only want to hold position of the front month contract. + (_future as Future).SetFilter((u) => u.OnlyApplyFilterAtMarketOpen().FrontMonth()); + // Create a 252-day EMA indicator as a trend estimator. + _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); + } + + 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)) { - SetHoldings(_future.Mapped, -0.1m); + // Buy the mapped contract if the trend is estimated to go up (price above EMA). + if (_future.ema.Current.Value >= slice.Bars[_future.Symbol].Close) + { + SetHoldings(_future.Mapped, 0.1m); + } + // Short the mapped contract if the trend is estimated to go down (price below EMA). + else + { + SetHoldings(_future.Mapped, -0.1m); + } } } -} - -public override void OnSecuritiesChanged(SecurityChanges changes) -{ - // Liquidate if expired (or not being the front month contract anymore) and exit universe - foreach (var removed in changes.RemovedSecurities) + + public override void OnSecuritiesChanged(SecurityChanges changes) { - Liquidate(removed.Symbol); + // Liquidate if expired (or not being the front month contract anymore) and exit universe. + foreach (var removed in changes.RemovedSecurities) + { + Liquidate(removed.Symbol); + } } }-def initialize(self) -> None: - # Use backward ratio normalization for continuous contract to feed smooth, comparable price series to the indicator - self._future = self.add_future(Futures.Indices.SP_500_E_MINI, - 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()) - # 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) -> 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: - # Buy the mapped contract if the trend is estimated to go up (price above EMA) - if self._future.ema.current.value >= slice.bars[self._future.symbol].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: - self.set_holdings(self._future.mapped, -0.1) - -def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate if expired (or not being the front month contract anymore) and exit universe - for removed in changes.removed_securities: - self.liquidate(removed.symbol)+class FutureExampleAlgorithm(QCAlgorithm): + def initialize(self) -> None: + # Use backward ratio normalization for continuous contract to feed smooth, comparable price series to the indicator. + self._future = self.add_future(Futures.Indices.SP_500_E_MINI, + 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()) + # 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) -> 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: + # Buy the mapped contract if the trend is estimated to go up (price above EMA). + if self._future.ema.current.value >= slice.bars[self._future.symbol].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: + self.set_holdings(self._future.mapped, -0.1) + + def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate if expired (or not being the front month contract anymore) and exit universe. + for removed in changes.removed_securities: + self.liquidate(removed.symbol)
In Future trading, contango refers to the far-to-expiry Future contract price is higher than the spot price due to various reasons, such as storage fee and insurance of the commodities. The following example shows a contango trading by shorting the far contract that the price is above a threshold compared to the front month contract price and buying the front month contract to earn the premium in between.
private Symbol _future; - -public override void Initialize() +public class FutureExampleAlgorithm : QCAlgorithm { - // Allow extended market hours trade, which is common for Future - 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) => u.OnlyApplyFilterAtMarketOpen().Expiration(0, 183)); -} + private Symbol _future; -public override void OnData(Slice slice) -{ - // Get Future chain only for the selected Future contract - if (!Portfolio.Invested && slice.FutureChains.TryGetValue(_future, out var chain)) + public override void Initialize() { - // It takes 2 contracts with different expiries to form a horizontal spread arbitration to earn price difference in contango - if (chain.Count() < 2) return; - var farContract = chain.MaxBy(x => x.Expiry); - var nearContract = chain.MinBy(x => x.Expiry); - - // Check if the far contract price is 1% higher than the near one - // If so, short the far contract and buy the near one to earn the horizontal spread premium - if (farContract.BidPrice >= nearContract.AskPrice * 1.01m) + // Allow extended market hours trade, which is common for Future since extended market hour is still popular. + 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) => u.OnlyApplyFilterAtMarketOpen().Expiration(0, 183)); + } + + public override void OnData(Slice slice) + { + // Get Future chain only for the selected Future contract. + if (!Portfolio.Invested && slice.FutureChains.TryGetValue(_future, out var chain)) { - MarketOrder(farContract.Symbol, -1); - MarketOrder(nearContract.Symbol, 1); + // It takes 2 contracts with different expiries to form a horizontal spread arbitration to earn price difference in contango. + if (chain.Count() < 2) return; + var farContract = chain.MaxBy(x => x.Expiry); + var nearContract = chain.MinBy(x => x.Expiry); + + // Check if the far contract price is 1% higher than the near one. + // If so, short the far contract and buy the near one to earn the horizontal spread premium. + if (farContract.BidPrice >= nearContract.AskPrice * 1.01m) + { + MarketOrder(farContract.Symbol, -1); + MarketOrder(nearContract.Symbol, 1); + } } } -} - -public override void OnSecuritiesChanged(SecurityChanges changes) -{ - // Liquidate if expired (or not being the front month contract anymore) and exit universe - foreach (var removed in changes.RemovedSecurities) + + public override void OnSecuritiesChanged(SecurityChanges changes) { - Liquidate(); + // Liquidate if expired (or not being the front month contract anymore) and exit universe. + foreach (var removed in changes.RemovedSecurities) + { + Liquidate(); + } } }-def initialize(self) -> None: - # Allow extended market hours trade, which is common for Future - 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)) - -def on_data(self, slice: Slice) -> None: - # Get Future chain only for the selected Future contract - chain = slice.future_chains.get(self._future) - if not self.portfolio.invested and chain: - # It takes 2 contracts with different expiries to form a horizontal spread arbitration to earn price difference in contango - if len(list(chain)) < 2: - return - sorted_by_expiry = sorted(chain, key=lambda x: x.expiry) - far_contract = sorted_by_expiry[-1] - near_contract = sorted_by_expiry[0] - - # Check if the far contract price is 1% higher than the near one - # If so, short the far contract and buy the near one to earn the horizontal spread premium - if far_contract.bid_price >= near_contract.ask_price * 1.01: - self.market_order(far_contract.symbol, -1) - self.market_order(near_contract.symbol, 1) - -def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate if expired (or not being the front month contract anymore) and exit universe - for removed in changes.removed_securities: - self.liquidate()-
class FutureExampleAlgorithm(QCAlgorithm): + def initialize(self) -> None: + # Allow extended market hours trade, which is common for Future since extended market hour is still popular. + 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)) + + def on_data(self, slice: Slice) -> None: + # Get Future chain only for the selected Future contract. + chain = slice.future_chains.get(self._future) + if not self.portfolio.invested and chain: + # It takes 2 contracts with different expiries to form a horizontal spread arbitration to earn price difference in contango. + if len(list(chain)) < 2: + return + sorted_by_expiry = sorted(chain, key=lambda x: x.expiry) + far_contract = sorted_by_expiry[-1] + near_contract = sorted_by_expiry[0] + + # Check if the far contract price is 1% higher than the near one. + # If so, short the far contract and buy the near one to earn the horizontal spread premium. + if far_contract.bid_price >= near_contract.ask_price * 1.01: + self.market_order(far_contract.symbol, -1) + self.market_order(near_contract.symbol, 1) + + def on_securities_changed(self, changes: SecurityChanges) -> None: + # Liquidate if expired (or not being the front month contract anymore) and exit universe. + for removed in changes.removed_securities: + self.liquidate()+