| Overall Statistics |
|
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -5.908 Tracking Error 0.136 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% |
# from datetime import timedelta
# from QuantConnect import *
# from QuantConnect.Algorithm.Framework.Selection import FutureUniverseSelectionModel
# from QuantConnect.Data.UniverseSelection import *
# from QuantConnect.Securities.Future import FutureFilterUniverse
# from QuantConnect.Securities import MarketHoursDatabase
from AlgorithmImports import *
class OpenInterestFutureUniverseSelectionModel(FutureUniverseSelectionModel):
"""
This model selects futures contracts based on open interest. It extends the FutureUniverseSelectionModel
and applies an additional filter to sort the contracts by open interest.
Attributes:
algorithm (QCAlgorithm): The algorithm instance using this selection model.
future_chain_symbol_selector (Callable[[DateTime], List[Symbol]]): Function to select futures symbols.
_chain_contracts_lookup_limit (int): The maximum number of contracts to inspect for open interest.
_results_limit (int): The maximum number of contracts to return in the universe.
_market_hours_database (MarketHoursDatabase): Provides exchange hours needed for history requests.
"""
def __init__(self, algorithm, future_chain_symbol_selector, chain_contracts_lookup_limit=6, results_limit=1):
"""
Creates a new instance of the model.
Args:
algorithm (QCAlgorithm): The algorithm instance.
future_chain_symbol_selector (Callable[[DateTime], List[Symbol]]): Selector function for futures symbols.
chain_contracts_lookup_limit (int): Maximum number of contracts to query for open interest.
results_limit (int): Maximum number of contracts that will be part of the universe.
"""
super().__init__(timedelta(days=1), future_chain_symbol_selector)
self.algorithm = algorithm
self._chain_contracts_lookup_limit = chain_contracts_lookup_limit
self._results_limit = results_limit
self._market_hours_database = MarketHoursDatabase.FromDataFolder()
def Filter(self, filter: FutureFilterUniverse) -> FutureFilterUniverse:
"""
Overrides the base method to filter futures contracts based on open interest.
Args:
filter (FutureFilterUniverse): The future filter universe to apply the filtering.
Returns:
FutureFilterUniverse: The filtered universe of futures contracts.
"""
contracts = filter.ToDictionary(lambda x: x.Symbol, lambda x: self._market_hours_database.GetEntry(x.Symbol.ID.Market, x.Symbol, x.Symbol.ID.SecurityType))
return filter.Contracts(self.FilterByOpenInterest(contracts))
def FilterByOpenInterest(self, contracts):
"""
Filters the provided contracts by open interest.
Args:
contracts (Dictionary[Symbol, MarketHoursDatabase.Entry]): Contracts to filter.
Returns:
List[Symbol]: The list of symbols sorted by open interest and date.
"""
sorted_contracts = sorted(contracts.keys(), key=lambda x: x.ID.Date)
if self._chain_contracts_lookup_limit is not None:
sorted_contracts = sorted_contracts[:self._chain_contracts_lookup_limit]
open_interest_data = self.GetOpenInterest(contracts, sorted_contracts)
filtered_contracts = sorted(open_interest_data.items(), key=lambda x: (-x[1], x[0].ID.Date))
if self._results_limit is not None:
filtered_contracts = filtered_contracts[:self._results_limit]
return [symbol for symbol, _ in filtered_contracts]
def GetOpenInterest(self, contracts, symbols):
"""
Retrieves and processes historical open interest data for the given symbols.
Args:
contracts (Dictionary[Symbol, MarketHoursDatabase.Entry]): Contracts for which to retrieve open interest.
symbols (List[Symbol]): The list of symbols to get historical data for.
Returns:
Dictionary[Symbol, float]: A dictionary mapping each symbol to its latest open interest value.
"""
current_utc_time = self.algorithm.UtcTime
start_time = current_utc_time - timedelta(days=1)
# Request historical open interest data
history = self.algorithm.History(symbols, start_time, current_utc_time, Resolution.Daily)
open_interest_data = {}
for symbol in symbols:
try:
# Access the open interest data for the symbol
symbol_data = history.get(symbol)
if symbol_data.empty:
continue
open_interest_data[symbol] = symbol_data['openinterest'][-1]
except KeyError:
# If data is missing, log a message and skip the symbol
self.algorithm.Debug(f'No open interest data for {symbol.Value}')
return open_interest_data
from AlgorithmImports import *
# Define a custom universe selection model that inherits from OpenInterestFutureUniverseSelectionModel.
# This model will select futures contracts based on open interest.
class CustomFutureUniverseSelectionModel(OpenInterestFutureUniverseSelectionModel):
"""
A universe selection model that selects futures contracts based on open interest
and inherits the behavior of the OpenInterestFutureUniverseSelectionModel.
"""
def __init__(self, algorithm, chainContractsLookupLimit=6, resultsLimit=1):
"""
Initialize the custom universe selection model with the algorithm instance and limits for contracts lookup.
:param algorithm: The algorithm instance.
:param chainContractsLookupLimit: The maximum number of contracts to consider based on open interest.
:param resultsLimit: The maximum number of contracts to include in the universe.
"""
super().__init__(algorithm, self.select_future_chain_symbols, chainContractsLookupLimit, resultsLimit)
def select_future_chain_symbols(self, utcTime: datetime) -> List[Symbol]:
"""
Selects the future symbols to be included in the universe.
:param utcTime: The current UTC time as a datetime object.
:return: A list of Symbols representing the selected futures contracts.
"""
return [
Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME),
Symbol.Create(Futures.Metals.GOLD, SecurityType.Future, Market.COMEX)
]
from AlgorithmImports import *
from Universe import CustomFutureUniverseSelectionModel
# Main algorithm class that uses the CustomFutureUniverseSelectionModel for universe selection.
class OpenInterestFutureUniverseSelectionAlgorithm(QCAlgorithm):
"""
This algorithm uses a custom future universe selection model to select futures contracts based on open interest.
It logs the contract data and rollover events for analysis.
"""
def Initialize(self):
"""
Initialize the algorithm settings, including start date, cash, universe settings, and universe selection model.
"""
self.SetStartDate(2023, 1, 1)
self.SetEndDate(2023,2,1) # Start date for the backtest
self.SetCash(100000) # Starting cash for the algorithm
# Set properties of the universe settings
self.UniverseSettings.Resolution = Resolution.MINUTE
self.UniverseSettings.Leverage = 1.0
self.UniverseSettings.FillForward = True
self.UniverseSettings.ExtendedMarketHours = True
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
# Add the custom universe selection model to the algorithm
self.AddUniverseSelection(CustomFutureUniverseSelectionModel(self))
# Keep track of active contracts to detect rollovers
self.previous_active_contracts = set()
def OnData(self, slice: Slice):
"""
Event handler for new data.
:param slice: Slice object containing the data for the current time step.
"""
# Log the current time slice
self.Log(f"OnData for time slice: {self.Time}")
# Process data for each futures chain in the slice
if slice.FutureChains:
for chain in slice.FutureChains:
# Log the underlying Symbol for the current futures chain
self.Log(f"Processing future chain for underlying: {chain.Key.Value}")
# Iterate through contracts in the current chain
for contract in chain.Value:
# Log contract details including price and open interest
self.Log(f"Contract: {contract.Symbol.Value} Price: {contract.LastPrice} OpenInterest: {contract.OpenInterest}")
# Detect and log new active contracts based on open interest
if contract.Symbol not in self.previous_active_contracts and contract.OpenInterest > 0:
self.Log(f"New active contract detected based on open interest: {contract.Symbol.Value}")
# Update the active contracts list
self.previous_active_contracts.clear()
self.previous_active_contracts.add(contract.Symbol)
def OnSecuritiesChanged(self, changes: SecurityChanges):
"""
Event handler for changes in the securities in the universe, such as additions and removals.
:param changes: SecurityChanges object containing lists of added and removed securities.
"""
# Log securities that have been added to the universe
for security in changes.AddedSecurities:
self.Log(f"Added security: {security.Symbol.Value}")
# Log securities that have been removed from the universe
for security in changes.RemovedSecurities:
self.Log(f"Removed security: {security.Symbol.Value}")
from AlgorithmImports import *
class OpenInterestFutureUniverseSelectionAlgorithm(QCAlgorithm):
"""
Algorithm using the OpenInterestFutureUniverseSelectionModel to select futures contracts based on open interest
and prints out data and rollover events.
"""
def Initialize(self):
self.SetStartDate(2023, 1, 1) # Set the start date
# self.SetEndDate(2023, 6, 1) # Set the end date
self.SetCash(100000) # Set the starting cash
# Set UniverseSettings properties as required
self.UniverseSettings.Resolution = Resolution.Daily
self.UniverseSettings.Leverage = 1.0
self.UniverseSettings.FillForward = True
self.UniverseSettings.ExtendedMarketHours = True
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
# Add OpenInterestFutureUniverseSelectionModel with the selector function for the futures chains
self.AddUniverseSelection(
OpenInterestFutureUniverseSelectionModel(self, self.FutureChainSymbolSelector, chainContractsLookupLimit=6, resultsLimit=1)
)
# Keep track of the active contracts and rollover
self.previous_active_contracts = set()
def FutureChainSymbolSelector(self, utc_time: datetime) -> List[Symbol]:
# This function returns the future symbols to be included in the universe
return [
Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME),
Symbol.Create(Futures.Metals.GOLD, SecurityType.Future, Market.COMEX)
]
def OnData(self, slice: Slice):
# Log the time of the data slice
self.Log(f"OnData for time slice: {self.Time}")
if slice.FutureChains:
for chain in slice.FutureChains:
# Use 'chain.Key' to get the underlying Symbol for the futures chain
self.Log(f"Processing future chain for {chain.Key}")
for contract in chain.Value:
# Log the contract Symbol, Price, and OpenInterest for each contract in the chain
self.Log(f"Contract: {contract.Symbol.Value} Price: {contract.LastPrice} OpenInterest: {contract.OpenInterest}")
# Check if this is a newly active contract based on open interest
if contract.Symbol not in self.previous_active_contracts and contract.OpenInterest > 0:
# Log the new active contract
self.Log(f"New active contract detected based on open interest: {contract.Symbol.Value}")
# Clear the previous contracts and add this contract as the currently active one
self.previous_active_contracts.clear()
self.previous_active_contracts.add(contract.Symbol)
def OnSecuritiesChanged(self, changes: SecurityChanges):
# This method will be called when the active future contract changes, i.e., a rollover event
for security in changes.AddedSecurities:
self.Log(f"Added security: {security.Symbol.Value}")
for security in changes.RemovedSecurities:
self.Log(f"Removed security: {security.Symbol.Value}")