Skip to content

Commit

Permalink
Add Example to Writing Algorithms / Universes / Futures (#2055)
Browse files Browse the repository at this point in the history
* add trading logic in examples
  • Loading branch information
LouisSzeto authored Feb 4, 2025
1 parent 2b1a87b commit 1446853
Showing 1 changed file with 169 additions and 11 deletions.
180 changes: 169 additions & 11 deletions 03 Writing Algorithms/12 Universes/06 Futures/99 Examples.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,101 @@
<p>The following examples demonstrate some common Futures universes.</p>

<h4>Example 1: Contracts Expiring On Quarter End</h4>
<p>If you run strategies with a quarterly trading cycle, you can filter Future contracts that expire only on quarter ends to reduce the need of rollovers.</p>
<p>To reduce the need for rolling over, you can filter Future contracts that expire only on quarter ends to save transaction costs. The following example trades a simple EMA cross strategy on SP500 EMini Future contracts that expire on quarter ends.</p>
<div class="section-example-container">
<pre class="csharp">public class QuarterlyFuturesAlgorithm : QCAlgorithm
{
private Future _future;
private ExponentialMovingAverage _ema;
// A variable to control the algorithm trading in daily frequency only, since the indicator signal is by daily.
private int _day = -1;

public override void Initialize()
{
var future = AddFuture("ES");
future.SetFilter(futureFilterUniverse => futureFilterUniverse.ExpirationCycle(new int[] { 3, 6, 9, 12 }));
SetStartDate(2024, 3, 1);
SetEndDate(2024, 5, 1);
SetCash(10000000);

// Request ES Future data for trading. We only use the continuous contract for indicator values.
_future = AddFuture(
Futures.Indices.SP500EMini,
extendedMarketHours: true,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
dataMappingMode: DataMappingMode.OpenInterest
);
// We only filter the ones expiring on quarter ends to reduce the transaction cost needed for rolling over contracts.
_future.SetFilter(futureFilterUniverse =&gt; futureFilterUniverse.ExpirationCycle(new int[] { 3, 6, 9, 12 }));

// Create an EMA indicator to track trends for trade signal generation.
_ema = EMA(_future.Symbol, 20, Resolution.Daily);
// Warm up the indicator for immediate readiness.
WarmUpIndicator(_future.Symbol, _ema, Resolution.Daily);

}

public override void OnData(Slice slice)
{
// Get the chain to trade from the filtered contracts.
if (_day != slice.Time.Day &amp;&amp; slice.FutureChains.TryGetValue(_future.Symbol, out var chain))
{
// Select the contract with the nearest expiry.
var contract = chain.MinBy(x =&gt; x.Expiry);

// Trade an EMA cross strategy to follow the trend.
var quantity = Portfolio[_future.Symbol].Quantity;
if (contract.LastPrice &gt; _ema &amp;&amp; quantity &lt;= 0)
{
MarketOrder(contract.Symbol, 1-quantity);
}
else if (contract.LastPrice &lt; _ema &amp;&amp; quantity &gt;= 0)
{
MarketOrder(contract.Symbol, -1-quantity);
}

_day = slice.Time.Day;
}
}
}</pre>
<pre class="python">class QuarterlyFuturesAlgorithm(QCAlgorithm):

def initialize(self) -&gt; None:
self.set_start_date(2024, 3, 1)
self.set_end_date(2024, 5, 1)
self.set_cash(10000000)

# Request ES Future data for trading.
self.future = self.add_future(
Futures.Indices.SP_500_E_MINI,
extended_market_hours=True,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode=DataMappingMode.OPEN_INTEREST,
)
# We only filter the ones expiring on quarter ends to reduce the transaction cost needed for rolling over contracts.
self.future.set_filter(lambda future_filter_universe: future_filter_universe.expiration_cycle([3, 6, 9, 12]))

# Create an EMA indicator to track trends for trade signal generation.
self._ema = self.ema(self.future.symbol, 20, Resolution.DAILY)
# Warm up the indicator for immediate readiness.
self.warm_up_indicator(self.future.symbol, self._ema, Resolution.DAILY)

# A variable to control the algorithm trading in daily frequency only, since the indicator signal is by daily.
self._day = -1

def initialize(self):
future = self.add_future("ES")
future.set_filter(lambda future_filter_universe: future_filter_universe.expiration_cycle([3, 6, 9, 12]))</pre>
def on_data(self, slice: Slice) -&gt; None:
# Get the chain to trade from the filtered contracts.
chain = slice.future_chains.get(self.future.symbol)
if self._day != slice.time.day and chain:
# Select the contract with the nearest expiry.
contract = min(chain, key=lambda x: x.expiry)

# Trade an EMA cross strategy to follow the trend.
quantity = self.portfolio[self.future.symbol].quantity
if contract.last_price &gt; self._ema.current.value and quantity &lt;= 0:
self.market_order(contract.symbol, 1-quantity)
elif contract.last_price &lt; self._ema.current.value and quantity &gt;= 0:
self.market_order(contract.symbol, -1-quantity)

self._day = slice.time.day</pre>
</div>

<h4>Example 2: Contracts Expiring Within One Week</h4>
Expand All @@ -26,17 +106,95 @@ <h4>Example 2: Contracts Expiring Within One Week</h4>
<div class="section-example-container">
<pre class="csharp">public class WeeklyFuturesAlgorithm : QCAlgorithm
{
private Future _future;
private BollingerBands _bb;

public override void Initialize()
{
var future = AddFuture("ES");
future.SetFilter(futureFilterUniverse => futureFilterUniverse.Expiration(0, 7));
SetStartDate(2024, 6, 1);
SetEndDate(2024, 7, 1);
SetCash(100000000);

// Request ES Future data for trading. The continuous contract data is only used for indicator values.
_future = AddFuture(
Futures.Indices.SP500EMini,
extendedMarketHours: true,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
dataMappingMode: DataMappingMode.OpenInterest
);
// Select the contracts expiring within a week since they have the highest liquidity.
_future.SetFilter(futureFilterUniverse =&gt; futureFilterUniverse.Expiration(0, 7));

// Create a BollingerBand indicator to identify extreme prices for trade signal generation.
_bb = BB(_future.Symbol, 60, 2);
// Warm up the indicator for immediate readiness.
WarmUpIndicator(_future.Symbol, _bb, Resolution.Minute);
}

public override void OnData(Slice slice)
{
// Trade based on updated data.
if (slice.FutureChains.TryGetValue(_future.Symbol, out var chain))
{
// Select the contract with the nearest expiry.
var contract = chain.MinBy(x =&gt; x.Expiry);

// Trade a mean-reversal strategy based on Bollinger Band signal.
var quantity = Portfolio[_future.Symbol].Quantity;
if (contract.LastPrice &gt; _bb.UpperBand &amp;&amp; quantity &gt;= 0)
{
MarketOrder(contract.Symbol, -1-quantity);
}
else if (contract.LastPrice &lt; _bb.LowerBand &amp;&amp; quantity &lt;= 0)
{
MarketOrder(contract.Symbol, 1-quantity);
}
else if ((Portfolio[_future.Symbol].IsLong &amp;&amp; contract.LastPrice &gt; _bb.MiddleBand) ||
(Portfolio[_future.Symbol].IsShort &amp;&amp; contract.LastPrice &lt; _bb.MiddleBand))
{
Liquidate(contract.Symbol);
}
}
}
}</pre>
<pre class="python">class WeeklyFuturesAlgorithm(QCAlgorithm):

def initialize(self) -&gt; None:
self.set_start_date(2024, 6, 1)
self.set_end_date(2024, 7, 1)
self.set_cash(100000000)

# Request ES Future data for trading. The continuous contract data is only used for indicator values.
self.future = self.add_future(
Futures.Indices.SP_500_E_MINI,
extended_market_hours=True,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode=DataMappingMode.OPEN_INTEREST
)
# Select the contracts expiring within a week since they have the highest liquidity.
self.future.set_filter(lambda future_filter_universe: future_filter_universe.expiration(0, 7))

# Create a BollingerBand indicator to identify extreme prices for trade signal generation.
self._bb = self.bb(self.future.symbol, 60, 2)
# Warm up the indicator for immediate readiness.
self.warm_up_indicator(self.future.symbol, self._bb, Resolution.MINUTE)

def on_data(self, slice: Slice) -&gt; None:
# Trade based on updated data.
chain = slice.future_chain.get(self.future.symbol)
if chain:
# Select the contract with the nearest expiry.
contract = min(chain, key=lambda x: x.expiry)

def initialize(self):
future = self.add_future("ES")
future.set_filter(lambda future_filter_universe: future_filter_universe.expiration(0, 7))</pre>
# Trade a mean-reversal strategy based on Bollinger Band signal.
quantity = self.portfolio[self.future.symbol].quantity
if contract.last_price &gt; self._bb.upper_band.current.value and quantity >= 0:
self.market_order(contract.symbol, -1-quantity)
elif contract.last_price &lt; self._bb.lower_band.current.value and quantity <= 0:
self.market_order(contract.symbol, 1-quantity)
elif (self.portfolio[self.future.symbol].is_short and contract.last_price &lt; self._bb.middle_band.current.value)\
or (self.portfolio[self.future.symbol].is_long and contract.last_price &gt; self._bb.middle_band.current.value):
self.liquidate(contract.symbol)</pre>
</div>

<h4>Other Examples</h4>
Expand Down

0 comments on commit 1446853

Please sign in to comment.