chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,43 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Test algorithm using 'AccumulativeInsightPortfolioConstructionModel.py' and 'ConstantAlphaModel'
### generating a constant 'Insight'
### </summary>
class AccumulativeInsightPortfolioRegressionAlgorithm(QCAlgorithm):
def initialize(self):
''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
# Set requested data resolution
self.universe_settings.resolution = Resolution.MINUTE
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
symbols = [ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]
# set algorithm framework models
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(minutes = 20), 0.025, 0.25))
self.set_portfolio_construction(AccumulativeInsightPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
def on_end_of_algorithm(self):
# holdings value should be 0.03 - to avoid price fluctuation issue we compare with 0.06 and 0.01
if (self.portfolio.total_holdings_value > self.portfolio.total_portfolio_value * 0.06
or self.portfolio.total_holdings_value < self.portfolio.total_portfolio_value * 0.01):
raise ValueError("Unexpected Total Holdings Value: " + str(self.portfolio.total_holdings_value))
@@ -0,0 +1,56 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Test algorithm using 'QCAlgorithm.add_alpha_model()'
### </summary>
class AddAlphaModelAlgorithm(QCAlgorithm):
def initialize(self):
''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
self.universe_settings.resolution = Resolution.DAILY
spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
fb = Symbol.create("FB", SecurityType.EQUITY, Market.USA)
ibm = Symbol.create("IBM", SecurityType.EQUITY, Market.USA)
# set algorithm framework models
self.set_universe_selection(ManualUniverseSelectionModel([ spy, fb, ibm ]))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.add_alpha(OneTimeAlphaModel(spy))
self.add_alpha(OneTimeAlphaModel(fb))
self.add_alpha(OneTimeAlphaModel(ibm))
class OneTimeAlphaModel(AlphaModel):
def __init__(self, symbol):
self.symbol = symbol
self.triggered = False
def update(self, algorithm, data):
insights = []
if not self.triggered:
self.triggered = True
insights.append(Insight.price(
self.symbol,
Resolution.DAILY,
1,
InsightDirection.DOWN))
return insights
@@ -0,0 +1,94 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This regression algorithm tests that we receive the expected data when
### we add future option contracts individually using <see cref="AddFutureOptionContract"/>
### </summary>
class AddFutureOptionContractDataStreamingRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.on_data_reached = False
self.invested = False
self.symbols_received = []
self.expected_symbols_received = []
self.data_received = {}
self.set_start_date(2020, 1, 4)
self.set_end_date(2020, 1, 8)
self.es20h20 = self.add_future_contract(
Symbol.create_future(Futures.Indices.SP_500_E_MINI, Market.CME, datetime(2020, 3, 20)),
Resolution.MINUTE).symbol
self.es19m20 = self.add_future_contract(
Symbol.create_future(Futures.Indices.SP_500_E_MINI, Market.CME, datetime(2020, 6, 19)),
Resolution.MINUTE).symbol
# Get option contract lists for 2020/01/05 (timedelta(days=1)) because Lean has local data for that date
option_chains = list(self.option_chain_provider.get_option_contract_list(self.es20h20, self.time + timedelta(days=1)))
option_chains += self.option_chain_provider.get_option_contract_list(self.es19m20, self.time + timedelta(days=1))
for option_contract in option_chains:
self.expected_symbols_received.append(self.add_future_option_contract(option_contract, Resolution.MINUTE).symbol)
def on_data(self, data: Slice):
if not data.has_data:
return
self.on_data_reached = True
has_option_quote_bars = False
for qb in data.quote_bars.values():
if qb.symbol.security_type != SecurityType.FUTURE_OPTION:
continue
has_option_quote_bars = True
self.symbols_received.append(qb.symbol)
if qb.symbol not in self.data_received:
self.data_received[qb.symbol] = []
self.data_received[qb.symbol].append(qb)
if self.invested or not has_option_quote_bars:
return
if data.contains_key(self.es20h20) and data.contains_key(self.es19m20):
self.set_holdings(self.es20h20, 0.2)
self.set_holdings(self.es19m20, 0.2)
self.invested = True
def on_end_of_algorithm(self):
self.symbols_received = list(set(self.symbols_received))
self.expected_symbols_received = list(set(self.expected_symbols_received))
if not self.on_data_reached:
raise AssertionError("OnData() was never called.")
if len(self.symbols_received) != len(self.expected_symbols_received):
raise AssertionError(f"Expected {len(self.expected_symbols_received)} option contracts Symbols, found {len(self.symbols_received)}")
missing_symbols = [expected_symbol.value for expected_symbol in self.expected_symbols_received if expected_symbol not in self.symbols_received]
if any(missing_symbols):
raise AssertionError(f'Symbols: "{", ".join(missing_symbols)}" were not found in OnData')
for expected_symbol in self.expected_symbols_received:
data = self.data_received[expected_symbol]
for data_point in data:
data_point.end_time = datetime(1970, 1, 1)
non_dupe_data_count = len(set(data))
if non_dupe_data_count < 1000:
raise AssertionError(f"Received too few data points. Expected >=1000, found {non_dupe_data_count} for {expected_symbol}")
@@ -0,0 +1,118 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This regression algorithm tests that we only receive the option chain for a single future contract
### in the option universe filter.
### </summary>
class AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.invested = False
self.on_data_reached = False
self.option_filter_ran = False
self.symbols_received = []
self.expected_symbols_received = []
self.data_received = {}
self.set_start_date(2020, 1, 4)
self.set_end_date(2020, 1, 8)
self.es = self.add_future(Futures.Indices.SP_500_E_MINI, Resolution.MINUTE, Market.CME)
self.es.set_filter(lambda future_filter: future_filter.expiration(0, 365).expiration_cycle([3, 6]))
self.add_future_option(self.es.symbol, self.option_contract_universe_filter_function)
def option_contract_universe_filter_function(self, option_contracts: OptionFilterUniverse) -> OptionFilterUniverse:
self.option_filter_ran = True
expiry_dates = list(set([x.symbol.underlying.id.date for x in option_contracts]))
expiry = None if not any(expiry_dates) else expiry_dates[0]
symbols = [x.symbol.underlying for x in option_contracts]
symbol = None if not any(symbols) else symbols[0]
if expiry is None or symbol is None:
raise AssertionError("Expected a single Option contract in the chain, found 0 contracts")
self.expected_symbols_received.extend([x.symbol for x in option_contracts])
return option_contracts
def on_data(self, data: Slice):
if not data.has_data:
return
self.on_data_reached = True
has_option_quote_bars = False
for qb in data.quote_bars.values():
if qb.symbol.security_type != SecurityType.FUTURE_OPTION:
continue
has_option_quote_bars = True
self.symbols_received.append(qb.symbol)
if qb.symbol not in self.data_received:
self.data_received[qb.symbol] = []
self.data_received[qb.symbol].append(qb)
if self.invested or not has_option_quote_bars:
return
for chain in sorted(data.option_chains.values(), key=lambda chain: chain.symbol.underlying.id.date):
future_invested = False
option_invested = False
for option in chain.contracts.keys():
if future_invested and option_invested:
return
future = option.underlying
if not option_invested and data.contains_key(option):
self.market_order(option, 1)
self.invested = True
option_invested = True
if not future_invested and data.contains_key(future):
self.market_order(future, 1)
self.invested = True
future_invested = True
def on_end_of_algorithm(self):
super().on_end_of_algorithm()
self.symbols_received = list(set(self.symbols_received))
self.expected_symbols_received = list(set(self.expected_symbols_received))
if not self.option_filter_ran:
raise AssertionError("Option chain filter was never ran")
if not self.on_data_reached:
raise AssertionError("OnData() was never called.")
if len(self.symbols_received) != len(self.expected_symbols_received):
raise AssertionError(f"Expected {len(self.expected_symbols_received)} option contracts Symbols, found {len(self.symbols_received)}")
missing_symbols = [expected_symbol for expected_symbol in self.expected_symbols_received if expected_symbol not in self.symbols_received]
if any(missing_symbols):
raise AssertionError(f'Symbols: "{", ".join(missing_symbols)}" were not found in OnData')
for expected_symbol in self.expected_symbols_received:
data = self.data_received[expected_symbol]
for data_point in data:
data_point.end_time = datetime(1970, 1, 1)
non_dupe_data_count = len(set(data))
if non_dupe_data_count < 1000:
raise AssertionError(f"Received too few data points. Expected >=1000, found {non_dupe_data_count} for {expected_symbol}")
@@ -0,0 +1,41 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to use the FutureUniverseSelectionModel to select futures contracts for a given underlying asset.
### The model is set to update daily, and the algorithm ensures that the selected contracts meet specific criteria.
### This also includes a check to ensure that only future contracts are added to the algorithm's universe.
### </summary>
class AddFutureUniverseSelectionModelRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2013, 10, 10)
self.set_universe_selection(FutureUniverseSelectionModel(
timedelta(days=1),
lambda time: [ Symbol.create(Futures.Indices.SP_500_E_MINI, SecurityType.FUTURE, Market.CME) ]
))
def on_securities_changed(self, changes):
if len(changes.added_securities) > 0:
for security in changes.added_securities:
if security.symbol.security_type != SecurityType.FUTURE:
raise RegressionTestException(f"Expected future security, but found '{security.symbol.security_type}'")
if security.symbol.id.symbol != "ES":
raise RegressionTestException(f"Expected future symbol 'ES', but found '{security.symbol.id.symbol}'")
def on_end_of_algorithm(self):
if len(self.active_securities) == 0:
raise RegressionTestException("No active securities found. Expected at least one active security")
@@ -0,0 +1,63 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### We add an option contract using 'QCAlgorithm.add_option_contract' and place a trade, the underlying
### gets deselected from the universe selection but should still be present since we manually added the option contract.
### Later we call 'QCAlgorithm.remove_option_contract' and expect both option and underlying to be removed.
### </summary>
class AddOptionContractExpiresRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2014, 6, 5)
self.set_end_date(2014, 6, 30)
self._expiration = datetime(2014, 6, 21)
self._option = None
self._traded = False
self._twx = Symbol.create("TWX", SecurityType.EQUITY, Market.USA)
self.add_universe("my-daily-universe-name", self.selector)
def selector(self, time):
return [ "AAPL" ]
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if self._option == None:
chain = self.option_chain(self._twx)
options = sorted(chain, key=lambda x: x.id.symbol)
option = next((option
for option in options
if option.id.date == self._expiration and
option.id.option_right == OptionRight.CALL and
option.id.option_style == OptionStyle.AMERICAN), None)
if option != None:
self._option = self.add_option_contract(option).symbol
if self._option != None and self.securities[self._option].price != 0 and not self._traded:
self._traded = True
self.buy(self._option, 1)
if self.time > self._expiration and self.securities[self._twx].invested:
# we liquidate the option exercised position
self.liquidate(self._twx)
@@ -0,0 +1,83 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### We add an option contract using 'QCAlgorithm.add_option_contract' and place a trade, the underlying
### gets deselected from the universe selection but should still be present since we manually added the option contract.
### Later we call 'QCAlgorithm.remove_option_contract' and expect both option and underlying to be removed.
### </summary>
class AddOptionContractFromUniverseRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2014, 6, 5)
self.set_end_date(2014, 6, 9)
self._expiration = datetime(2014, 6, 21)
self._security_changes = None
self._option = None
self._traded = False
self._twx = Symbol.create("TWX", SecurityType.EQUITY, Market.USA)
self._aapl = Symbol.create("AAPL", SecurityType.EQUITY, Market.USA)
self.universe_settings.resolution = Resolution.MINUTE
self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
self.add_universe(self.selector, self.selector)
def selector(self, fundamental):
if self.time <= datetime(2014, 6, 5):
return [ self._twx ]
return [ self._aapl ]
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if self._option != None and self.securities[self._option].price != 0 and not self._traded:
self._traded = True
self.buy(self._option, 1)
if self.time == datetime(2014, 6, 6, 14, 0, 0):
# liquidate & remove the option
self.remove_option_contract(self._option)
def on_securities_changed(self, changes):
# keep track of all removed and added securities
if self._security_changes == None:
self._security_changes = changes
else:
self._security_changes += changes
if any(security.symbol.security_type == SecurityType.OPTION for security in changes.added_securities):
return
for addedSecurity in changes.added_securities:
option_chain = self.option_chain(addedSecurity.symbol)
options = sorted(option_chain, key=lambda x: x.id.symbol)
option = next((option
for option in options
if option.id.date == self._expiration and
option.id.option_right == OptionRight.CALL and
option.id.option_style == OptionStyle.AMERICAN), None)
self.add_option_contract(option)
# just keep the first we got
if self._option == None:
self._option = option
@@ -0,0 +1,55 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to use the OptionUniverseSelectionModel to select options contracts based on specified conditions.
### The model is updated daily and selects different options based on the current date.
### The algorithm ensures that only valid option contracts are selected for the universe.
### </summary>
class AddOptionUniverseSelectionModelRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2014, 6, 5)
self.set_end_date(2014, 6, 6)
self.option_count = 0
self.universe_settings.resolution = Resolution.MINUTE
self.set_universe_selection(OptionUniverseSelectionModel(
timedelta(days=1),
self.select_option_chain_symbols
))
def select_option_chain_symbols(self, utc_time):
new_york_time = Extensions.convert_from_utc(utc_time, TimeZones.NEW_YORK)
if new_york_time.date() < datetime(2014, 6, 6).date():
return [ Symbol.create("TWX", SecurityType.OPTION, Market.USA) ]
if new_york_time.date() >= datetime(2014, 6, 6).date():
return [ Symbol.create("AAPL", SecurityType.OPTION, Market.USA) ]
def on_securities_changed(self, changes):
if len(changes.added_securities) > 0:
for security in changes.added_securities:
symbol = security.symbol.underlying if security.symbol.underlying else security.Symbol
if symbol.value != "AAPL" and symbol.value != "TWX":
raise RegressionTestException(f"Unexpected security {security.Symbol}")
if security.symbol.security_type == SecurityType.OPTION:
self.option_count += 1
def on_end_of_algorithm(self):
if len(self.active_securities) == 0:
raise RegressionTestException("No active securities found. Expected at least one active security")
if self.option_count == 0:
raise RegressionTestException("The option count should be greater than 0")
@@ -0,0 +1,65 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This algorithm demonstrates the runtime addition and removal of securities from your algorithm.
### With LEAN it is possible to add and remove securities after the initialization.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="assets" />
### <meta name="tag" content="regression test" />
class AddRemoveSecurityRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.add_equity("SPY")
self._last_action = None
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
if self._last_action is not None and self._last_action.date() == self.time.date():
return
if not self.portfolio.invested:
self.set_holdings("SPY", .5)
self._last_action = self.time
if self.time.weekday() == 1:
self.add_equity("AIG")
self.add_equity("BAC")
self._last_action = self.time
if self.time.weekday() == 2:
self.set_holdings("AIG", .25)
self.set_holdings("BAC", .25)
self._last_action = self.time
if self.time.weekday() == 3:
self.remove_security("AIG")
self.remove_security("BAC")
self._last_action = self.time
def on_order_event(self, order_event):
if order_event.status == OrderStatus.SUBMITTED:
self.debug("{0}: Submitted: {1}".format(self.time, self.transactions.get_order_by_id(order_event.order_id)))
if order_event.status == OrderStatus.FILLED:
self.debug("{0}: Filled: {1}".format(self.time, self.transactions.get_order_by_id(order_event.order_id)))
@@ -0,0 +1,44 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Test algorithm using 'QCAlgorithm.add_risk_management(IRiskManagementModel)'
### </summary>
class AddRiskManagementAlgorithm(QCAlgorithm):
'''Basic template framework algorithm uses framework components to define the algorithm.'''
def initialize(self):
''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.universe_settings.resolution = Resolution.MINUTE
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
symbols = [ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]
# set algorithm framework models
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(minutes = 20), 0.025, None))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
# Both setting methods should work
risk_model = CompositeRiskManagementModel(MaximumDrawdownPercentPortfolio(0.02))
risk_model.add_risk_management(MaximumUnrealizedProfitPercentPerSecurity(0.01))
self.set_risk_management(MaximumDrawdownPercentPortfolio(0.02))
self.add_risk_management(MaximumUnrealizedProfitPercentPerSecurity(0.01))
@@ -0,0 +1,44 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Test algorithm using 'QCAlgorithm.add_universe_selection(IUniverseSelectionModel)'
### </summary>
class AddUniverseSelectionModelAlgorithm(QCAlgorithm):
def initialize(self):
''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10,8) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
self.universe_settings.resolution = Resolution.DAILY
# set algorithm framework models
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(minutes = 20), 0.025, None))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_universe_selection(ManualUniverseSelectionModel([ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]))
self.add_universe_selection(ManualUniverseSelectionModel([ Symbol.create("AAPL", SecurityType.EQUITY, Market.USA) ]))
self.add_universe_selection(ManualUniverseSelectionModel(
Symbol.create("SPY", SecurityType.EQUITY, Market.USA), # duplicate will be ignored
Symbol.create("FB", SecurityType.EQUITY, Market.USA)))
def on_end_of_algorithm(self):
if self.universe_manager.count != 3:
raise ValueError("Unexpected universe count")
if self.universe_manager.active_securities.count != 3:
raise ValueError("Unexpected active securities")
@@ -0,0 +1,44 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Algorithm asserting the correct values for the deployment target and algorithm mode.
### </summary>
class AlgorithmModeAndDeploymentTargetAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013,10, 7)
self.set_end_date(2013,10,11)
self.set_cash(100000)
#translate commented code from c# to python
self.debug(f"Algorithm Mode: {self.algorithm_mode}. Is Live Mode: {self.live_mode}. Deployment Target: {self.deployment_target}.")
if self.algorithm_mode != AlgorithmMode.BACKTESTING:
raise AssertionError(f"Algorithm mode is not backtesting. Actual: {self.algorithm_mode}")
if self.live_mode:
raise AssertionError("Algorithm should not be live")
if self.deployment_target != DeploymentTarget.LOCAL_PLATFORM:
raise AssertionError(f"Algorithm deployment target is not local. Actual{self.deployment_target}")
# For a live deployment these checks should pass:
# if self.algorithm_mode != AlgorithmMode.LIVE: raise AssertionError("Algorithm mode is not live")
# if not self.live_mode: raise AssertionError("Algorithm should be live")
# For a cloud deployment these checks should pass:
# if self.deployment_target != DeploymentTarget.CLOUD_PLATFORM: raise AssertionError("Algorithm deployment target is not cloud")
self.quit()
@@ -0,0 +1,137 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Tests filtering in coarse selection by shortable quantity
### </summary>
class AllShortableSymbolsCoarseSelectionRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self._20140325 = datetime(2014, 3, 25)
self._20140326 = datetime(2014, 3, 26)
self._20140327 = datetime(2014, 3, 27)
self._20140328 = datetime(2014, 3, 28)
self._20140329 = datetime(2014, 3, 29)
self.last_trade_date = date(1,1,1)
self._aapl = Symbol.create("AAPL", SecurityType.EQUITY, Market.USA)
self._bac = Symbol.create("BAC", SecurityType.EQUITY, Market.USA)
self._gme = Symbol.create("GME", SecurityType.EQUITY, Market.USA)
self._goog = Symbol.create("GOOG", SecurityType.EQUITY, Market.USA)
self._qqq = Symbol.create("QQQ", SecurityType.EQUITY, Market.USA)
self._spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
self.coarse_selected = { self._20140325: False, self._20140326: False, self._20140327:False, self._20140328:False }
self.expected_symbols = { self._20140325: [ self._bac, self._qqq, self._spy ],
self._20140326: [ self._spy ],
self._20140327: [ self._aapl, self._bac, self._gme, self._qqq, self._spy ],
self._20140328: [ self._goog ],
self._20140329: []}
self.set_start_date(2014, 3, 25)
self.set_end_date(2014, 3, 29)
self.set_cash(10000000)
self.shortable_provider = RegressionTestShortableProvider()
self.security = self.add_equity(self._spy)
self.add_universe(self.coarse_selection)
self.universe_settings.resolution = Resolution.DAILY
self.set_brokerage_model(AllShortableSymbolsRegressionAlgorithmBrokerageModel(self.shortable_provider))
def on_data(self, data):
if self.time.date() == self.last_trade_date:
return
for (symbol, security) in {x.key: x.value for x in sorted(self.active_securities, key = lambda kvp:kvp.key)}.items():
if security.invested:
continue
shortable_quantity = security.shortable_provider.shortable_quantity(symbol, self.time)
if not shortable_quantity:
raise AssertionError(f"Expected {symbol} to be shortable on {self.time.strftime('%Y%m%d')}")
"""
Buy at least once into all Symbols. Since daily data will always use
MOO orders, it makes the testing of liquidating buying into Symbols difficult.
"""
self.market_order(symbol, -shortable_quantity)
self.last_trade_date = self.time.date()
def coarse_selection(self, coarse):
shortable_symbols = self.shortable_provider.all_shortable_symbols(self.time)
selected_symbols = list(sorted(filter(lambda x: (x in shortable_symbols.keys()) and (shortable_symbols[x] >= 500), map(lambda x: x.symbol, coarse)), key= lambda x: x.value))
expected_missing = 0
if self.time.date() == self._20140327.date():
gme = Symbol.create("GME", SecurityType.EQUITY, Market.USA)
if gme not in shortable_symbols.keys():
raise AssertionError("Expected unmapped GME in shortable symbols list on 2014-03-27")
if "GME" not in list(map(lambda x: x.symbol.value, coarse)):
raise AssertionError("Expected mapped GME in coarse symbols on 2014-03-27")
expected_missing = 1
missing = list(filter(lambda x: x not in selected_symbols, self.expected_symbols[self.time]))
if len(missing) != expected_missing:
raise AssertionError(f"Expected Symbols selected on {self.time.strftime('%Y%m%d')} to match expected Symbols, but the following Symbols were missing: {', '.join(list(map(lambda x:x.value, missing)))}")
self.coarse_selected[self.time] = True
return selected_symbols
def on_end_of_algorithm(self):
if not all(x for x in self.coarse_selected.values()):
raise AssertionError(f"Expected coarse selection on all dates, but didn't run on: {', '.join(list(map(lambda x: x[0].strftime('%Y%m%d'), filter(lambda x:not x[1], self.coarse_selected.items()))))}")
class AllShortableSymbolsRegressionAlgorithmBrokerageModel(DefaultBrokerageModel):
def __init__(self, shortable_provider):
self.shortable_provider = shortable_provider
super().__init__()
def get_shortable_provider(self, security):
return self.shortable_provider
class RegressionTestShortableProvider(LocalDiskShortableProvider):
def __init__(self):
super().__init__("testbrokerage")
"""
Gets a list of all shortable Symbols, including the quantity shortable as a Dictionary.
"""
def all_shortable_symbols(self, localtime):
shortable_data_directory = os.path.join(Globals.data_folder, "equity", Market.USA, "shortable", self.brokerage)
all_symbols = {}
"""
Check backwards up to one week to see if we can source a previous file.
If not, then we return a list of all Symbols with quantity set to zero.
"""
i = 0
while i <= 7:
shortable_list_file = os.path.join(shortable_data_directory, "dates", f"{(localtime - timedelta(days=i)).strftime('%Y%m%d')}.csv")
for line in Extensions.read_lines(self.data_provider, shortable_list_file):
csv = line.split(',')
ticker = csv[0]
symbol = Symbol(SecurityIdentifier.generate_equity(ticker, Market.USA, mapping_resolve_date = localtime), ticker)
quantity = int(csv[1])
all_symbols[symbol] = quantity
if len(all_symbols) > 0:
return all_symbols
i += 1
# Return our empty dictionary if we did not find a file to extract
return all_symbols
@@ -0,0 +1,179 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
import scipy.stats as sp
from Risk.NullRiskManagementModel import NullRiskManagementModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
class ContingentClaimsAnalysisDefaultPredictionAlpha(QCAlgorithm):
''' Contingent Claim Analysis is put forth by Robert Merton, recepient of the Noble Prize in Economics in 1997 for his work in contributing to
Black-Scholes option pricing theory, which says that the equity market value of stockholders equity is given by the Black-Scholes solution
for a European call option. This equation takes into account Debt, which in CCA is the equivalent to a strike price in the BS solution. The probability
of default on corporate debt can be calculated as the N(-d2) term, where d2 is a function of the interest rate on debt(µ), face value of the debt (B), value of the firm's assets (V),
standard deviation of the change in a firm's asset value (σ), the dividend and interest payouts due (D), and the time to maturity of the firm's debt(τ). N(*) is the cumulative
distribution function of a standard normal distribution, and calculating N(-d2) gives us the probability of the firm's assets being worth less
than the debt of the company at the time that the debt reaches maturity -- that is, the firm doesn't have enough in assets to pay off its debt and defaults.
We use a Fine/Coarse Universe Selection model to select small cap stocks, who we postulate are more likely to default
on debt in general than blue-chip companies, and extract Fundamental data to plug into the CCA formula.
This Alpha emits insights based on whether or not a company is likely to default given its probability of default vs a default probability threshold that we set arbitrarily.
Prob. default (on principal B at maturity T) = Prob(VT < B) = 1 - N(d2) = N(-d2) where -d2(µ) = -{ln(V/B) + [(µ - D) - ½σ2]τ}/ σ √τ.
N(d) = (univariate) cumulative standard normal distribution function (from -inf to d)
B = face value (principal) of the debt
D = dividend + interest payout
V = value of firms assets
σ (sigma) = standard deviation of firm value changes (returns in V)
τ (tau) = time to debts maturity
µ (mu) = interest rate
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.'''
def initialize(self):
## Set requested data resolution and variables to help with Universe Selection control
self.universe_settings.resolution = Resolution.DAILY
self.month = -1
## Declare single variable to be passed in multiple places -- prevents issue with conflicting start dates declared in different places
self.set_start_date(2018,1,1)
self.set_cash(100000)
## SPDR Small Cap ETF is a better benchmark than the default SP500
self.set_benchmark('IJR')
## Set Universe Selection Model
self.set_universe_selection(FineFundamentalUniverseSelectionModel(self.coarse_selection_function, self.fine_selection_function))
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
## Set CCA Alpha Model
self.set_alpha(ContingentClaimsAnalysisAlphaModel())
## Set Portfolio Construction Model
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Risk Management Model
self.set_risk_management(NullRiskManagementModel())
def coarse_selection_function(self, coarse):
## Boolean controls so that our symbol universe is only updated once per month
if self.time.month == self.month:
return Universe.UNCHANGED
self.month = self.time.month
## Sort by dollar volume, lowest to highest
sorted_by_dollar_volume = sorted([x for x in coarse if x.has_fundamental_data],
key=lambda x: x.dollar_volume, reverse=True)
## Return smallest 750 -- idea is that smaller companies are most likely to go bankrupt than blue-chip companies
## Filter for assets with fundamental data
return [x.symbol for x in sorted_by_dollar_volume[:750]]
def fine_selection_function(self, fine):
def is_valid(x):
statement = x.financial_statements
sheet = statement.balance_sheet
total_assets = sheet.total_assets
ratios = x.operation_ratios
return total_assets.one_month > 0 and \
total_assets.three_months > 0 and \
total_assets.six_months > 0 and \
total_assets.twelve_months > 0 and \
sheet.current_liabilities.twelve_months > 0 and \
sheet.interest_payable.twelve_months > 0 and \
ratios.total_assets_growth.one_year > 0 and \
statement.income_statement.gross_dividend_payment.twelve_months > 0 and \
ratios.roa.one_year > 0
return [x.symbol for x in sorted(fine, key=lambda x: is_valid(x))]
class ContingentClaimsAnalysisAlphaModel(AlphaModel):
def __init__(self, *args, **kwargs):
self.probability_of_default_by_symbol = {}
self.default_threshold = kwargs['default_threshold'] if 'default_threshold' in kwargs else 0.25
def update(self, algorithm, data):
'''Updates this alpha model with the latest data from the algorithm.
This is called each time the algorithm receives data for subscribed securities
Args:
algorithm: The algorithm instance
data: The new data available
Returns:
The new insights generated'''
## Build a list to hold our insights
insights = []
for symbol, pod in self.probability_of_default_by_symbol.items():
## If Prob. of Default is greater than our set threshold, then emit an insight indicating that this asset is trending downward
if pod >= self.default_threshold and pod != 1.0:
insights.append(Insight.price(symbol, timedelta(30), InsightDirection.DOWN, pod, None))
return insights
def on_securities_changed(self, algorithm, changes):
for removed in changes.removed_securities:
self.probability_of_default_by_symbol.pop(removed.symbol, None)
# initialize data for added securities
symbols = [ x.symbol for x in changes.added_securities ]
for symbol in symbols:
if symbol not in self.probability_of_default_by_symbol:
## CCA valuation
pod = self.get_probability_of_default(algorithm, symbol)
if pod is not None:
self.probability_of_default_by_symbol[symbol] = pod
def get_probability_of_default(self, algorithm, symbol):
'''This model applies options pricing theory, Black-Scholes specifically,
to fundamental data to give the probability of a default'''
security = algorithm.securities[symbol]
if security.fundamentals is None or security.fundamentals.financial_statements is None or security.fundamentals.operation_ratios is None:
return None
statement = security.fundamentals.financial_statements
sheet = statement.balance_sheet
total_assets = sheet.total_assets
tau = 360 ## Days
mu = security.fundamentals.operation_ratios.roa.one_year
V = total_assets.twelve_months
B = sheet.current_liabilities.twelve_months
D = statement.income_statement.gross_dividend_payment.twelve_months + sheet.interest_payable.twelve_months
series = pd.Series(
[
total_assets.one_month,
total_assets.three_months,
total_assets.six_months,
V
])
sigma = series.iloc[series.nonzero()[0]]
sigma = np.std(sigma.pct_change()[1:len(sigma)])
d2 = ((np.log(V) - np.log(B)) + ((mu - D) - 0.5*sigma**2.0)*tau)/ (sigma*np.sqrt(tau))
return sp.norm.cdf(-d2)
@@ -0,0 +1,219 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''
Energy prices, especially Oil and Natural Gas, are in general fairly correlated,
meaning they typically move in the same direction as an overall trend. This Alpha
uses this idea and implements an Alpha Model that takes Natural Gas ETF price
movements as a leading indicator for Crude Oil ETF price movements. We take the
Natural Gas/Crude Oil ETF pair with the highest historical price correlation and
then create insights for Crude Oil depending on whether or not the Natural Gas ETF price change
is above/below a certain threshold that we set (arbitrarily).
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.
'''
from AlgorithmImports import *
class GasAndCrudeOilEnergyCorrelationAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1) #Set Start Date
self.set_cash(100000) #Set Strategy Cash
natural_gas = [Symbol.create(x, SecurityType.EQUITY, Market.USA) for x in ['UNG','BOIL','FCG']]
crude_oil = [Symbol.create(x, SecurityType.EQUITY, Market.USA) for x in ['USO','UCO','DBO']]
## Set Universe Selection
self.universe_settings.resolution = Resolution.MINUTE
self.set_universe_selection( ManualUniverseSelectionModel(natural_gas + crude_oil) )
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
## Custom Alpha Model
self.set_alpha(PairsAlphaModel(leading = natural_gas, following = crude_oil, history_days = 90, resolution = Resolution.MINUTE))
## Equal-weight our positions, in this case 100% in USO
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(resolution = Resolution.MINUTE))
## Immediate Execution Fill Model
self.set_execution(CustomExecutionModel())
## Null Risk-Management Model
self.set_risk_management(NullRiskManagementModel())
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug(f'Purchased Stock: {order_event.symbol}')
def on_end_of_algorithm(self):
for kvp in self.portfolio:
if kvp.value.invested:
self.log(f'Invested in: {kvp.key}')
class PairsAlphaModel(AlphaModel):
'''This Alpha model assumes that the ETF for natural gas is a good leading-indicator
of the price of the crude oil ETF. The model will take in arguments for a threshold
at which the model triggers an insight, the length of the look-back period for evaluating
rate-of-change of UNG prices, and the duration of the insight'''
def __init__(self, *args, **kwargs):
self.leading = kwargs.get('leading', [])
self.following = kwargs.get('following', [])
self.history_days = kwargs.get('history_days', 90) ## In days
self.lookback = kwargs.get('lookback', 5)
self.resolution = kwargs.get('resolution', Resolution.HOUR)
self.prediction_interval = Time.multiply(Extensions.to_time_span(self.resolution), 5) ## Arbitrary
self.difference_trigger = kwargs.get('difference_trigger', 0.75)
self._symbol_data_by_symbol = {}
self.next_update = None
def update(self, algorithm, data):
if (self.next_update is None) or (algorithm.time > self.next_update):
self.correlation_pairs_selection()
self.next_update = algorithm.time + timedelta(30)
magnitude = round(self.pairs[0].rate_of_return / 100, 6)
## Check if Natural Gas returns are greater than the threshold we've set
if self.pairs[0].rate_of_return > self.difference_trigger:
return [Insight.price(self.pairs[1].symbol, self.prediction_interval, InsightDirection.UP, magnitude)]
if self.pairs[0].rate_of_return < -self.difference_trigger:
return [Insight.price(self.pairs[1].symbol, self.prediction_interval, InsightDirection.DOWN, magnitude)]
return []
def correlation_pairs_selection(self):
## Get returns for each natural gas/oil ETF
daily_return = {}
for symbol, symbol_data in self._symbol_data_by_symbol.items():
daily_return[symbol] = symbol_data.daily_return_array
## Estimate coefficients of different correlation measures
tau = pd.DataFrame.from_dict(daily_return).corr(method='kendall')
## Calculate the pair with highest historical correlation
max_corr = -1
for x in self.leading:
df = tau[[x]].loc[self.following]
corr = float(df.max())
if corr > max_corr:
self.pairs = (
self._symbol_data_by_symbol[x],
self._symbol_data_by_symbol[df.idxmax()[0]])
max_corr = corr
def on_securities_changed(self, algorithm, changes):
'''Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
for removed in changes.removed_securities:
symbol_data = self._symbol_data_by_symbol.pop(removed.symbol, None)
if symbol_data is not None:
symbol_data.remove_consolidators(algorithm)
# initialize data for added securities
symbols = [ x.symbol for x in changes.added_securities ]
history = algorithm.history(symbols, self.history_days + 1, Resolution.DAILY)
if history.empty: return
tickers = history.index.levels[0]
for ticker in tickers:
symbol = SymbolCache.get_symbol(ticker)
if symbol not in self._symbol_data_by_symbol:
symbol_data = SymbolData(symbol, self.history_days, self.lookback, self.resolution, algorithm)
self._symbol_data_by_symbol[symbol] = symbol_data
symbol_data.update_daily_rate_of_change(history.loc[ticker])
history = algorithm.history(symbols, self.lookback, self.resolution)
if history.empty: return
for ticker in tickers:
symbol = SymbolCache.get_symbol(ticker)
if symbol in self._symbol_data_by_symbol:
self._symbol_data_by_symbol[symbol].update_rate_of_change(history.loc[ticker])
class SymbolData:
'''Contains data specific to a symbol required by this model'''
def __init__(self, symbol, daily_lookback, lookback, resolution, algorithm):
self.symbol = symbol
self.daily_return = RateOfChangePercent(f'{symbol}.daily_rocp({1})', 1)
self.daily_consolidator = algorithm.resolve_consolidator(symbol, Resolution.DAILY)
self.daily_return_history = RollingWindow(daily_lookback)
def updatedaily_return_history(s, e):
self.daily_return_history.add(e)
self.daily_return.updated += updatedaily_return_history
algorithm.register_indicator(symbol, self.daily_return, self.daily_consolidator)
self.rocp = RateOfChangePercent(f'{symbol}.rocp({lookback})', lookback)
self.consolidator = algorithm.resolve_consolidator(symbol, resolution)
algorithm.register_indicator(symbol, self.rocp, self.consolidator)
def remove_consolidators(self, algorithm):
algorithm.subscription_manager.remove_consolidator(self.symbol, self.consolidator)
algorithm.subscription_manager.remove_consolidator(self.symbol, self.daily_consolidator)
def update_rate_of_change(self, history):
for tuple in history.itertuples():
self.rocp.update(tuple.Index, tuple.close)
def update_daily_rate_of_change(self, history):
for tuple in history.itertuples():
self.daily_return.update(tuple.Index, tuple.close)
@property
def rate_of_return(self):
return float(self.rocp.current.value)
@property
def daily_return_array(self):
return pd.Series({x.end_time: x.value for x in self.daily_return_history})
def __repr__(self):
return f"{self.rocp.name} - {self.daily_return}"
class CustomExecutionModel(ExecutionModel):
'''Provides an implementation of IExecutionModel that immediately submits market orders to achieve the desired portfolio targets'''
def __init__(self):
'''Initializes a new instance of the ImmediateExecutionModel class'''
self.targets_collection = PortfolioTargetCollection()
self.previous_symbol = None
def execute(self, algorithm, targets):
'''Immediately submits orders for the specified portfolio targets.
Args:
algorithm: The algorithm instance
targets: The portfolio targets to be ordered'''
self.targets_collection.add_range(targets)
for target in self.targets_collection.order_by_margin_impact(algorithm):
open_quantity = sum([x.quantity for x in algorithm.transactions.get_open_orders(target.symbol)])
existing = algorithm.securities[target.symbol].holdings.quantity + open_quantity
quantity = target.quantity - existing
## Liquidate positions in Crude Oil ETF that is no longer part of the highest-correlation pair
if (str(target.symbol) != str(self.previous_symbol)) and (self.previous_symbol is not None):
algorithm.liquidate(self.previous_symbol)
if quantity != 0:
algorithm.market_order(target.symbol, quantity)
self.previous_symbol = target.symbol
self.targets_collection.clear_fulfilled(algorithm)
@@ -0,0 +1,111 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# Equity indices exhibit mean reversion in daily returns. The Internal Bar Strength indicator (IBS),
# which relates the closing price of a security to its daily range can be used to identify overbought
# and oversold securities.
#
# This alpha ranks 33 global equity ETFs on its IBS value the previous day and predicts for the following day
# that the ETF with the highest IBS value will decrease in price, and the ETF with the lowest IBS value
# will increase in price.
#
# Source: Kakushadze, Zura, and Juan Andrés Serur. “4. Exchange-Traded Funds (ETFs).” 151 Trading Strategies, Palgrave Macmillan, 2018, pp. 9091.
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha.
#
class GlobalEquityMeanReversionIBSAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_cash(100000)
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# Global Equity ETF tickers
tickers = ["ECH","EEM","EFA","EPHE","EPP","EWA","EWC","EWG",
"EWH","EWI","EWJ","EWL","EWM","EWM","EWO","EWP",
"EWQ","EWS","EWT","EWU","EWY","EWZ","EZA","FXI",
"GXG","IDX","ILF","EWM","QQQ","RSX","SPY","THD"]
symbols = [Symbol.create(ticker, SecurityType.EQUITY, Market.USA) for ticker in tickers]
# Manually curated universe
self.universe_settings.resolution = Resolution.DAILY
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
# Use GlobalEquityMeanReversionAlphaModel to establish insights
self.set_alpha(MeanReversionIBSAlphaModel())
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
# Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class MeanReversionIBSAlphaModel(AlphaModel):
'''Uses ranking of Internal Bar Strength (IBS) to create direction prediction for insights'''
def __init__(self, *args, **kwargs):
lookback = kwargs['lookback'] if 'lookback' in kwargs else 1
resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.DAILY
self.prediction_interval = Time.multiply(Extensions.to_time_span(resolution), lookback)
self.number_of_stocks = kwargs['number_of_stocks'] if 'number_of_stocks' in kwargs else 2
def update(self, algorithm, data):
insights = []
symbols_ibs = dict()
returns = dict()
for security in algorithm.active_securities.values:
if security.has_data:
high = security.high
low = security.low
hilo = high - low
# Do not consider symbol with zero open and avoid division by zero
if security.open * hilo != 0:
# Internal bar strength (IBS)
symbols_ibs[security.symbol] = (security.close - low)/hilo
returns[security.symbol] = security.close/security.open-1
# Number of stocks cannot be higher than half of symbols_ibs length
number_of_stocks = min(int(len(symbols_ibs)/2), self.number_of_stocks)
if number_of_stocks == 0:
return []
# Rank securities with the highest IBS value
ordered = sorted(symbols_ibs.items(), key=lambda kv: (round(kv[1], 6), kv[0]), reverse=True)
high_ibs = dict(ordered[0:number_of_stocks]) # Get highest IBS
low_ibs = dict(ordered[-number_of_stocks:]) # Get lowest IBS
# Emit "down" insight for the securities with the highest IBS value
for key,value in high_ibs.items():
insights.append(Insight.price(key, self.prediction_interval, InsightDirection.DOWN, abs(returns[key]), None))
# Emit "up" insight for the securities with the lowest IBS value
for key,value in low_ibs.items():
insights.append(Insight.price(key, self.prediction_interval, InsightDirection.UP, abs(returns[key]), None))
return insights
@@ -0,0 +1,211 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
from math import ceil
from itertools import chain
class GreenblattMagicFormulaAlpha(QCAlgorithm):
''' Alpha Streams: Benchmark Alpha: Pick stocks according to Joel Greenblatt's Magic Formula
This alpha picks stocks according to Joel Greenblatt's Magic Formula.
First, each stock is ranked depending on the relative value of the ratio EV/EBITDA. For example, a stock
that has the lowest EV/EBITDA ratio in the security universe receives a score of one while a stock that has
the tenth lowest EV/EBITDA score would be assigned 10 points.
Then, each stock is ranked and given a score for the second valuation ratio, Return on Capital (ROC).
Similarly, a stock that has the highest ROC value in the universe gets one score point.
The stocks that receive the lowest combined score are chosen for insights.
Source: Greenblatt, J. (2010) The Little Book That Beats the Market
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.'''
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_cash(100000)
#Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# select stocks using MagicFormulaUniverseSelectionModel
self.set_universe_selection(GreenBlattMagicFormulaUniverseSelectionModel())
# Use MagicFormulaAlphaModel to establish insights
self.set_alpha(RateOfChangeAlphaModel())
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class RateOfChangeAlphaModel(AlphaModel):
'''Uses Rate of Change (ROC) to create magnitude prediction for insights.'''
def __init__(self, *args, **kwargs):
self.lookback = kwargs.get('lookback', 1)
self.resolution = kwargs.get('resolution', Resolution.DAILY)
self.prediction_interval = Time.multiply(Extensions.to_time_span(self.resolution), self.lookback)
self._symbol_data_by_symbol = {}
def update(self, algorithm, data):
insights = []
for symbol, symbol_data in self._symbol_data_by_symbol.items():
if symbol_data.can_emit:
insights.append(Insight.price(symbol, self.prediction_interval, InsightDirection.UP, symbol_data.returns, None))
return insights
def on_securities_changed(self, algorithm, changes):
# clean up data for removed securities
for removed in changes.removed_securities:
symbol_data = self._symbol_data_by_symbol.pop(removed.symbol, None)
if symbol_data is not None:
symbol_data.remove_consolidators(algorithm)
# initialize data for added securities
symbols = [ x.symbol for x in changes.added_securities
if x.symbol not in self._symbol_data_by_symbol]
history = algorithm.history(symbols, self.lookback, self.resolution)
if history.empty: return
for symbol in symbols:
symbol_data = SymbolData(algorithm, symbol, self.lookback, self.resolution)
self._symbol_data_by_symbol[symbol] = symbol_data
symbol_data.warm_up_indicators(history.loc[symbol])
class SymbolData:
'''Contains data specific to a symbol required by this model'''
def __init__(self, algorithm, symbol, lookback, resolution):
self.previous = 0
self._symbol = symbol
self.roc = RateOfChange(f'{symbol}.roc({lookback})', lookback)
self.consolidator = algorithm.resolve_consolidator(symbol, resolution)
algorithm.register_indicator(symbol, self.roc, self.consolidator)
def remove_consolidators(self, algorithm):
algorithm.subscription_manager.remove_consolidator(self._symbol, self.consolidator)
def warm_up_indicators(self, history):
for tuple in history.itertuples():
self.roc.update(tuple.Index, tuple.close)
@property
def returns(self):
return self.roc.current.value
@property
def can_emit(self):
if self.previous == self.roc.samples:
return False
self.previous = self.roc.samples
return self.roc.is_ready
def __str__(self, **kwargs):
return f'{self.roc.name}: {(1 + self.returns)**252 - 1:.2%}'
class GreenBlattMagicFormulaUniverseSelectionModel(FundamentalUniverseSelectionModel):
'''Defines a universe according to Joel Greenblatt's Magic Formula, as a universe selection model for the framework algorithm.
From the universe QC500, stocks are ranked using the valuation ratios, Enterprise Value to EBITDA (EV/EBITDA) and Return on Assets (ROA).
'''
def __init__(self,
filter_fine_data = True,
universe_settings = None):
'''Initializes a new default instance of the MagicFormulaUniverseSelectionModel'''
super().__init__(filter_fine_data, universe_settings)
# Number of stocks in Coarse Universe
self.number_of_symbols_coarse = 500
# Number of sorted stocks in the fine selection subset using the valuation ratio, EV to EBITDA (EV/EBITDA)
self.number_of_symbols_fine = 20
# Final number of stocks in security list, after sorted by the valuation ratio, Return on Assets (ROA)
self.number_of_symbols_in_portfolio = 10
self.last_month = -1
self.dollar_volume_by_symbol = {}
def select_coarse(self, algorithm, coarse):
'''Performs coarse selection for constituents.
The stocks must have fundamental data'''
month = algorithm.time.month
if month == self.last_month:
return Universe.UNCHANGED
self.last_month = month
# sort the stocks by dollar volume and take the top 1000
top = sorted([x for x in coarse if x.has_fundamental_data],
key=lambda x: x.dollar_volume, reverse=True)[:self.number_of_symbols_coarse]
self.dollar_volume_by_symbol = { i.symbol: i.dollar_volume for i in top }
return list(self.dollar_volume_by_symbol.keys())
def select_fine(self, algorithm, fine):
'''QC500: Performs fine selection for the coarse selection constituents
The company's headquarter must in the U.S.
The stock must be traded on either the NYSE or NASDAQ
At least half a year since its initial public offering
The stock's market cap must be greater than 500 million
Magic Formula: Rank stocks by Enterprise Value to EBITDA (EV/EBITDA)
Rank subset of previously ranked stocks (EV/EBITDA), using the valuation ratio Return on Assets (ROA)'''
# QC500:
## The company's headquarter must in the U.S.
## The stock must be traded on either the NYSE or NASDAQ
## At least half a year since its initial public offering
## The stock's market cap must be greater than 500 million
filtered_fine = [x for x in fine if x.company_reference.country_id == "USA"
and (x.company_reference.primary_exchange_id == "NYS" or x.company_reference.primary_exchange_id == "NAS")
and (algorithm.time - x.security_reference.ipo_date).days > 180
and x.earning_reports.basic_average_shares.three_months * x.earning_reports.basic_eps.twelve_months * x.valuation_ratios.pe_ratio > 5e8]
count = len(filtered_fine)
if count == 0: return []
my_dict = dict()
percent = self.number_of_symbols_fine / count
# select stocks with top dollar volume in every single sector
for key in ["N", "M", "U", "T", "B", "I"]:
value = [x for x in filtered_fine if x.company_reference.industry_template_code == key]
value = sorted(value, key=lambda x: self.dollar_volume_by_symbol[x.symbol], reverse = True)
my_dict[key] = value[:ceil(len(value) * percent)]
# stocks in QC500 universe
top_fine = chain.from_iterable(my_dict.values())
# Magic Formula:
## Rank stocks by Enterprise Value to EBITDA (EV/EBITDA)
## Rank subset of previously ranked stocks (EV/EBITDA), using the valuation ratio Return on Assets (ROA)
# sort stocks in the security universe of QC500 based on Enterprise Value to EBITDA valuation ratio
sorted_by_ev_to_ebitda = sorted(top_fine, key=lambda x: x.valuation_ratios.ev_to_ebitda , reverse=True)
# sort subset of stocks that have been sorted by Enterprise Value to EBITDA, based on the valuation ratio Return on Assets (ROA)
sorted_by_roa = sorted(sorted_by_ev_to_ebitda[:self.number_of_symbols_fine], key=lambda x: x.valuation_ratios.forward_roa, reverse=False)
# retrieve list of securites in portfolio
return [f.symbol for f in sorted_by_roa[:self.number_of_symbols_in_portfolio]]
@@ -0,0 +1,122 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# Reversal strategy that goes long when price crosses below SMA and Short when price crosses above SMA.
# The trading strategy is implemented only between 10AM - 3PM (NY time). Research suggests this is due to
# institutional trades during market hours which need hedging with the USD. Source paper:
# LeBaron, Zhao: Intraday Foreign Exchange Reversals
# http://people.brandeis.edu/~blebaron/wps/fxnyc.pdf
# http://www.fma.org/Reno/Papers/ForeignExchangeReversalsinNewYorkTime.PDF
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha.
#
class IntradayReversalCurrencyMarketsAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2015, 1, 1)
self.set_cash(100000)
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# Select resolution
resolution = Resolution.HOUR
# Reversion on the USD.
symbols = [Symbol.create("EURUSD", SecurityType.FOREX, Market.OANDA)]
# Set requested data resolution
self.universe_settings.resolution = resolution
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.set_alpha(IntradayReversalAlphaModel(5, resolution))
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
# Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
#Set WarmUp for Indicators
self.set_warm_up(20)
class IntradayReversalAlphaModel(AlphaModel):
'''Alpha model that uses a Price/SMA Crossover to create insights on Hourly Frequency.
Frequency: Hourly data with 5-hour simple moving average.
Strategy:
Reversal strategy that goes Long when price crosses below SMA and Short when price crosses above SMA.
The trading strategy is implemented only between 10AM - 3PM (NY time)'''
# Initialize variables
def __init__(self, period_sma = 5, resolution = Resolution.HOUR):
self.period_sma = period_sma
self.resolution = resolution
self.cache = {} # Cache for SymbolData
self.name = 'IntradayReversalAlphaModel'
def update(self, algorithm, data):
# Set the time to close all positions at 3PM
time_to_close = algorithm.time.replace(hour=15, minute=1, second=0)
insights = []
for kvp in algorithm.active_securities:
symbol = kvp.key
if self.should_emit_insight(algorithm, symbol) and symbol in self.cache:
price = kvp.value.price
symbol_data = self.cache[symbol]
direction = InsightDirection.UP if symbol_data.is_uptrend(price) else InsightDirection.DOWN
# Ignore signal for same direction as previous signal (when no crossover)
if direction == symbol_data.previous_direction:
continue
# Save the current Insight Direction to check when the crossover happens
symbol_data.previous_direction = direction
# Generate insight
insights.append(Insight.price(symbol, time_to_close, direction))
return insights
def on_securities_changed(self, algorithm, changes):
'''Handle creation of the new security and its cache class.
Simplified in this example as there is 1 asset.'''
for security in changes.added_securities:
self.cache[security.symbol] = SymbolData(algorithm, security.symbol, self.period_sma, self.resolution)
def should_emit_insight(self, algorithm, symbol):
'''Time to control when to start and finish emitting (10AM to 3PM)'''
time_of_day = algorithm.time.time()
return algorithm.securities[symbol].has_data and time_of_day >= time(10) and time_of_day <= time(15)
class SymbolData:
def __init__(self, algorithm, symbol, period_sma, resolution):
self.previous_direction = InsightDirection.FLAT
self.price_sma = algorithm.sma(symbol, period_sma, resolution)
def is_uptrend(self, price):
return self.price_sma.is_ready and price < round(self.price_sma.current.value * 1.001, 6)
@@ -0,0 +1,123 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# Academic research suggests that stock market participants generally place their orders at the market open and close.
# Intraday trading volume is J-Shaped, where the minimum trading volume of the day is during lunch-break. Stocks become
# more volatile as order flow is reduced and tend to mean-revert during lunch-break.
#
# This alpha aims to capture the mean-reversion effect of ETFs during lunch-break by ranking 20 ETFs
# on their return between the close of the previous day to 12:00 the day after and predicting mean-reversion
# in price during lunch-break.
#
# Source: Lunina, V. (June 2011). The Intraday Dynamics of Stock Returns and Trading Activity: Evidence from OMXS 30 (Master's Essay, Lund University).
# Retrieved from http://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=1973850&fileOId=1973852
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha.
#
class MeanReversionLunchBreakAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_cash(100000)
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# Use Hourly Data For Simplicity
self.universe_settings.resolution = Resolution.HOUR
self.set_universe_selection(CoarseFundamentalUniverseSelectionModel(self.coarse_selection_function))
# Use MeanReversionLunchBreakAlphaModel to establish insights
self.set_alpha(MeanReversionLunchBreakAlphaModel())
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
# Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
# Sort the data by daily dollar volume and take the top '20' ETFs
def coarse_selection_function(self, coarse):
sorted_by_dollar_volume = sorted(coarse, key=lambda x: x.dollar_volume, reverse=True)
filtered = [ x.symbol for x in sorted_by_dollar_volume if not x.has_fundamental_data ]
return filtered[:20]
class MeanReversionLunchBreakAlphaModel(AlphaModel):
'''Uses the price return between the close of previous day to 12:00 the day after to
predict mean-reversion of stock price during lunch break and creates direction prediction
for insights accordingly.'''
def __init__(self, *args, **kwargs):
lookback = kwargs['lookback'] if 'lookback' in kwargs else 1
self.resolution = Resolution.HOUR
self.prediction_interval = Time.multiply(Extensions.to_time_span(self.resolution), lookback)
self._symbol_data_by_symbol = dict()
def update(self, algorithm, data):
for symbol, symbol_data in self._symbol_data_by_symbol.items():
if data.bars.contains_key(symbol):
bar = data.bars.get(symbol)
symbol_data.update(bar.end_time, bar.close)
return [] if algorithm.time.hour != 12 else \
[x.insight for x in self._symbol_data_by_symbol.values()]
def on_securities_changed(self, algorithm, changes):
for security in changes.removed_securities:
self._symbol_data_by_symbol.pop(security.symbol, None)
# Retrieve price history for all securities in the security universe
# and update the indicators in the SymbolData object
symbols = [x.symbol for x in changes.added_securities]
history = algorithm.history(symbols, 1, self.resolution)
if history.empty:
algorithm.debug(f"No data on {algorithm.time}")
return
history = history.close.unstack(level = 0)
for ticker, values in history.items():
symbol = next((x for x in symbols if str(x) == ticker ), None)
if symbol in self._symbol_data_by_symbol or symbol is None: continue
self._symbol_data_by_symbol[symbol] = self.SymbolData(symbol, self.prediction_interval)
self._symbol_data_by_symbol[symbol].update(values.index[0], values[0])
class SymbolData:
def __init__(self, symbol, period):
self._symbol = symbol
self.period = period
# Mean value of returns for magnitude prediction
self.mean_of_price_change = IndicatorExtensions.sma(RateOfChangePercent(1),3)
# Price change from close price the previous day
self.price_change = RateOfChangePercent(3)
def update(self, time, value):
return self.mean_of_price_change.update(time, value) and \
self.price_change.update(time, value)
@property
def insight(self):
direction = InsightDirection.DOWN if self.price_change.current.value > 0 else InsightDirection.UP
margnitude = abs(self.mean_of_price_change.current.value)
return Insight.price(self._symbol, self.period, direction, margnitude, None)
@@ -0,0 +1,109 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''
This Alpha Model uses Wells Fargo 30-year Fixed Rate Mortgage data from Quandl to
generate Insights about the movement of Real Estate ETFs. Mortgage rates can provide information
regarding the general price trend of real estate, and ETFs provide good continuous-time instruments
to measure the impact against. Volatility in mortgage rates tends to put downward pressure on real
estate prices, whereas stable mortgage rates, regardless of true rate, lead to stable or higher real
estate prices. This Alpha model seeks to take advantage of this correlation by emitting insights
based on volatility and rate deviation from its historic mean.
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.
'''
from AlgorithmImports import *
class MortgageRateVolatilityAlpha(QCAlgorithmFramework):
def initialize(self):
# Set requested data resolution
self.set_start_date(2017, 1, 1) #Set Start Date
self.set_cash(100000) #Set Strategy Cash
self.universe_settings.resolution = Resolution.DAILY
## Universe of six liquid real estate ETFs
etfs = ['VNQ', 'REET', 'TAO', 'FREL', 'SRET', 'HIPS']
symbols = [ Symbol.create(etf, SecurityType.EQUITY, Market.USA) for etf in etfs ]
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
self.set_universe_selection(ManualUniverseSelectionModel(symbols) )
self.set_alpha(MortgageRateVolatilityAlphaModel(self))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
class MortgageRateVolatilityAlphaModel(AlphaModel):
def __init__(self, algorithm, indicator_period = 15, insight_magnitude = 0.005, deviations = 2):
## Add Quandl data for a Well's Fargo 30-year Fixed Rate mortgage
self.mortgage_rate = algorithm.add_data(QuandlMortgagePriceColumns, 'WFC/PR_GOV_30YFIXEDVA_APR').symbol
self.indicator_period = indicator_period
self.insight_duration = TimeSpan.from_days(indicator_period)
self.insight_magnitude = insight_magnitude
self.deviations = deviations
## Add indicators for the mortgage rate -- Standard Deviation and Simple Moving Average
self.mortgage_rate_std = algorithm.std(self.mortgage_rate, indicator_period)
self.mortgage_rate_sma = algorithm.sma(self.mortgage_rate, indicator_period)
## Use a history call to warm-up the indicators
self.warmup_indicators(algorithm)
def update(self, algorithm, data):
insights = []
## Return empty list if data slice doesn't contain monrtgage rate data
if self.mortgage_rate not in data.keys():
return []
## Extract current mortgage rate, the current STD indicator value, and current SMA value
mortgage_rate = data[self.mortgage_rate].value
deviation = self.deviations * self.mortgage_rate_std.current.value
sma = self.mortgage_rate_sma.current.value
## If volatility in mortgage rates is high, then we emit an Insight to sell
if (mortgage_rate < sma - deviation) or (mortgage_rate > sma + deviation):
## Emit insights for all securities that are currently in the Universe,
## except for the Quandl Symbol
insights = [Insight(security, self.insight_duration, InsightType.PRICE, InsightDirection.DOWN, self.insight_magnitude, None) \
for security in algorithm.active_securities.keys if security != self.mortgage_rate]
## If volatility in mortgage rates is low, then we emit an Insight to buy
if (mortgage_rate < sma - deviation/2) or (mortgage_rate > sma + deviation/2):
insights = [Insight(security, self.insight_duration, InsightType.PRICE, InsightDirection.UP, self.insight_magnitude, None) \
for security in algorithm.active_securities.keys if security != self.mortgage_rate]
return insights
def warmup_indicators(self, algorithm):
## Make a history call and update the indicators
history = algorithm.history(self.mortgage_rate, self.indicator_period, Resolution.DAILY)
for index, row in history.iterrows():
self.mortgage_rate_std.update(index[1], row['value'])
self.mortgage_rate_sma.update(index[1], row['value'])
class QuandlMortgagePriceColumns(PythonQuandl):
def __init__(self):
## Rename the Quandl object column to the data we want, which is the 'Value' column
## of the CSV that our API call returns
self.value_column_name = "Value"
@@ -0,0 +1,139 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class PriceGapMeanReversionAlpha(QCAlgorithm):
'''The motivating idea for this Alpha Model is that a large price gap (here we use true outliers --
price gaps that whose absolutely values are greater than 3 * Volatility) is due to rebound
back to an appropriate price or at least retreat from its brief extreme. Using a Coarse Universe selection
function, the algorithm selects the top x-companies by Dollar Volume (x can be any number you choose)
to trade with, and then uses the Standard Deviation of the 100 most-recent closing prices to determine
which price movements are outliers that warrant emitting insights.
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.'''
def initialize(self):
self.set_start_date(2018, 1, 1) #Set Start Date
self.set_cash(100000) #Set Strategy Cash
## Initialize variables to be used in controlling frequency of universe selection
self.week = -1
## Manual Universe Selection
self.universe_settings.resolution = Resolution.MINUTE
self.set_universe_selection(CoarseFundamentalUniverseSelectionModel(self.coarse_selection_function))
## Set trading fees to $0
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
## Set custom Alpha Model
self.set_alpha(PriceGapMeanReversionAlphaModel())
## Set equal-weighting Portfolio Construction Model
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Risk Management Model
self.set_risk_management(NullRiskManagementModel())
def coarse_selection_function(self, coarse):
## If it isn't a new week, return the same symbols
current_week = self.time.isocalendar()[1]
if current_week == self.week:
return Universe.UNCHANGED
self.week = current_week
## If its a new week, then re-filter stocks by Dollar Volume
sorted_by_dollar_volume = sorted(coarse, key=lambda x: x.dollar_volume, reverse=True)
return [ x.symbol for x in sorted_by_dollar_volume[:25] ]
class PriceGapMeanReversionAlphaModel(AlphaModel):
def __init__(self, *args, **kwargs):
''' Initialize variables and dictionary for Symbol Data to support algorithm's function '''
self.lookback = 100
self.resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.MINUTE
self.prediction_interval = Time.multiply(Extensions.to_time_span(self.resolution), 5) ## Arbitrary
self._symbol_data_by_symbol = {}
def update(self, algorithm, data):
insights = []
## Loop through all Symbol Data objects
for symbol, symbol_data in self._symbol_data_by_symbol.items():
## Evaluate whether or not the price jump is expected to rebound
if not symbol_data.is_trend(data):
continue
## Emit insights accordingly to the price jump sign
direction = InsightDirection.DOWN if symbol_data.price_jump > 0 else InsightDirection.UP
insights.append(Insight.price(symbol, self.prediction_interval, direction, symbol_data.price_jump, None))
return insights
def on_securities_changed(self, algorithm, changes):
# Clean up data for removed securities
for removed in changes.removed_securities:
symbol_data = self._symbol_data_by_symbol.pop(removed.symbol, None)
if symbol_data is not None:
symbol_data.remove_consolidators(algorithm)
symbols = [x.symbol for x in changes.added_securities
if x.symbol not in self._symbol_data_by_symbol]
history = algorithm.history(symbols, self.lookback, self.resolution)
if history.empty: return
## Create and initialize SymbolData objects
for symbol in symbols:
symbol_data = SymbolData(algorithm, symbol, self.lookback, self.resolution)
symbol_data.warm_up_indicators(history.loc[symbol])
self._symbol_data_by_symbol[symbol] = symbol_data
class SymbolData:
def __init__(self, algorithm, symbol, lookback, resolution):
self._symbol = symbol
self.close = 0
self.last_price = 0
self.price_jump = 0
self.consolidator = algorithm.resolve_consolidator(symbol, resolution)
self.volatility = StandardDeviation(f'{symbol}.std({lookback})', lookback)
algorithm.register_indicator(symbol, self.volatility, self.consolidator)
def remove_consolidators(self, algorithm):
algorithm.subscription_manager.remove_consolidator(self._symbol, self.consolidator)
def warm_up_indicators(self, history):
self.close = history.iloc[-1].close
for tuple in history.itertuples():
self.volatility.update(tuple.Index, tuple.close)
def is_trend(self, data):
## Check for any data events that would return a NoneBar in the Alpha Model Update() method
if not data.bars.contains_key(self._symbol):
return False
self.last_price = self.close
self.close = data.bars[self._symbol].close
self.price_jump = (self.close / self.last_price) - 1
return abs(100*self.price_jump) > 3*self.volatility.current.value
@@ -0,0 +1,113 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Alpha Benchmark Strategy capitalizing on ETF rebalancing causing momentum during trending markets.
### </summary>
### <meta name="tag" content="alphastream" />
### <meta name="tag" content="etf" />
### <meta name="tag" content="algorithm framework" />
class RebalancingLeveragedETFAlpha(QCAlgorithm):
''' Alpha Streams: Benchmark Alpha: Leveraged ETF Rebalancing
Strategy by Prof. Shum, reposted by Ernie Chan.
Source: http://epchan.blogspot.com/2012/10/a-leveraged-etfs-strategy.html'''
def initialize(self):
self.set_start_date(2017, 6, 1)
self.set_end_date(2018, 8, 1)
self.set_cash(100000)
underlying = ["SPY","QLD","DIA","IJR","MDY","IWM","QQQ","IYE","EEM","IYW","EFA","GAZB","SLV","IEF","IYM","IYF","IYH","IYR","IYC","IBB","FEZ","USO","TLT"]
ultra_long = ["SSO","UGL","DDM","SAA","MZZ","UWM","QLD","DIG","EET","ROM","EFO","BOIL","AGQ","UST","UYM","UYG","RXL","URE","UCC","BIB","ULE","UCO","UBT"]
ultra_short = ["SDS","GLL","DXD","SDD","MVV","TWM","QID","DUG","EEV","REW","EFU","KOLD","ZSL","PST","SMN","SKF","RXD","SRS","SCC","BIS","EPV","SCO","TBT"]
groups = []
for i in range(len(underlying)):
group = ETFGroup(self.add_equity(underlying[i], Resolution.MINUTE).symbol,
self.add_equity(ultra_long[i], Resolution.MINUTE).symbol,
self.add_equity(ultra_short[i], Resolution.MINUTE).symbol)
groups.append(group)
# Manually curated universe
self.set_universe_selection(ManualUniverseSelectionModel())
# Select the demonstration alpha model
self.set_alpha(RebalancingLeveragedETFAlphaModel(groups))
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
# Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class RebalancingLeveragedETFAlphaModel(AlphaModel):
'''
If the underlying ETF has experienced a return >= 1% since the previous day's close up to the current time at 14:15,
then buy it's ultra ETF right away, and exit at the close. If the return is <= -1%, sell it's ultra-short ETF.
'''
def __init__(self, ETFgroups):
self.etfgroups = ETFgroups
self.date = datetime.min.date
self.name = "RebalancingLeveragedETFAlphaModel"
def update(self, algorithm, data):
'''Scan to see if the returns are greater than 1% at 2.15pm to emit an insight.'''
insights = []
magnitude = 0.0005
# Paper suggests leveraged ETF's rebalance from 2.15pm - to close
# giving an insight period of 105 minutes.
period = timedelta(minutes=105)
# Get yesterday's close price at the market open
if algorithm.time.date() != self.date:
self.date = algorithm.time.date()
# Save yesterday's price and reset the signal
for group in self.etfgroups:
history = algorithm.history([group.underlying], 1, Resolution.DAILY)
group.yesterday_close = None if history.empty else history.loc[str(group.underlying)]['close'][0]
# Check if the returns are > 1% at 14.15
if algorithm.time.hour == 14 and algorithm.time.minute == 15:
for group in self.etfgroups:
if group.yesterday_close == 0 or group.yesterday_close is None: continue
returns = round((algorithm.portfolio[group.underlying].price - group.yesterday_close) / group.yesterday_close, 10)
if returns > 0.01:
insights.append(Insight.price(group.ultra_long, period, InsightDirection.UP, magnitude))
elif returns < -0.01:
insights.append(Insight.price(group.ultra_short, period, InsightDirection.DOWN, magnitude))
return insights
class ETFGroup:
'''
Group the underlying ETF and it's ultra ETFs
Args:
underlying: The underlying index ETF
ultra_long: The long-leveraged version of underlying ETF
ultra_short: The short-leveraged version of the underlying ETF
'''
def __init__(self,underlying, ultra_long, ultra_short):
self.underlying = underlying
self.ultra_long = ultra_long
self.ultra_short = ultra_short
self.yesterday_close = 0
@@ -0,0 +1,152 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# A number of companies publicly trade two different classes of shares
# in US equity markets. If both assets trade with reasonable volume, then
# the underlying driving forces of each should be similar or the same. Given
# this, we can create a relatively dollar-neutral long/short portfolio using
# the dual share classes. Theoretically, any deviation of this portfolio from
# its mean-value should be corrected, and so the motivating idea is based on
# mean-reversion. Using a Simple Moving Average indicator, we can
# compare the value of this portfolio against its SMA and generate insights
# to buy the under-valued symbol and sell the over-valued symbol.
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
# sourced so the community and client funds can see an example of an alpha.
#
class ShareClassMeanReversionAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 1, 1) #Set Start Date
self.set_cash(100000) #Set Strategy Cash
self.set_warm_up(20)
## Setup Universe settings and tickers to be used
tickers = ['VIA','VIAB']
self.universe_settings.resolution = Resolution.MINUTE
symbols = [ Symbol.create(ticker, SecurityType.EQUITY, Market.USA) for ticker in tickers]
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0))) ## Set $0 fees to mimic High-Frequency Trading
## Set Manual Universe Selection
self.set_universe_selection( ManualUniverseSelectionModel(symbols) )
## Set Custom Alpha Model
self.set_alpha(ShareClassMeanReversionAlphaModel(tickers = tickers))
## Set Equal Weighting Portfolio Construction Model
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class ShareClassMeanReversionAlphaModel(AlphaModel):
''' Initialize helper variables for the algorithm'''
def __init__(self, *args, **kwargs):
self.sma = SimpleMovingAverage(10)
self.position_window = RollingWindow(2)
self.alpha = None
self.beta = None
if 'tickers' not in kwargs:
raise AssertionError('ShareClassMeanReversionAlphaModel: Missing argument: "tickers"')
self.tickers = kwargs['tickers']
self.position_value = None
self.invested = False
self.liquidate = 'liquidate'
self.long_symbol = self.tickers[0]
self.short_symbol = self.tickers[1]
self.resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.MINUTE
self.prediction_interval = Time.multiply(Extensions.to_time_span(self.resolution), 5) ## Arbitrary
self.insight_magnitude = 0.001
def update(self, algorithm, data):
insights = []
## Check to see if either ticker will return a NoneBar, and skip the data slice if so
for security in algorithm.securities:
if self.data_event_occured(data, security.key):
return insights
## If Alpha and Beta haven't been calculated yet, then do so
if (self.alpha is None) or (self.beta is None):
self.calculate_alpha_beta(algorithm, data)
algorithm.log('Alpha: ' + str(self.alpha))
algorithm.log('Beta: ' + str(self.beta))
## If the SMA isn't fully warmed up, then perform an update
if not self.sma.is_ready:
self.update_indicators(data)
return insights
## Update indicator and Rolling Window for each data slice passed into Update() method
self.update_indicators(data)
## Check to see if the portfolio is invested. If no, then perform value comparisons and emit insights accordingly
if not self.invested:
if self.position_value >= self.sma.current.value:
insights.append(Insight(self.long_symbol, self.prediction_interval, InsightType.PRICE, InsightDirection.DOWN, self.insight_magnitude, None))
insights.append(Insight(self.short_symbol, self.prediction_interval, InsightType.PRICE, InsightDirection.UP, self.insight_magnitude, None))
## Reset invested boolean
self.invested = True
elif self.position_value < self.sma.current.value:
insights.append(Insight(self.long_symbol, self.prediction_interval, InsightType.PRICE, InsightDirection.UP, self.insight_magnitude, None))
insights.append(Insight(self.short_symbol, self.prediction_interval, InsightType.PRICE, InsightDirection.DOWN, self.insight_magnitude, None))
## Reset invested boolean
self.invested = True
## If the portfolio is invested and crossed back over the SMA, then emit flat insights
elif self.invested and self.crossed_mean():
## Reset invested boolean
self.invested = False
return Insight.group(insights)
def data_event_occured(self, data, symbol):
## Helper function to check to see if data slice will contain a symbol
if data.splits.contains_key(symbol) or \
data.dividends.contains_key(symbol) or \
data.delistings.contains_key(symbol) or \
data.symbol_changed_events.contains_key(symbol):
return True
def update_indicators(self, data):
## Calculate position value and update the SMA indicator and Rolling Window
self.position_value = (self.alpha * data[self.long_symbol].close) - (self.beta * data[self.short_symbol].close)
self.sma.update(data[self.long_symbol].end_time, self.position_value)
self.position_window.add(self.position_value)
def crossed_mean(self):
## Check to see if the position value has crossed the SMA and then return a boolean value
if (self.position_window[0] >= self.sma.current.value) and (self.position_window[1] < self.sma.current.value):
return True
elif (self.position_window[0] < self.sma.current.value) and (self.position_window[1] >= self.sma.current.value):
return True
else:
return False
def calculate_alpha_beta(self, algorithm, data):
## Calculate Alpha and Beta, the initial number of shares for each security needed to achieve a 50/50 weighting
self.alpha = algorithm.calculate_order_quantity(self.long_symbol, 0.5)
self.beta = algorithm.calculate_order_quantity(self.short_symbol, 0.5)
@@ -0,0 +1,99 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
class SykesShortMicroCapAlpha(QCAlgorithm):
''' Alpha Streams: Benchmark Alpha: Identify "pumped" penny stocks and predict that the price of a "pumped" penny stock reverts to mean
This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open
sourced so the community and client funds can see an example of an alpha.'''
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_cash(100000)
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# select stocks using PennyStockUniverseSelectionModel
self.universe_settings.resolution = Resolution.DAILY
self.universe_settings.schedule.on(self.date_rules.month_start())
self.set_universe_selection(PennyStockUniverseSelectionModel())
# Use SykesShortMicroCapAlphaModel to establish insights
self.set_alpha(SykesShortMicroCapAlphaModel())
# Equally weigh securities in portfolio, based on insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
# Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class SykesShortMicroCapAlphaModel(AlphaModel):
'''Uses ranking of intraday percentage difference between open price and close price to create magnitude and direction prediction for insights'''
def __init__(self, *args, **kwargs):
lookback = kwargs['lookback'] if 'lookback' in kwargs else 1
resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.DAILY
self.prediction_interval = Time.multiply(Extensions.to_time_span(resolution), lookback)
self.number_of_stocks = kwargs['number_of_stocks'] if 'number_of_stocks' in kwargs else 10
def update(self, algorithm, data):
insights = []
symbols_ret = dict()
for security in algorithm.active_securities.values:
if security.has_data:
open_ = security.open
if open_ != 0:
# Intraday price change for penny stocks
symbols_ret[security.symbol] = security.close / open_ - 1
# Rank penny stocks on one day price change and retrieve list of ten "pumped" penny stocks
pumped_stocks = dict(sorted(symbols_ret.items(),
key = lambda kv: (-round(kv[1], 6), kv[0]))[:self.number_of_stocks])
# Emit "down" insight for "pumped" penny stocks
for symbol, value in pumped_stocks.items():
insights.append(Insight.price(symbol, self.prediction_interval, InsightDirection.DOWN, abs(value), None))
return insights
class PennyStockUniverseSelectionModel(FundamentalUniverseSelectionModel):
'''Defines a universe of penny stocks, as a universe selection model for the framework algorithm:
The stocks must have fundamental data
The stock must have positive previous-day close price
The stock must have volume between $1000000 and $10000 on the previous trading day
The stock must cost less than $5'''
def __init__(self):
super().__init__()
# Number of stocks in Coarse Universe
self.number_of_symbols_coarse = 500
def select(self, algorithm, fundamental):
# sort the stocks by dollar volume and take the top 500
top = sorted([x for x in fundamental if x.has_fundamental_data
and 5 > x.price > 0
and 1000000 > x.volume > 10000],
key=lambda x: x.dollar_volume, reverse=True)[:self.number_of_symbols_coarse]
return [x.symbol for x in top]
@@ -0,0 +1,92 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# In a perfect market, you could buy 100 EUR worth of USD, sell 100 EUR worth of GBP,
# and then use the GBP to buy USD and wind up with the same amount in USD as you received when
# you bought them with EUR. This relationship is expressed by the Triangle Exchange Rate, which is
#
# Triangle Exchange Rate = (A/B) * (B/C) * (C/A)
#
# where (A/B) is the exchange rate of A-to-B. In a perfect market, TER = 1, and so when
# there is a mispricing in the market, then TER will not be 1 and there exists an arbitrage opportunity.
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha.
#
class TriangleExchangeRateArbitrageAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 2, 1) #Set Start Date
self.set_cash(100000) #Set Strategy Cash
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
## Select trio of currencies to trade where
## Currency A = USD
## Currency B = EUR
## Currency C = GBP
currencies = ['EURUSD','EURGBP','GBPUSD']
symbols = [ Symbol.create(currency, SecurityType.FOREX, Market.OANDA) for currency in currencies]
## Manual universe selection with tick-resolution data
self.universe_settings.resolution = Resolution.MINUTE
self.set_universe_selection( ManualUniverseSelectionModel(symbols) )
self.set_alpha(ForexTriangleArbitrageAlphaModel(Resolution.MINUTE, symbols))
## Set Equal Weighting Portfolio Construction Model
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class ForexTriangleArbitrageAlphaModel(AlphaModel):
def __init__(self, insight_resolution, symbols):
self.insight_period = Time.multiply(Extensions.to_time_span(insight_resolution), 5)
self._symbols = symbols
def update(self, algorithm, data):
## Check to make sure all currency symbols are present
if len(data.keys()) < 3:
return []
## Extract QuoteBars for all three Forex securities
bar_a = data[self._symbols[0]]
bar_b = data[self._symbols[1]]
bar_c = data[self._symbols[2]]
## Calculate the triangle exchange rate
## Bid(Currency A -> Currency B) * Bid(Currency B -> Currency C) * Bid(Currency C -> Currency A)
## If exchange rates are priced perfectly, then this yield 1. If it is different than 1, then an arbitrage opportunity exists
triangle_rate = bar_a.ask.close / bar_b.bid.close / bar_c.ask.close
## If the triangle rate is significantly different than 1, then emit insights
if triangle_rate > 1.0005:
return Insight.group(
[
Insight.price(self._symbols[0], self.insight_period, InsightDirection.UP, 0.0001, None),
Insight.price(self._symbols[1], self.insight_period, InsightDirection.DOWN, 0.0001, None),
Insight.price(self._symbols[2], self.insight_period, InsightDirection.UP, 0.0001, None)
] )
return []
@@ -0,0 +1,81 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# Leveraged ETFs (LETF) promise a fixed leverage ratio with respect to an underlying asset or an index.
# A Triple-Leveraged ETF allows speculators to amplify their exposure to the daily returns of an underlying index by a factor of 3.
#
# Increased volatility generally decreases the value of a LETF over an extended period of time as daily compounding is amplified.
#
# This alpha emits short-biased insight to capitalize on volatility decay for each listed pair of TL-ETFs, by rebalancing the
# ETFs with equal weights each day.
#
# This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha.
#
class TripleLeverageETFPairVolatilityDecayAlpha(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_cash(100000)
# Set zero transaction fees
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
# 3X ETF pair tickers
ultra_long = Symbol.create("UGLD", SecurityType.EQUITY, Market.USA)
ultra_short = Symbol.create("DGLD", SecurityType.EQUITY, Market.USA)
# Manually curated universe
self.universe_settings.resolution = Resolution.DAILY
self.set_universe_selection(ManualUniverseSelectionModel([ultra_long, ultra_short]))
# Select the demonstration alpha model
self.set_alpha(RebalancingTripleLeveragedETFAlphaModel(ultra_long, ultra_short))
## Set Equal Weighting Portfolio Construction Model
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Set Immediate Execution Model
self.set_execution(ImmediateExecutionModel())
## Set Null Risk Management Model
self.set_risk_management(NullRiskManagementModel())
class RebalancingTripleLeveragedETFAlphaModel(AlphaModel):
'''
Rebalance a pair of 3x leveraged ETFs and predict that the value of both ETFs in each pair will decrease.
'''
def __init__(self, ultra_long, ultra_short):
# Giving an insight period 1 days.
self.period = timedelta(1)
self.magnitude = 0.001
self.ultra_long = ultra_long
self.ultra_short = ultra_short
self.name = "RebalancingTripleLeveragedETFAlphaModel"
def update(self, algorithm, data):
return Insight.group(
[
Insight.price(self.ultra_long, self.period, InsightDirection.DOWN, self.magnitude),
Insight.price(self.ultra_short, self.period, InsightDirection.DOWN, self.magnitude)
] )
@@ -0,0 +1,177 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
#
# This is a demonstration algorithm. It trades UVXY.
# Dual Thrust alpha model is used to produce insights.
# Those input parameters have been chosen that gave acceptable results on a series
# of random backtests run for the period from Oct, 2016 till Feb, 2019.
#
class VIXDualThrustAlpha(QCAlgorithm):
def initialize(self):
# -- STRATEGY INPUT PARAMETERS --
self.k1 = 0.63
self.k2 = 0.63
self.range_period = 20
self.consolidator_bars = 30
# Settings
self.set_start_date(2018, 10, 1)
self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0)))
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
# Universe Selection
self.universe_settings.resolution = Resolution.MINUTE # it's minute by default, but lets leave this param here
symbols = [Symbol.create("SPY", SecurityType.EQUITY, Market.USA)]
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
# Warming up
resolution_in_time_span = Extensions.to_time_span(self.universe_settings.resolution)
warm_up_time_span = Time.multiply(resolution_in_time_span, self.consolidator_bars)
self.set_warm_up(warm_up_time_span)
# Alpha Model
self.set_alpha(DualThrustAlphaModel(self.k1, self.k2, self.range_period, self.universe_settings.resolution, self.consolidator_bars))
## Portfolio Construction
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
## Execution
self.set_execution(ImmediateExecutionModel())
## Risk Management
self.set_risk_management(MaximumDrawdownPercentPerSecurity(0.03))
class DualThrustAlphaModel(AlphaModel):
'''Alpha model that uses dual-thrust strategy to create insights
https://medium.com/@FMZ_Quant/dual-thrust-trading-strategy-2cc74101a626
or here:
https://www.quantconnect.com/tutorials/strategy-library/dual-thrust-trading-algorithm'''
def __init__(self,
k1,
k2,
range_period,
resolution = Resolution.DAILY,
bars_to_consolidate = 1):
'''Initializes a new instance of the class
Args:
k1: Coefficient for upper band
k2: Coefficient for lower band
range_period: Amount of last bars to calculate the range
resolution: The resolution of data sent into the EMA indicators
bars_to_consolidate: If we want alpha to work on trade bars whose length is different
from the standard resolution - 1m 1h etc. - we need to pass this parameters along
with proper data resolution'''
# coefficient that used to determine upper and lower borders of a breakout channel
self.k1 = k1
self.k2 = k2
# period the range is calculated over
self.range_period = range_period
# initialize with empty dict.
self._symbol_data_by_symbol = dict()
# time for bars we make the calculations on
resolution_in_time_span = Extensions.to_time_span(resolution)
self.consolidator_time_span = Time.multiply(resolution_in_time_span, bars_to_consolidate)
# in 5 days after emission an insight is to be considered expired
self.period = timedelta(5)
def update(self, algorithm, data):
insights = []
for symbol, symbol_data in self._symbol_data_by_symbol.items():
if not symbol_data.is_ready:
continue
holding = algorithm.portfolio[symbol]
price = algorithm.securities[symbol].price
# buying condition
# - (1) price is above upper line
# - (2) and we are not long. this is a first time we crossed the line lately
if price > symbol_data.upper_line and not holding.is_long:
insight_close_time_utc = algorithm.utc_time + self.period
insights.append(Insight.price(symbol, insight_close_time_utc, InsightDirection.UP))
# selling condition
# - (1) price is lower that lower line
# - (2) and we are not short. this is a first time we crossed the line lately
if price < symbol_data.lower_line and not holding.is_short:
insight_close_time_utc = algorithm.utc_time + self.period
insights.append(Insight.price(symbol, insight_close_time_utc, InsightDirection.DOWN))
return insights
def on_securities_changed(self, algorithm, changes):
# added
for symbol in [x.symbol for x in changes.added_securities]:
if symbol not in self._symbol_data_by_symbol:
# add symbol/symbol_data pair to collection
symbol_data = self.SymbolData(symbol, self.k1, self.k2, self.range_period, self.consolidator_time_span)
self._symbol_data_by_symbol[symbol] = symbol_data
# register consolidator
algorithm.subscription_manager.add_consolidator(symbol, symbol_data.get_consolidator())
# removed
for symbol in [x.symbol for x in changes.removed_securities]:
symbol_data = self._symbol_data_by_symbol.pop(symbol, None)
if symbol_data is None:
algorithm.error("Unable to remove data from collection: DualThrustAlphaModel")
else:
# unsubscribe consolidator from data updates
algorithm.subscription_manager.remove_consolidator(symbol, symbol_data.get_consolidator())
class SymbolData:
'''Contains data specific to a symbol required by this model'''
def __init__(self, symbol, k1, k2, range_period, consolidator_resolution):
self.symbol = symbol
self.range_window = RollingWindow(range_period)
self.consolidator = TradeBarConsolidator(consolidator_resolution)
def on_data_consolidated(sender, consolidated):
# add new tradebar to
self.range_window.add(consolidated)
if self.range_window.is_ready:
hh = max([x.high for x in self.range_window])
hc = max([x.close for x in self.range_window])
lc = min([x.close for x in self.range_window])
ll = min([x.low for x in self.range_window])
range = max([hh - lc, hc - ll])
self.upper_line = consolidated.close + k1 * range
self.lower_line = consolidated.close - k2 * range
# event fired at new consolidated trade bar
self.consolidator.data_consolidated += on_data_consolidated
# Returns the interior consolidator
def get_consolidator(self):
return self.consolidator
@property
def is_ready(self):
return self.range_window.is_ready
@@ -0,0 +1,24 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from FundamentalRegressionAlgorithm import FundamentalRegressionAlgorithm
### <summary>
### Example algorithm using the asynchronous universe selection functionality
### </summary>
class AsynchronousUniverseRegressionAlgorithm(FundamentalRegressionAlgorithm):
def initialize(self):
super().initialize()
self.universe_settings.asynchronous = True
@@ -0,0 +1,44 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
# <summary>
# Regression algorithm to test the behaviour of ARMA versus AR models at the same order of differencing.
# In particular, an ARIMA(1,1,1) and ARIMA(1,1,0) are instantiated while orders are placed if their difference
# is sufficiently large (which would be due to the inclusion of the MA(1) term).
# </summary>
class AutoRegressiveIntegratedMovingAverageRegressionAlgorithm(QCAlgorithm):
def initialize(self) -> None:
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013, 1, 7)
self.set_end_date(2013, 12, 11)
self.settings.automatic_indicator_warm_up = True
self.add_equity("SPY", Resolution.DAILY)
self._arima = self.arima("SPY", 1, 1, 1, 50)
self._ar = self.arima("SPY", 1, 1, 0, 50)
self._last = None
def on_data(self, data: Slice) -> None:
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if self._arima.is_ready:
if abs(self._arima.current.value - self._ar.current.value) > 1:
if self._arima.current.value > self._last:
self.market_order("SPY", 1)
else:
self.market_order("SPY", -1)
self._last = self._arima.current.value
@@ -0,0 +1,57 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Example algorithm using and asserting the behavior of auxiliary data handlers
### </summary>
class AuxiliaryDataHandlersRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2007, 5, 16)
self.set_end_date(2015, 1, 1)
self.universe_settings.resolution = Resolution.DAILY
# will get delisted
self.add_equity("AAA.1")
# get's remapped
self.add_equity("SPWR")
# has a split & dividends
self.add_equity("AAPL")
def on_delistings(self, delistings: Delistings):
self._on_delistings_called = True
def on_symbol_changed_events(self, symbolsChanged: SymbolChangedEvents):
self._on_symbol_changed_events = True
def on_splits(self, splits: Splits):
self._on_splits = True
def on_dividends(self, dividends: Dividends):
self._on_dividends = True
def on_end_of_algorithm(self):
if not self._on_delistings_called:
raise ValueError("OnDelistings was not called!")
if not self._on_symbol_changed_events:
raise ValueError("OnSymbolChangedEvents was not called!")
if not self._on_splits:
raise ValueError("OnSplits was not called!")
if not self._on_dividends:
raise ValueError("OnDividends was not called!")
@@ -0,0 +1,49 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Abstract regression framework algorithm for multiple framework regression tests
### </summary>
class BaseFrameworkRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2014, 6, 1)
self.set_end_date(2014, 6, 30)
self.universe_settings.resolution = Resolution.HOUR
self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
symbols = [Symbol.create(ticker, SecurityType.EQUITY, Market.USA)
for ticker in ["AAPL", "AIG", "BAC", "SPY"]]
# Manually add AAPL and AIG when the algorithm starts
self.set_universe_selection(ManualUniverseSelectionModel(symbols[:2]))
# At midnight, add all securities every day except on the last data
# With this procedure, the Alpha Model will experience multiple universe changes
self.add_universe_selection(ScheduledUniverseSelectionModel(
self.date_rules.every_day(), self.time_rules.midnight,
lambda dt: symbols if dt < self.end_date - timedelta(1) else []))
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(31), 0.025, None))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
def on_end_of_algorithm(self):
# The base implementation checks for active insights
insights_count = len(self.insights.get_insights(lambda insight: insight.is_active(self.utc_time)))
if insights_count != 0:
raise AssertionError(f"The number of active insights should be 0. Actual: {insights_count}")
@@ -0,0 +1,37 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class BasicCSharpIntegrationTemplateAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013,10, 7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
self.add_equity("SPY", Resolution.SECOND)
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.set_holdings("SPY", 1)
## Calculate value of sin(10) for both python and C#
self.debug(f'According to Python, the value of sin(10) is {np.sin(10)}')
self.debug(f'According to C#, the value of sin(10) is {Math.sin(10)}')
@@ -0,0 +1,42 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic algorithm using SetAccountCurrency
### </summary>
class BasicSetAccountCurrencyAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2018, 4, 4) #Set Start Date
self.set_end_date(2018, 4, 4) #Set End Date
self.set_brokerage_model(BrokerageName.GDAX, AccountType.CASH)
self.set_account_currency_and_amount()
self._btc_eur = self.add_crypto("BTCEUR").symbol
def set_account_currency_and_amount(self):
# Before setting any cash or adding a Security call SetAccountCurrency
self.set_account_currency("EUR")
self.set_cash(100000) #Set Strategy Cash
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.set_holdings(self._btc_eur, 1)
@@ -0,0 +1,23 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicSetAccountCurrencyAlgorithm import BasicSetAccountCurrencyAlgorithm
### <summary>
### Basic algorithm using SetAccountCurrency with an amount
### </summary>
class BasicSetAccountCurrencyWithAmountAlgorithm(BasicSetAccountCurrencyAlgorithm):
def set_account_currency_and_amount(self):
# Before setting any cash or adding a Security call SetAccountCurrency
self.set_account_currency("EUR", 200000)
@@ -0,0 +1,43 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10, 7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.add_equity("SPY", Resolution.MINUTE)
self.debug("numpy test >>> print numpy.pi: " + str(np.pi))
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.set_holdings("SPY", 1)
@@ -0,0 +1,44 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic template algorithm for the Axos brokerage
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateAxosAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10, 7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
self.set_brokerage_model(BrokerageName.AXOS)
self.add_equity("SPY", Resolution.MINUTE)
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
# will set 25% of our buying power with a market order
self.set_holdings("SPY", 0.25)
self.debug("Purchased SPY!")
@@ -0,0 +1,53 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### The demonstration algorithm shows some of the most common order methods when working with CFD assets.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateCfdAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_account_currency('EUR')
self.set_start_date(2019, 2, 20)
self.set_end_date(2019, 2, 21)
self.set_cash('EUR', 100000)
self._symbol = self.add_cfd('DE30EUR').symbol
# Historical Data
history = self.history(self._symbol, 60, Resolution.DAILY)
self.log(f"Received {len(history)} bars from CFD historical data call.")
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
slice: Slice object keyed by symbol containing the stock data
'''
# Access Data
if data.quote_bars.contains_key(self._symbol):
quote_bar = data.quote_bars[self._symbol]
self.log(f"{quote_bar.end_time} :: {quote_bar.close}")
if not self.portfolio.invested:
self.set_holdings(self._symbol, 1)
def on_order_event(self, order_event):
self.debug("{} {}".format(self.time, order_event.to_string()))
@@ -0,0 +1,72 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic Continuous Futures Template Algorithm
### </summary>
class BasicTemplateContinuousFutureAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013, 7, 1)
self.set_end_date(2014, 1, 1)
self._continuous_contract = self.add_future(Futures.Indices.SP_500_E_MINI,
data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode = DataMappingMode.LAST_TRADING_DAY,
contract_depth_offset = 0)
self._fast = self.sma(self._continuous_contract.symbol, 4, Resolution.DAILY)
self._slow = self.sma(self._continuous_contract.symbol, 10, Resolution.DAILY)
self._current_contract = None
# Minimum SMA gap required before acting on a cross; see the workaround note in on_data.
self._cross_threshold = 0.001
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
for changed_event in data.symbol_changed_events.values():
if changed_event.symbol == self._continuous_contract.symbol:
self.log(f"SymbolChanged event: {changed_event}")
# Workaround so the C# and Python versions take the exact same trades on the limited
# sample data in the repository (decimal vs double rounding can disagree at a cross).
if not self.portfolio.invested:
if self._fast.current.value - self._slow.current.value > self._cross_threshold:
self._current_contract = self.securities[self._continuous_contract.mapped]
self.buy(self._current_contract.symbol, 1)
elif self._slow.current.value - self._fast.current.value > self._cross_threshold:
self.liquidate()
# We check exchange hours because the contract mapping can call OnData outside of regular hours.
if self._current_contract is not None and self._current_contract.symbol != self._continuous_contract.mapped and self._continuous_contract.exchange.exchange_open:
self.log(f"{self.time} - rolling position from {self._current_contract.symbol} to {self._continuous_contract.mapped}")
current_position_size = self._current_contract.holdings.quantity
self.liquidate(self._current_contract.symbol)
self.buy(self._continuous_contract.mapped, current_position_size)
self._current_contract = self.securities[self._continuous_contract.mapped]
def on_order_event(self, order_event):
self.debug("Purchased Stock: {0}".format(order_event.symbol))
def on_securities_changed(self, changes):
self.debug(f"{self.time}-{changes}")
@@ -0,0 +1,78 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic Continuous Futures Template Algorithm with extended market hours
### </summary>
class BasicTemplateContinuousFutureWithExtendedMarketAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013, 7, 1)
self.set_end_date(2014, 1, 1)
self._continuous_contract = self.add_future(Futures.Indices.SP_500_E_MINI,
data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode = DataMappingMode.LAST_TRADING_DAY,
contract_depth_offset = 0,
extended_market_hours = True)
self._fast = self.sma(self._continuous_contract.symbol, 4, Resolution.DAILY)
self._slow = self.sma(self._continuous_contract.symbol, 10, Resolution.DAILY)
self._current_contract = None
# Minimum SMA gap required before acting on a cross; see the workaround note in on_data.
self._cross_threshold = 0.001
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
for changed_event in data.symbol_changed_events.values():
if changed_event.symbol == self._continuous_contract.symbol:
self.log(f"SymbolChanged event: {changed_event}")
if not self.is_market_open(self._continuous_contract.symbol):
return
# This is just to limit the amount of orders done in this regression test, since data in the repo is limited.
# Also limit it to 3 orders so that the continuous contract rolls happens with an open position.
if self.time < datetime(2013, 11, 12) and self.transactions.orders_count < 3:
# Workaround so the C# and Python versions take the exact same trades on the limited
# sample data in the repository (decimal vs double rounding can disagree at a cross).
if not self.portfolio.invested:
if self._fast.current.value - self._slow.current.value > self._cross_threshold:
self._current_contract = self.securities[self._continuous_contract.mapped]
self.buy(self._current_contract.symbol, 1)
elif self._slow.current.value - self._fast.current.value > self._cross_threshold:
self.liquidate()
if self._current_contract is not None and self._current_contract.symbol != self._continuous_contract.mapped:
self.log(f"{Time} - rolling position from {self._current_contract.symbol} to {self._continuous_contract.mapped}")
current_position_size = self._current_contract.holdings.quantity
self.liquidate(self._current_contract.symbol)
self.buy(self._continuous_contract.mapped, current_position_size)
self._current_contract = self.securities[self._continuous_contract.mapped]
def on_order_event(self, order_event):
self.debug("Purchased Stock: {0}".format(order_event.symbol))
def on_securities_changed(self, changes):
self.debug(f"{self.time}-{changes}")
@@ -0,0 +1,145 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### The demonstration algorithm shows some of the most common order methods when working with Crypto assets.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateCryptoAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2018, 4, 4) #Set Start Date
self.set_end_date(2018, 4, 4) #Set End Date
# Although typically real brokerages as GDAX only support a single account currency,
# here we add both USD and EUR to demonstrate how to handle non-USD account currencies.
# Set Strategy Cash (USD)
self.set_cash(10000)
# Set Strategy Cash (EUR)
# EUR/USD conversion rate will be updated dynamically
self.set_cash("EUR", 10000)
# Add some coins as initial holdings
# When connected to a real brokerage, the amount specified in SetCash
# will be replaced with the amount in your actual account.
self.set_cash("BTC", 1)
self.set_cash("ETH", 5)
self.set_brokerage_model(BrokerageName.GDAX, AccountType.CASH)
# You can uncomment the following lines when live trading with GDAX,
# to ensure limit orders will only be posted to the order book and never executed as a taker (incurring fees).
# Please note this statement has no effect in backtesting or paper trading.
# self.default_order_properties = GDAXOrderProperties()
# self.default_order_properties.post_only = True
# Find more symbols here: http://quantconnect.com/data
self.add_crypto("BTCUSD", Resolution.MINUTE)
self.add_crypto("ETHUSD", Resolution.MINUTE)
self.add_crypto("BTCEUR", Resolution.MINUTE)
symbol = self.add_crypto("LTCUSD", Resolution.MINUTE).symbol
# create two moving averages
self.fast = self.ema(symbol, 30, Resolution.MINUTE)
self.slow = self.ema(symbol, 60, Resolution.MINUTE)
def on_data(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
# Note: all limit orders in this algorithm will be paying taker fees,
# they shouldn't, but they do (for now) because of this issue:
# https://github.com/QuantConnect/Lean/issues/1852
if self.time.hour == 1 and self.time.minute == 0:
# Sell all ETH holdings with a limit order at 1% above the current price
limit_price = round(self.securities["ETHUSD"].price * 1.01, 2)
quantity = self.portfolio.cash_book["ETH"].amount
self.limit_order("ETHUSD", -quantity, limit_price)
elif self.time.hour == 2 and self.time.minute == 0:
# Submit a buy limit order for BTC at 5% below the current price
usd_total = self.portfolio.cash_book["USD"].amount
limit_price = round(self.securities["BTCUSD"].price * 0.95, 2)
# use only half of our total USD
quantity = usd_total * 0.5 / limit_price
self.limit_order("BTCUSD", quantity, limit_price)
elif self.time.hour == 2 and self.time.minute == 1:
# Get current USD available, subtracting amount reserved for buy open orders
usd_total = self.portfolio.cash_book["USD"].amount
usd_reserved = sum(x.quantity * x.limit_price for x
in [x for x in self.transactions.get_open_orders()
if x.direction == OrderDirection.BUY
and x.type == OrderType.LIMIT
and (x.symbol.value == "BTCUSD" or x.symbol.value == "ETHUSD")])
usd_available = usd_total - usd_reserved
self.debug("usd_available: {}".format(usd_available))
# Submit a marketable buy limit order for ETH at 1% above the current price
limit_price = round(self.securities["ETHUSD"].price * 1.01, 2)
# use all of our available USD
quantity = usd_available / limit_price
# this order will be rejected (for now) because of this issue:
# https://github.com/QuantConnect/Lean/issues/1852
self.limit_order("ETHUSD", quantity, limit_price)
# use only half of our available USD
quantity = usd_available * 0.5 / limit_price
self.limit_order("ETHUSD", quantity, limit_price)
elif self.time.hour == 11 and self.time.minute == 0:
# Liquidate our BTC holdings (including the initial holding)
self.set_holdings("BTCUSD", 0)
elif self.time.hour == 12 and self.time.minute == 0:
# Submit a market buy order for 1 BTC using EUR
self.buy("BTCEUR", 1)
# Submit a sell limit order at 10% above market price
limit_price = round(self.securities["BTCEUR"].price * 1.1, 2)
self.limit_order("BTCEUR", -1, limit_price)
elif self.time.hour == 13 and self.time.minute == 0:
# Cancel the limit order if not filled
self.transactions.cancel_open_orders("BTCEUR")
elif self.time.hour > 13:
# To include any initial holdings, we read the LTC amount from the cashbook
# instead of using Portfolio["LTCUSD"].quantity
if self.fast > self.slow:
if self.portfolio.cash_book["LTC"].amount == 0:
self.buy("LTCUSD", 10)
else:
if self.portfolio.cash_book["LTC"].amount > 0:
self.liquidate("LTCUSD")
def on_order_event(self, order_event):
self.debug("{} {}".format(self.time, order_event.to_string()))
def on_end_of_algorithm(self):
self.log("{} - TotalPortfolioValue: {}".format(self.time, self.portfolio.total_portfolio_value))
self.log("{} - CashBook: {}".format(self.time, self.portfolio.cash_book))
@@ -0,0 +1,146 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Minute resolution regression algorithm trading Coin and USDT binance futures long and short asserting the behavior
### </summary>
class BasicTemplateCryptoFutureAlgorithm(QCAlgorithm):
# <summary>
# Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
# </summary>
def initialize(self):
self.set_start_date(2022, 12, 13)
self.set_end_date(2022, 12, 13)
self.set_time_zone(TimeZones.UTC)
try:
self.set_brokerage_model(BrokerageName.BINANCE_FUTURES, AccountType.CASH)
except:
# expected, we don't allow cash account type
pass
self.set_brokerage_model(BrokerageName.BINANCE_FUTURES, AccountType.MARGIN)
self.btc_usd = self.add_crypto_future("BTCUSD")
self.ada_usdt = self.add_crypto_future("ADAUSDT")
self.fast = self.ema(self.btc_usd.symbol, 30, Resolution.MINUTE)
self.slow = self.ema(self.btc_usd.symbol, 60, Resolution.MINUTE)
self.interest_per_symbol = {self.btc_usd.symbol: 0, self.ada_usdt.symbol: 0}
self.set_cash(1000000)
# the amount of BTC we need to hold to trade 'BTCUSD'
self.btc_usd.base_currency.set_amount(0.005)
# the amount of USDT we need to hold to trade 'ADAUSDT'
self.ada_usdt.quote_currency.set_amount(200)
# <summary>
# OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
# </summary>
# <param name="data">Slice object keyed by symbol containing the stock data</param>
def on_data(self, slice):
interest_rates = slice.Get(MarginInterestRate)
for interest_rate in interest_rates:
self.interest_per_symbol[interest_rate.key] += 1
self.cached_interest_rate = self.securities[interest_rate.key].cache.get_data(MarginInterestRate)
if self.cached_interest_rate != interest_rate.value:
raise AssertionError(f"Unexpected cached margin interest rate for {interest_rate.key}!")
if self.fast > self.slow:
if self.portfolio.invested == False and self.transactions.orders_count == 0:
self.ticket = self.buy(self.btc_usd.symbol, 50)
if self.ticket.status != OrderStatus.INVALID:
raise AssertionError(f"Unexpected valid order {self.ticket}, should fail due to margin not sufficient")
self.buy(self.btc_usd.symbol, 1)
self.margin_used = self.portfolio.total_margin_used
self.btc_usd_holdings = self.btc_usd.holdings
# Coin futures value is 100 USD
self.holdings_value_btc_usd = 100
if abs(self.btc_usd_holdings.total_sale_volume - self.holdings_value_btc_usd) > 1:
raise AssertionError(f"Unexpected TotalSaleVolume {self.btc_usd_holdings.total_sale_volume}")
if abs(self.btc_usd_holdings.absolute_holdings_cost - self.holdings_value_btc_usd) > 1:
raise AssertionError(f"Unexpected holdings cost {self.btc_usd_holdings.holdings_cost}")
# margin used is based on the maintenance rate
if BuyingPowerModelExtensions.get_maintenance_margin(self.btc_usd.buying_power_model, self.btc_usd) != self.margin_used:
raise AssertionError(f"Unexpected margin used {self.margin_used}")
self.buy(self.ada_usdt.symbol, 1000)
self.margin_used = self.portfolio.total_margin_used - self.margin_used
self.ada_usdt_holdings = self.ada_usdt.holdings
# USDT/BUSD futures value is based on it's price
self.holdings_value_usdt = self.ada_usdt.price * self.ada_usdt.symbol_properties.contract_multiplier * 1000
if abs(self.ada_usdt_holdings.total_sale_volume - self.holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected TotalSaleVolume {self.ada_usdt_holdings.total_sale_volume}")
if abs(self.ada_usdt_holdings.absolute_holdings_cost - self.holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {self.ada_usdt_holdings.holdings_cost}")
if BuyingPowerModelExtensions.get_maintenance_margin(self.ada_usdt.buying_power_model, self.ada_usdt) != self.margin_used:
raise AssertionError(f"Unexpected margin used {self.margin_used}")
# position just opened should be just spread here
self.profit = self.portfolio.total_unrealized_profit
if (5 - abs(self.profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
if (self.portfolio.total_profit != 0):
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
else:
if self.time.hour > 10 and self.transactions.orders_count == 3:
self.sell(self.btc_usd.symbol, 3)
self.btc_usd_holdings = self.btc_usd.holdings
if abs(self.btc_usd_holdings.absolute_holdings_cost - 100 * 2) > 1:
raise AssertionError(f"Unexpected holdings cost {self.btc_usd_holdings.holdings_cost}")
self.sell(self.ada_usdt.symbol, 3000)
ada_usdt_holdings = self.ada_usdt.holdings
# USDT/BUSD futures value is based on it's price
holdings_value_usdt = self.ada_usdt.price * self.ada_usdt.symbol_properties.contract_multiplier * 2000
if abs(ada_usdt_holdings.absolute_holdings_cost - holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {ada_usdt_holdings.holdings_cost}")
# position just opened should be just spread here
profit = self.portfolio.total_unrealized_profit
if (5 - abs(profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
# we barely did any difference on the previous trade
if (5 - abs(self.portfolio.total_profit)) < 0:
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
def on_end_of_algorithm(self):
if self.interest_per_symbol[self.ada_usdt.symbol] != 1:
raise AssertionError(f"Unexpected interest rate count {self.interest_per_symbol[self.ada_usdt.symbol]}")
if self.interest_per_symbol[self.btc_usd.symbol] != 3:
raise AssertionError(f"Unexpected interest rate count {self.interest_per_symbol[self.btc_usd.symbol]}")
def on_order_event(self, order_event):
self.debug("{0} {1}".format(self.time, order_event))
@@ -0,0 +1,126 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Hourly regression algorithm trading ADAUSDT binance futures long and short asserting the behavior
### </summary>
class BasicTemplateCryptoFutureHourlyAlgorithm(QCAlgorithm):
# <summary>
# Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
# </summary>
def initialize(self):
self.set_start_date(2022, 12, 13)
self.set_end_date(2022, 12, 13)
self.set_time_zone(TimeZones.UTC)
try:
self.set_brokerage_model(BrokerageName.BINANCE_COIN_FUTURES, AccountType.CASH)
except:
# expected, we don't allow cash account type
pass
self.set_brokerage_model(BrokerageName.BINANCE_COIN_FUTURES, AccountType.MARGIN)
self.ada_usdt = self.add_crypto_future("ADAUSDT", Resolution.HOUR)
self.fast = self.ema(self.ada_usdt.symbol, 3, Resolution.HOUR)
self.slow = self.ema(self.ada_usdt.symbol, 6, Resolution.HOUR)
self.interest_per_symbol = {self.ada_usdt.symbol: 0}
# Default USD cash, set 1M but it wont be used
self.set_cash(1000000)
# the amount of USDT we need to hold to trade 'ADAUSDT'
self.ada_usdt.quote_currency.set_amount(200)
# <summary>
# OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
# </summary>
# <param name="data">Slice object keyed by symbol containing the stock data</param>
def on_data(self, slice):
interest_rates = slice.get(MarginInterestRate);
for interest_rate in interest_rates:
self.interest_per_symbol[interest_rate.key] += 1
self.cached_interest_rate = self.securities[interest_rate.key].cache.get_data(MarginInterestRate)
if self.cached_interest_rate != interest_rate.value:
raise AssertionError(f"Unexpected cached margin interest rate for {interest_rate.key}!")
if self.fast > self.slow:
if self.portfolio.invested == False and self.transactions.orders_count == 0:
self.ticket = self.buy(self.ada_usdt.symbol, 100000)
if self.ticket.status != OrderStatus.INVALID:
raise AssertionError(f"Unexpected valid order {self.ticket}, should fail due to margin not sufficient")
self.buy(self.ada_usdt.symbol, 1000)
self.margin_used = self.portfolio.total_margin_used
self.ada_usdt_holdings = self.ada_usdt.holdings
# USDT/BUSD futures value is based on it's price
self.holdings_value_usdt = self.ada_usdt.price * self.ada_usdt.symbol_properties.contract_multiplier * 1000
if abs(self.ada_usdt_holdings.total_sale_volume - self.holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected TotalSaleVolume {self.ada_usdt_holdings.total_sale_volume}")
if abs(self.ada_usdt_holdings.absolute_holdings_cost - self.holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {self.ada_usdt_holdings.holdings_cost}")
if BuyingPowerModelExtensions.get_maintenance_margin(self.ada_usdt.buying_power_model, self.ada_usdt) != self.margin_used:
raise AssertionError(f"Unexpected margin used {self.margin_used}")
# position just opened should be just spread here
self.profit = self.portfolio.total_unrealized_profit
if (5 - abs(self.profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
if (self.portfolio.total_profit != 0):
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
else:
# let's revert our position and double
if self.time.hour > 10 and self.transactions.orders_count == 2:
self.sell(self.ada_usdt.symbol, 3000)
self.ada_usdt_holdings = self.ada_usdt.holdings
# USDT/BUSD futures value is based on it's price
self.holdings_value_usdt = self.ada_usdt.price * self.ada_usdt.symbol_properties.contract_multiplier * 2000
if abs(self.ada_usdt_holdings.absolute_holdings_cost - self.holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {self.ada_usdt_holdings.holdings_cost}")
# position just opened should be just spread here
self.profit = self.portfolio.total_unrealized_profit
if (5 - abs(self.profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
# we barely did any difference on the previous trade
if (5 - abs(self.portfolio.total_profit)) < 0:
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
if self.time.hour >= 22 and self.transactions.orders_count == 3:
self.liquidate()
def on_end_of_algorithm(self):
if self.interest_per_symbol[self.ada_usdt.symbol] != 1:
raise AssertionError(f"Unexpected interest rate count {self.interest_per_symbol[self.ada_usdt.symbol]}")
def on_order_event(self, order_event):
self.debug("{0} {1}".format(self.time, order_event))
@@ -0,0 +1,42 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Demonstration of requesting daily resolution data for US Equities.
### This is a simple regression test algorithm using a skeleton algorithm and requesting daily data.
### </summary>
### <meta name="tag" content="using data" />
class BasicTemplateDailyAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10,8) #Set Start Date
self.set_end_date(2013,10,17) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.add_equity("SPY", Resolution.DAILY)
def on_data(self, data):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.set_holdings("SPY", 1)
self.debug("Purchased Stock")
@@ -0,0 +1,120 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This algorithm tests and demonstrates EUREX futures subscription and trading:
### - It tests contracts rollover by adding a continuous future and asserting that mapping happens at some point.
### - It tests basic trading by buying a contract and holding it until expiration.
### - It tests delisting and asserts the holdings are liquidated after that.
### </summary>
class BasicTemplateEurexFuturesAlgorithm(QCAlgorithm):
def __init__(self):
super().__init__()
self._continuous_contract = None
self._mapped_symbol = None
self._contract_to_trade = None
self._mappings_count = 0
self._bought_quantity = 0
self._liquidated_quantity = 0
self._delisted = False
def initialize(self):
self.set_start_date(2024, 5, 30)
self.set_end_date(2024, 6, 23)
self.set_account_currency(Currencies.EUR);
self.set_cash(1000000)
self._continuous_contract = self.add_future(
Futures.Indices.EURO_STOXX_50,
Resolution.MINUTE,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode=DataMappingMode.FIRST_DAY_MONTH,
contract_depth_offset=0,
)
self._continuous_contract.set_filter(timedelta(days=0), timedelta(days=180))
self._mapped_symbol = self._continuous_contract.mapped
benchmark = self.add_index("SX5E")
self.set_benchmark(benchmark.symbol)
func_seeder = FuncSecuritySeeder(self.get_last_known_prices)
self.set_security_initializer(lambda security: func_seeder.seed_security(security))
def on_data(self, slice):
for changed_event in slice.symbol_changed_events.values():
self._mappings_count += 1
if self._mappings_count > 1:
raise AssertionError(f"{self.time} - Unexpected number of symbol changed events (mappings): {self._mappings_count}. Expected only 1.")
self.debug(f"{self.time} - SymbolChanged event: {changed_event}")
if changed_event.old_symbol != str(self._mapped_symbol.id):
raise AssertionError(f"{self.time} - Unexpected symbol changed event old symbol: {changed_event}")
if changed_event.new_symbol != str(self._continuous_contract.mapped.id):
raise AssertionError(f"{self.time} - Unexpected symbol changed event new symbol: {changed_event}")
# Let's trade the previous mapped contract, so we can hold it until expiration for testing
# (will be sooner than the new mapped contract)
self._contract_to_trade = self._mapped_symbol
self._mapped_symbol = self._continuous_contract.mapped
# Let's trade after the mapping is done
if self._contract_to_trade is not None and self._bought_quantity == 0 and self.securities[self._contract_to_trade].exchange.exchange_open:
self.buy(self._contract_to_trade, 1)
if self._contract_to_trade is not None and slice.delistings.contains_key(self._contract_to_trade):
delisting = slice.delistings[self._contract_to_trade]
if delisting.type == DelistingType.DELISTED:
self._delisted = True
if self.portfolio.invested:
raise AssertionError(f"{self.time} - Portfolio should not be invested after the traded contract is delisted.")
def on_order_event(self, order_event):
if order_event.symbol != self._contract_to_trade:
raise AssertionError(f"{self.time} - Unexpected order event symbol: {order_event.symbol}. Expected {self._contract_to_trade}")
if order_event.direction == OrderDirection.BUY:
if order_event.status == OrderStatus.FILLED:
if self._bought_quantity != 0 and self._liquidated_quantity != 0:
raise AssertionError(f"{self.time} - Unexpected buy order event status: {order_event.status}")
self._bought_quantity = order_event.quantity
elif order_event.direction == OrderDirection.SELL:
if order_event.status == OrderStatus.FILLED:
if self._bought_quantity <= 0 and self._liquidated_quantity != 0:
raise AssertionError(f"{self.time} - Unexpected sell order event status: {order_event.status}")
self._liquidated_quantity = order_event.quantity
if self._liquidated_quantity != -self._bought_quantity:
raise AssertionError(f"{self.time} - Unexpected liquidated quantity: {self._liquidated_quantity}. Expected: {-self._bought_quantity}")
def on_securities_changed(self, changes):
for added_security in changes.added_securities:
if added_security.symbol.security_type == SecurityType.FUTURE and added_security.symbol.is_canonical():
self._mapped_symbol = self._continuous_contract.mapped
def on_end_of_algorithm(self):
if self._mappings_count == 0:
raise AssertionError(f"Unexpected number of symbol changed events (mappings): {self._mappings_count}. Expected 1.")
if not self._delisted:
raise AssertionError("Contract was not delisted")
# Make sure we traded and that the position was liquidated on delisting
if self._bought_quantity <= 0 or self._liquidated_quantity >= 0:
raise AssertionError(f"Unexpected sold quantity: {self._bought_quantity} and liquidated quantity: {self._liquidated_quantity}")
@@ -0,0 +1,35 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class BasicTemplateFillForwardAlgorithm(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,11,30) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.add_security(SecurityType.EQUITY, "ASUR", Resolution.SECOND)
def on_data(self, data):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.set_holdings("ASUR", 1)
@@ -0,0 +1,53 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Algorithm demonstrating FOREX asset types and requesting history on them in bulk. As FOREX uses
### QuoteBars you should request slices
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="history and warm up" />
### <meta name="tag" content="history" />
### <meta name="tag" content="forex" />
class BasicTemplateForexAlgorithm(QCAlgorithm):
def initialize(self):
# Set the cash we'd like to use for our backtest
self.set_cash(100000)
# Start and end dates for the backtest.
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
# Add FOREX contract you want to trade
# find available contracts here https://www.quantconnect.com/data#forex/oanda/cfd
self.add_forex("EURUSD", Resolution.MINUTE)
self.add_forex("GBPUSD", Resolution.MINUTE)
self.add_forex("EURGBP", Resolution.MINUTE)
self.history(5, Resolution.DAILY)
self.history(5, Resolution.HOUR)
self.history(5, Resolution.MINUTE)
history = self.history(TimeSpan.from_seconds(5), Resolution.SECOND)
for data in sorted(history, key=lambda x: x.time):
for key in data.keys():
self.log(str(key.value) + ": " + str(data.time) + " > " + str(data[key].value))
def on_data(self, data):
# Print to console to verify that data is coming in
for key in data.keys():
self.log(str(key.value) + ": " + str(data.time) + " > " + str(data[key].value))
@@ -0,0 +1,60 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic template framework algorithm uses framework components to define the algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateFrameworkAlgorithm(QCAlgorithm):
'''Basic template framework algorithm uses framework components to define the algorithm.'''
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
# Set requested data resolution
self.universe_settings.resolution = Resolution.MINUTE
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
# Forex, CFD, Equities Resolutions: Tick, Second, Minute, Hour, Daily.
# Futures Resolution: Tick, Second, Minute
# Options Resolution: Minute Only.
symbols = [ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]
# set algorithm framework models
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(minutes = 20), 0.025, None))
# We can define how often the EWPCM will rebalance if no new insight is submitted using:
# Resolution Enum:
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(Resolution.DAILY))
# timedelta
# self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(timedelta(2)))
# A lamdda datetime -> datetime. In this case, we can use the pre-defined func at Expiry helper class
# self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(Expiry.END_OF_WEEK))
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(MaximumDrawdownPercentPerSecurity(0.01))
self.debug("numpy test >>> print numpy.pi: " + str(np.pi))
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug("Purchased Stock: {0}".format(order_event.symbol))
@@ -0,0 +1,71 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### The demonstration algorithm shows some of the most common order methods when working with FutureOption assets.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateFutureOptionAlgorithm(QCAlgorithm):
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2022, 1, 1)
self.set_end_date(2022, 2, 1)
self.set_cash(100000)
gold_futures = self.add_future(Futures.Metals.GOLD, Resolution.MINUTE)
gold_futures.set_filter(0, 180)
self._symbol = gold_futures.symbol
self.add_future_option(self._symbol, lambda universe: universe.strikes(-5, +5)
.calls_only()
.back_month()
.only_apply_filter_at_market_open())
# Historical Data
history = self.history(self._symbol, 60, Resolution.DAILY)
self.log(f"Received {len(history)} bars from {self._symbol} FutureOption historical data call.")
def on_data(self, data):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
slice: Slice object keyed by symbol containing the stock data
'''
# Access Data
for kvp in data.option_chains:
underlying_future_contract = kvp.key.underlying
chain = kvp.value
if not chain: continue
for contract in chain:
self.log(f"""Canonical Symbol: {kvp.key};
Contract: {contract};
Right: {contract.right};
Expiry: {contract.expiry};
Bid price: {contract.bid_price};
Ask price: {contract.ask_price};
Implied Volatility: {contract.implied_volatility}""")
if not self.portfolio.invested:
atm_strike = sorted(chain, key = lambda x: abs(chain.underlying.price - x.strike))[0].strike
selected_contract = sorted([contract for contract in chain if contract.strike == atm_strike], \
key = lambda x: x.expiry, reverse=True)[0]
self.market_order(selected_contract.symbol, 1)
def on_order_event(self, order_event):
self.debug("{} {}".format(self.time, order_event.to_string()))
@@ -0,0 +1,129 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Example algorithm for trading continuous future
### </summary>
class BasicTemplateFutureRolloverAlgorithm(QCAlgorithm):
### <summary>
### Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
### </summary>
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2013, 12, 10)
self.set_cash(1000000)
self._symbol_data_by_symbol = {}
futures = [
Futures.Indices.SP_500_E_MINI
]
for future in futures:
# Requesting data
continuous_contract = self.add_future(future,
resolution = Resolution.DAILY,
extended_market_hours = True,
data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode = DataMappingMode.OPEN_INTEREST,
contract_depth_offset = 0
)
symbol_data = SymbolData(self, continuous_contract)
self._symbol_data_by_symbol[continuous_contract.symbol] = symbol_data
### <summary>
### on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
### </summary>
### <param name="slice">Slice object keyed by symbol containing the stock data</param>
def on_data(self, slice):
for symbol, symbol_data in self._symbol_data_by_symbol.items():
# Call SymbolData.update() method to handle new data slice received
symbol_data.update(slice)
# Check if information in SymbolData class and new slice data are ready for trading
if not symbol_data.is_ready or not slice.bars.contains_key(symbol):
return
ema_current_value = symbol_data.EMA.current.value
if ema_current_value < symbol_data.price and not symbol_data.is_long:
self.market_order(symbol_data.mapped, 1)
elif ema_current_value > symbol_data.price and not symbol_data.is_short:
self.market_order(symbol_data.mapped, -1)
### <summary>
### Abstracted class object to hold information (state, indicators, methods, etc.) from a Symbol/Security in a multi-security algorithm
### </summary>
class SymbolData:
### <summary>
### Constructor to instantiate the information needed to be hold
### </summary>
def __init__(self, algorithm, future):
self._algorithm = algorithm
self._future = future
self.EMA = algorithm.ema(future.symbol, 20, Resolution.DAILY)
self.price = 0
self.is_long = False
self.is_short = False
self.reset()
@property
def symbol(self):
return self._future.symbol
@property
def mapped(self):
return self._future.mapped
@property
def is_ready(self):
return self.mapped is not None and self.EMA.is_ready
### <summary>
### Handler of new slice of data received
### </summary>
def update(self, slice):
if slice.symbol_changed_events.contains_key(self.symbol):
changed_event = slice.symbol_changed_events[self.symbol]
old_symbol = changed_event.old_symbol
new_symbol = changed_event.new_symbol
tag = f"Rollover - Symbol changed at {self._algorithm.time}: {old_symbol} -> {new_symbol}"
quantity = self._algorithm.portfolio[old_symbol].quantity
# Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract
self._algorithm.liquidate(old_symbol, tag = tag)
self._algorithm.market_order(new_symbol, quantity, tag = tag)
self.reset()
self.price = slice.bars[self.symbol].price if slice.bars.contains_key(self.symbol) else self.price
self.is_long = self._algorithm.portfolio[self.mapped].is_long
self.is_short = self._algorithm.portfolio[self.mapped].is_short
### <summary>
### reset RollingWindow/indicator to adapt to newly mapped contract, then warm up the RollingWindow/indicator
### </summary>
def reset(self):
self.EMA.reset()
self._algorithm.warm_up_indicator(self.symbol, self.EMA, Resolution.DAILY)
### <summary>
### disposal method to remove consolidator/update method handler, and reset RollingWindow/indicator to free up memory and speed
### </summary>
def dispose(self):
self.EMA.reset()
@@ -0,0 +1,79 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add futures for a given underlying asset.
### It also shows how you can prefilter contracts easily based on expirations, and how you
### can inspect the futures chain to pick a specific contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2013, 10, 10)
self.set_cash(1000000)
self.contract_symbol = None
# Subscribe and set our expiry filter for the futures chain
futureSP500 = self.add_future(Futures.Indices.SP_500_E_MINI)
future_gold = self.add_future(Futures.Metals.GOLD)
# set our expiry filter for this futures chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
futureSP500.set_filter(timedelta(0), timedelta(182))
future_gold.set_filter(0, 182)
benchmark = self.add_equity("SPY")
self.set_benchmark(benchmark.symbol)
seeder = FuncSecuritySeeder(self.get_last_known_prices)
self.set_security_initializer(lambda security: seeder.seed_security(security))
def on_data(self,slice):
if not self.portfolio.invested:
for chain in slice.future_chains:
# Get contracts expiring no earlier than in 90 days
contracts = list(filter(lambda x: x.expiry > self.time + timedelta(90), chain.value))
# if there is any contract, trade the front contract
if len(contracts) == 0: continue
front = sorted(contracts, key = lambda x: x.expiry, reverse=True)[0]
self.contract_symbol = front.symbol
self.market_order(front.symbol , 1)
else:
self.liquidate()
def on_end_of_algorithm(self):
# Get the margin requirements
buying_power_model = self.securities[self.contract_symbol].buying_power_model
name = type(buying_power_model).__name__
if name != 'FutureMarginModel':
raise AssertionError(f"Invalid buying power model. Found: {name}. Expected: FutureMarginModel")
initial_overnight = buying_power_model.initial_overnight_margin_requirement
maintenance_overnight = buying_power_model.maintenance_overnight_margin_requirement
initial_intraday = buying_power_model.initial_intraday_margin_requirement
maintenance_intraday = buying_power_model.maintenance_intraday_margin_requirement
def on_securities_changed(self, changes):
for added_security in changes.added_securities:
if added_security.symbol.security_type == SecurityType.FUTURE and not added_security.symbol.is_canonical() and not added_security.has_data:
raise AssertionError(f"Future contracts did not work up as expected: {added_security.symbol}")
@@ -0,0 +1,57 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### A demonstration of consolidating futures data into larger bars for your algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="consolidating data" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesConsolidationAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
self.set_cash(1000000)
# Subscribe and set our expiry filter for the futures chain
futureSP500 = self.add_future(Futures.Indices.SP_500_E_MINI)
# set our expiry filter for this future chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
futureSP500.set_filter(0, 182)
# future.set_filter(timedelta(0), timedelta(182))
self.consolidators = dict()
def on_data(self,slice):
pass
def on_data_consolidated(self, sender, quote_bar):
self.log("OnDataConsolidated called on " + str(self.time))
self.log(str(quote_bar))
def on_securities_changed(self, changes):
for security in changes.added_securities:
consolidator = QuoteBarConsolidator(timedelta(minutes=5))
consolidator.data_consolidated += self.on_data_consolidated
self.subscription_manager.add_consolidator(security.symbol, consolidator)
self.consolidators[security.symbol] = consolidator
for security in changes.removed_securities:
consolidator = self.consolidators.pop(security.symbol)
self.subscription_manager.remove_consolidator(security.symbol, consolidator)
consolidator.data_consolidated -= self.on_data_consolidated
@@ -0,0 +1,67 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add futures with daily resolution.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesDailyAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2014, 10, 10)
self.set_cash(1000000)
resolution = self.get_resolution()
extended_market_hours = self.get_extended_market_hours()
# Subscribe and set our expiry filter for the futures chain
self.future_sp500 = self.add_future(Futures.Indices.SP_500_E_MINI, resolution, extended_market_hours=extended_market_hours)
self.future_gold = self.add_future(Futures.Metals.GOLD, resolution, extended_market_hours=extended_market_hours)
# set our expiry filter for this futures chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
self.future_sp500.set_filter(timedelta(0), timedelta(182))
self.future_gold.set_filter(0, 182)
def on_data(self,slice):
if not self.portfolio.invested:
for chain in slice.future_chains:
# Get contracts expiring no earlier than in 90 days
contracts = list(filter(lambda x: x.expiry > self.time + timedelta(90), chain.value))
# if there is any contract, trade the front contract
if len(contracts) == 0: continue
contract = sorted(contracts, key = lambda x: x.expiry)[0]
# if found, trade it.
self.market_order(contract.symbol, 1)
# Same as above, check for cases like trading on a friday night.
elif all(x.exchange.hours.is_open(self.time, True) for x in self.securities.values() if x.invested):
self.liquidate()
def on_securities_changed(self, changes: SecurityChanges) -> None:
if len(changes.removed_securities) > 0 and \
self.portfolio.invested and \
all(x.exchange.hours.is_open(self.time, True) for x in self.securities.values() if x.invested):
self.liquidate()
def get_resolution(self):
return Resolution.DAILY
def get_extended_market_hours(self):
return False
@@ -0,0 +1,81 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from Alphas.ConstantAlphaModel import ConstantAlphaModel
from Selection.FutureUniverseSelectionModel import FutureUniverseSelectionModel
### <summary>
### Basic template futures framework algorithm uses framework components
### to define an algorithm that trades futures.
### </summary>
class BasicTemplateFuturesFrameworkAlgorithm(QCAlgorithm):
def initialize(self):
self.universe_settings.resolution = Resolution.MINUTE
self.universe_settings.extended_market_hours = self.get_extended_market_hours()
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
self.set_cash(100000)
# set framework models
self.set_universe_selection(FrontMonthFutureUniverseSelectionModel(self.select_future_chain_symbols))
self.set_alpha(ConstantFutureContractAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(1)))
self.set_portfolio_construction(SingleSharePortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
def select_future_chain_symbols(self, utc_time):
new_york_time = Extensions.convert_from_utc(utc_time, TimeZones.NEW_YORK)
if new_york_time.date() < date(2013, 10, 9):
return [ Symbol.create(Futures.Indices.SP_500_E_MINI, SecurityType.FUTURE, Market.CME) ]
else:
return [ Symbol.create(Futures.Metals.GOLD, SecurityType.FUTURE, Market.COMEX) ]
def get_extended_market_hours(self):
return False
class FrontMonthFutureUniverseSelectionModel(FutureUniverseSelectionModel):
'''Creates futures chain universes that select the front month contract and runs a user
defined future_chain_symbol_selector every day to enable choosing different futures chains'''
def __init__(self, select_future_chain_symbols):
super().__init__(timedelta(1), select_future_chain_symbols)
def filter(self, filter):
'''Defines the futures chain universe filter'''
return (filter.front_month()
.only_apply_filter_at_market_open())
class ConstantFutureContractAlphaModel(ConstantAlphaModel):
'''Implementation of a constant alpha model that only emits insights for future symbols'''
def __init__(self, _type, direction, period):
super().__init__(_type, direction, period)
def should_emit_insight(self, utc_time, symbol):
# only emit alpha for future symbols and not underlying equity symbols
if symbol.security_type != SecurityType.FUTURE:
return False
return super().should_emit_insight(utc_time, symbol)
class SingleSharePortfolioConstructionModel(PortfolioConstructionModel):
'''Portfolio construction model that sets target quantities to 1 for up insights and -1 for down insights'''
def create_targets(self, algorithm, insights):
targets = []
for insight in insights:
targets.append(PortfolioTarget(insight.symbol, insight.direction))
return targets
@@ -0,0 +1,25 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicTemplateFuturesFrameworkAlgorithm import BasicTemplateFuturesFrameworkAlgorithm
### <summary>
### Basic template futures framework algorithm uses framework components
### to define an algorithm that trades futures.
### </summary>
class BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm(BasicTemplateFuturesFrameworkAlgorithm):
def get_extended_market_hours(self):
return True
@@ -0,0 +1,83 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to get access to futures history for a given root symbol.
### It also shows how you can prefilter contracts easily based on expirations, and inspect the futures
### chain to pick a specific contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="history and warm up" />
### <meta name="tag" content="history" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesHistoryAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2013, 10, 9)
self.set_cash(1000000)
extended_market_hours = self.get_extended_market_hours()
# Subscribe and set our expiry filter for the futures chain
# find the front contract expiring no earlier than in 90 days
future_es = self.add_future(Futures.Indices.SP_500_E_MINI, Resolution.MINUTE, extended_market_hours=extended_market_hours)
future_es.set_filter(timedelta(0), timedelta(182))
future_gc = self.add_future(Futures.Metals.GOLD, Resolution.MINUTE, extended_market_hours=extended_market_hours)
future_gc.set_filter(timedelta(0), timedelta(182))
self.set_benchmark(lambda x: 1000000)
self.schedule.on(self.date_rules.every_day(), self.time_rules.every(timedelta(hours=1)), self.make_history_call)
self._success_count = 0
def make_history_call(self):
history = self.history(self.securities.keys(), 10, Resolution.MINUTE)
if len(history) < 10:
raise AssertionError(f'Empty history at {self.time}')
self._success_count += 1
def on_end_of_algorithm(self):
if self._success_count < self.get_expected_history_call_count():
raise AssertionError(f'Scheduled Event did not assert history call as many times as expected: {self._success_count}/49')
def on_data(self,slice):
if self.portfolio.invested: return
for chain in slice.future_chains:
for contract in chain.value:
self.log(f'{contract.symbol.value},' +
f'Bid={contract.bid_price} ' +
f'Ask={contract.ask_price} ' +
f'Last={contract.last_price} ' +
f'OI={contract.open_interest}')
def on_securities_changed(self, changes):
for change in changes.added_securities:
history = self.history(change.symbol, 10, Resolution.MINUTE).sort_index(level='time', ascending=False)[:3]
for index, row in history.iterrows():
self.log(f'History: {index[1]} : {index[2]:%m/%d/%Y %I:%M:%S %p} > {row.close}')
def on_order_event(self, order_event):
# Order fill event handler. On an order fill update the resulting information is passed to this method.
# Order event details containing details of the events
self.log(f'{order_event}')
def get_extended_market_hours(self):
return False
def get_expected_history_call_count(self):
return 42
@@ -0,0 +1,33 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicTemplateFuturesHistoryAlgorithm import BasicTemplateFuturesHistoryAlgorithm
### <summary>
### This example demonstrates how to get access to futures history for a given root symbol with extended market hours.
### It also shows how you can prefilter contracts easily based on expirations, and inspect the futures
### chain to pick a specific contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="history and warm up" />
### <meta name="tag" content="history" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesHistoryWithExtendedMarketHoursAlgorithm(BasicTemplateFuturesHistoryAlgorithm):
def get_extended_market_hours(self):
return True
def get_expected_history_call_count(self):
return 49
@@ -0,0 +1,26 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicTemplateFuturesDailyAlgorithm import BasicTemplateFuturesDailyAlgorithm
### <summary>
### This example demonstrates how to add futures with hourly resolution.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesHourlyAlgorithm(BasicTemplateFuturesDailyAlgorithm):
def get_resolution(self):
return Resolution.HOUR
@@ -0,0 +1,79 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add futures for a given underlying asset.
### It also shows how you can prefilter contracts easily based on expirations, and how you
### can inspect the futures chain to pick a specific contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesWithExtendedMarketAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 8)
self.set_end_date(2013, 10, 10)
self.set_cash(1000000)
self.contract_symbol = None
# Subscribe and set our expiry filter for the futures chain
self.future_sp500 = self.add_future(Futures.Indices.SP_500_E_MINI, extended_market_hours = True)
self.future_gold = self.add_future(Futures.Metals.GOLD, extended_market_hours = True)
# set our expiry filter for this futures chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
self.future_sp500.set_filter(timedelta(0), timedelta(182))
self.future_gold.set_filter(0, 182)
benchmark = self.add_equity("SPY")
self.set_benchmark(benchmark.symbol)
seeder = FuncSecuritySeeder(self.get_last_known_prices)
self.set_security_initializer(lambda security: seeder.seed_security(security))
def on_data(self,slice):
if not self.portfolio.invested:
for chain in slice.future_chains:
# Get contracts expiring no earlier than in 90 days
contracts = list(filter(lambda x: x.expiry > self.time + timedelta(90), chain.value))
# if there is any contract, trade the front contract
if len(contracts) == 0: continue
front = sorted(contracts, key = lambda x: x.expiry, reverse=True)[0]
self.contract_symbol = front.symbol
self.market_order(front.symbol , 1)
else:
self.liquidate()
def on_end_of_algorithm(self):
# Get the margin requirements
buying_power_model = self.securities[self.contract_symbol].buying_power_model
name = type(buying_power_model).__name__
if name != 'FutureMarginModel':
raise AssertionError(f"Invalid buying power model. Found: {name}. Expected: FutureMarginModel")
initial_overnight = buying_power_model.initial_overnight_margin_requirement
maintenance_overnight = buying_power_model.maintenance_overnight_margin_requirement
initial_intraday = buying_power_model.initial_intraday_margin_requirement
maintenance_intraday = buying_power_model.maintenance_intraday_margin_requirement
def on_securities_changed(self, changes):
for added_security in changes.added_securities:
if added_security.symbol.security_type == SecurityType.FUTURE and not added_security.symbol.is_canonical() and not added_security.has_data:
raise AssertionError(f"Future contracts did not work up as expected: {added_security.symbol}")
@@ -0,0 +1,25 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicTemplateFuturesDailyAlgorithm import BasicTemplateFuturesDailyAlgorithm
### <summary>
### This example demonstrates how to add futures with daily resolution and extended market hours.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesWithExtendedMarketDailyAlgorithm(BasicTemplateFuturesDailyAlgorithm):
def get_extended_market_hours(self):
return True
@@ -0,0 +1,25 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from BasicTemplateFuturesHourlyAlgorithm import BasicTemplateFuturesHourlyAlgorithm
### <summary>
### This example demonstrates how to add futures with hourly resolution and extended market hours.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="futures" />
class BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm(BasicTemplateFuturesHourlyAlgorithm):
def get_extended_market_hours(self):
return True
@@ -0,0 +1,54 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from AlgorithmImports import *
class BasicTemplateIndexAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2021, 1, 4)
self.set_end_date(2021, 1, 18)
self.set_cash(1000000)
# Use indicator for signal; but it cannot be traded
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
# Trade on SPX ITM calls
self.spx_option = Symbol.create_option(
self.spx,
Market.USA,
OptionStyle.EUROPEAN,
OptionRight.CALL,
3200,
datetime(2021, 1, 15)
)
self.add_index_option_contract(self.spx_option, Resolution.MINUTE)
self.ema_slow = self.ema(self.spx, 80)
self.ema_fast = self.ema(self.spx, 200)
def on_data(self, data: Slice):
if self.spx not in data.bars or self.spx_option not in data.bars:
return
if not self.ema_slow.is_ready:
return
if self.ema_fast > self.ema_slow:
self.set_holdings(self.spx_option, 1)
else:
self.liquidate()
def on_end_of_algorithm(self) -> None:
if self.portfolio[self.spx].total_sale_volume > 0:
raise AssertionError("Index is not tradable.")
@@ -0,0 +1,66 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from AlgorithmImports import *
class BasicTemplateIndexDailyAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2021, 1, 1)
self.set_end_date(2021, 1, 18)
self.set_cash(1000000)
# Use indicator for signal; but it cannot be traded
self.spx = self.add_index("SPX", Resolution.DAILY).symbol
# Trade on SPX ITM calls
self.spx_option = Symbol.create_option(
self.spx,
Market.USA,
OptionStyle.EUROPEAN,
OptionRight.CALL,
3200,
datetime(2021, 1, 15)
)
self.add_index_option_contract(self.spx_option, Resolution.DAILY)
self.ema_slow = self.ema(self.spx, 80)
self.ema_fast = self.ema(self.spx, 200)
self.ExpectedBarCount = 10
self.BarCounter = 0
self.settings.daily_precise_end_time = True
def on_data(self, data: Slice):
if not self.portfolio.invested:
# SPX Index is not tradable, but we can trade an option
self.market_order(self.spx_option, 1)
else:
self.liquidate()
# Count how many slices we receive with SPX data in it to assert later
if data.contains_key(self.spx):
self.BarCounter = self.BarCounter + 1
def OnEndOfAlgorithm(self):
if self.BarCounter != self.ExpectedBarCount:
raise ValueError(f"Bar Count {self.BarCounter} is not expected count of {self.ExpectedBarCount}")
for symbol in [ self.spx_option, self.spx ]:
history = self.history(symbol, 10)
if len(history) != 10:
raise ValueError(f"Unexpected history count: {len(history)}")
if any(x for x in history.index.get_level_values('time') if x.time() != time(15, 15, 0)):
raise ValueError(f"Unexpected history data time")
@@ -0,0 +1,59 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from AlgorithmImports import *
class BasicTemplateIndexOptionsAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2021, 1, 4)
self.set_end_date(2021, 2, 1)
self.set_cash(1000000)
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
spx_options = self.add_index_option(self.spx, Resolution.MINUTE)
spx_options.set_filter(lambda x: x.calls_only())
self.ema_slow = self.ema(self.spx, 80)
self.ema_fast = self.ema(self.spx, 200)
def on_data(self, data: Slice) -> None:
if self.spx not in data.bars or not self.ema_slow.is_ready:
return
for chain in data.option_chains.values():
for contract in chain.contracts.values():
if self.portfolio.invested:
continue
if (self.ema_fast > self.ema_slow and contract.right == OptionRight.CALL) or \
(self.ema_fast < self.ema_slow and contract.right == OptionRight.PUT):
self.liquidate(self.invert_option(contract.symbol))
self.market_order(contract.symbol, 1)
def on_end_of_algorithm(self) -> None:
if self.portfolio[self.spx].total_sale_volume > 0:
raise AssertionError("Index is not tradable.")
if self.portfolio.total_sale_volume == 0:
raise AssertionError("Trade volume should be greater than zero by the end of this algorithm")
def invert_option(self, symbol: Symbol) -> Symbol:
return Symbol.create_option(
symbol.underlying,
symbol.id.market,
symbol.id.option_style,
OptionRight.PUT if symbol.id.option_right == OptionRight.CALL else OptionRight.CALL,
symbol.id.strike_price,
symbol.id.date
)
@@ -0,0 +1,50 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic template framework algorithm uses framework components to define the algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateIndiaAlgorithm(QCAlgorithm):
'''Basic template framework algorithm uses framework components to define the algorithm.'''
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_account_currency("INR") #Set Account Currency
self.set_start_date(2019, 1, 23) #Set Start Date
self.set_end_date(2019, 10, 31) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Find more symbols here: http://quantconnect.com/data
self.add_equity("YESBANK", Resolution.MINUTE, Market.INDIA)
self.debug("numpy test >>> print numpy.pi: " + str(np.pi))
# Set Order Properties as per the requirements for order placement
self.default_order_properties = IndiaOrderProperties(Exchange.NSE)
def on_data(self, data):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not self.portfolio.invested:
self.market_order("YESBANK", 1)
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug("Purchased Stock: {0}".format(order_event.symbol))
@@ -0,0 +1,71 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic Template India Index Algorithm uses framework components to define the algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateIndiaIndexAlgorithm(QCAlgorithm):
'''Basic template framework algorithm uses framework components to define the algorithm.'''
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_account_currency("INR") #Set Account Currency
self.set_start_date(2019, 1, 1) #Set Start Date
self.set_end_date(2019, 1, 5) #Set End Date
self.set_cash(1000000) #Set Strategy Cash
# Use indicator for signal; but it cannot be traded
self.nifty = self.add_index("NIFTY50", Resolution.MINUTE, Market.INDIA).symbol
# Trade Index based ETF
self.nifty_etf = self.add_equity("JUNIORBEES", Resolution.MINUTE, Market.INDIA).symbol
# Set Order Properties as per the requirements for order placement
self.default_order_properties = IndiaOrderProperties(Exchange.NSE)
# Define indicator
self._ema_slow = self.ema(self.nifty, 80)
self._ema_fast = self.ema(self.nifty, 200)
self.debug("numpy test >>> print numpy.pi: " + str(np.pi))
def on_data(self, data):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if not data.bars.contains_key(self.nifty) or not data.bars.contains_key(self.nifty_etf):
return
if not self._ema_slow.is_ready:
return
if self._ema_fast > self._ema_slow:
if not self.portfolio.invested:
self.market_ticket = self.market_order(self.nifty_etf, 1)
else:
self.liquidate()
def on_end_of_algorithm(self):
if self.portfolio[self.nifty].total_sale_volume > 0:
raise AssertionError("Index is not tradable.")
@@ -0,0 +1,63 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from QuantConnect.Data.Custom.Intrinio import *
class BasicTemplateIntrinioEconomicData(QCAlgorithm):
def initialize(self):
'''initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2010, 1, 1) #Set Start Date
self.set_end_date(2013, 12, 31) #Set End Date
self.set_cash(100000) #Set Strategy Cash
# Set your Intrinio user and password.
IntrinioConfig.set_user_and_password("intrinio-username", "intrinio-password")
# The Intrinio user and password can be also defined in the config.json file for local backtest.
# Set Intrinio config to make 1 call each minute, default is 1 call each 5 seconds.
#(1 call each minute is the free account limit for historical_data endpoint)
IntrinioConfig.set_time_interval_between_calls(timedelta(minutes = 1))
# United States Oil Fund LP
self.uso = self.add_equity("USO", Resolution.DAILY).symbol
self.securities[self.uso].set_leverage(2)
# United States Brent Oil Fund LP
self.bno = self.add_equity("BNO", Resolution.DAILY).symbol
self.securities[self.bno].set_leverage(2)
self.add_data(IntrinioEconomicData, "$DCOILWTICO", Resolution.DAILY)
self.add_data(IntrinioEconomicData, "$DCOILBRENTEU", Resolution.DAILY)
self.ema_wti = self.ema("$DCOILWTICO", 10)
def on_data(self, slice):
'''on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
if (slice.contains_key("$DCOILBRENTEU") or slice.contains_key("$DCOILWTICO")):
spread = slice["$DCOILBRENTEU"].value - slice["$DCOILWTICO"].value
else:
return
if ((spread > 0 and not self.portfolio[self.bno].is_long) or
(spread < 0 and not self.portfolio[self.uso].is_short)):
sign = math.copysign(1, spread)
self.set_holdings(self.bno, 0.25 * sign)
self.set_holdings(self.uso, -0.25 * sign)
+39
View File
@@ -0,0 +1,39 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Basic Template Library Class
###
### Library classes are snippets of code/classes you can reuse between projects. They are
### added to projects on compile. This can be useful for reusing indicators, math functions,
### risk modules etc. Make sure you import the class in your algorithm. You need
### to name the file the module you'll be importing (not main.cs).
### importing.
### </summary>
class BasicTemplateLibrary:
'''
To use this library place this at the top:
from BasicTemplateLibrary import BasicTemplateLibrary
Then instantiate the function:
x = BasicTemplateLibrary()
x.add(1,2)
'''
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
@@ -0,0 +1,66 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to execute a Call Butterfly option equity strategy
### It adds options for a given underlying equity security, and shows how you can prefilter contracts easily based on strikes and expirations
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
### <meta name="tag" content="trading and orders" />
class BasicTemplateOptionEquityStrategyAlgorithm(QCAlgorithm):
underlying_ticker = "GOOG"
def initialize(self) -> None:
self.set_start_date(2015, 12, 24)
self.set_end_date(2015, 12, 24)
equity = self.add_equity(self.underlying_ticker)
option = self.add_option(self.underlying_ticker)
self._option_symbol = option.symbol
# set our strike/expiry filter for this option chain
option.set_filter(lambda u: (u.standards_only().strikes(-2, +2)
# Expiration method accepts TimeSpan objects or integer for days.
# The following statements yield the same filtering criteria
.expiration(0, 180)))
def on_data(self, slice: Slice) -> None:
if self.portfolio.invested or not self.is_market_open(self._option_symbol):
return
chain = slice.option_chains.get(self._option_symbol)
if not chain:
return
grouped_by_expiry = dict()
for contract in [contract for contract in chain if contract.right == OptionRight.CALL]:
grouped_by_expiry.setdefault(int(contract.expiry.timestamp()), []).append(contract)
first_expiry = list(sorted(grouped_by_expiry))[0]
call_contracts = sorted(grouped_by_expiry[first_expiry], key = lambda x: x.strike)
expiry = call_contracts[0].expiry
lower_strike = call_contracts[0].strike
middle_strike = call_contracts[1].strike
higher_strike = call_contracts[2].strike
option_strategy = OptionStrategies.call_butterfly(self._option_symbol, higher_strike, middle_strike, lower_strike, expiry)
self.order(option_strategy, 10)
def on_order_event(self, order_event: OrderEvent) -> None:
self.log(str(order_event))
@@ -0,0 +1,62 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This algorithm demonstrate how to use Option Strategies (e.g. OptionStrategies.STRADDLE) helper classes to batch send orders for common strategies.
### It also shows how you can prefilter contracts easily based on strikes and expirations, and how you can inspect the
### option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="option strategies" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionStrategyAlgorithm(QCAlgorithm):
def initialize(self):
# Set the cash we'd like to use for our backtest
self.set_cash(1000000)
# Start and end dates for the backtest.
self.set_start_date(2015,12,24)
self.set_end_date(2015,12,24)
# Add assets you'd like to see
option = self.add_option("GOOG")
self.option_symbol = option.symbol
# set our strike/expiry filter for this option chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
option.set_filter(lambda u: (u.standards_only().strikes(-2, +2).expiration(0, 180)))
# use the underlying equity as the benchmark
self.set_benchmark("GOOG")
def on_data(self,slice):
if not self.portfolio.invested:
for kvp in slice.option_chains:
chain = kvp.value
contracts = sorted(sorted(chain, key = lambda x: abs(chain.underlying.price - x.strike)),
key = lambda x: x.expiry, reverse=False)
if len(contracts) == 0: continue
atm_straddle = contracts[0]
if atm_straddle != None:
self.sell(OptionStrategies.straddle(self.option_symbol, atm_straddle.strike, atm_straddle.expiry), 2)
else:
self.liquidate()
def on_order_event(self, order_event):
self.log(str(order_event))
@@ -0,0 +1,60 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add options for a given underlying equity security.
### It also shows how you can prefilter contracts easily based on strikes and expirations.
### It also shows how you can inspect the option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionTradesAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2015, 12, 24)
self.set_end_date(2015, 12, 24)
self.set_cash(100000)
option = self.add_option("GOOG")
# add the initial contract filter
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
option.set_filter(-2, +2, 0, 10)
# option.set_filter(-2, +2, timedelta(0), timedelta(10))
# use the underlying equity as the benchmark
self.set_benchmark("GOOG")
def on_data(self,slice):
if not self.portfolio.invested:
for kvp in slice.option_chains:
chain = kvp.value
# find the second call strike under market price expiring today
contracts = sorted(sorted(chain, key = lambda x: abs(chain.underlying.price - x.strike)),
key = lambda x: x.expiry, reverse=False)
if len(contracts) == 0: continue
if contracts[0] != None:
self.market_order(contracts[0].symbol, 1)
else:
self.liquidate()
for kpv in slice.bars:
self.log("---> OnData: {0}, {1}, {2}".format(self.time, kpv.key.value, str(kpv.value.close)))
def on_order_event(self, order_event):
self.log(str(order_event))
@@ -0,0 +1,66 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add options for a given underlying equity security.
### It also shows how you can prefilter contracts easily based on strikes and expirations, and how you
### can inspect the option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionsAlgorithm(QCAlgorithm):
underlying_ticker = "GOOG"
def initialize(self):
self.set_start_date(2015, 12, 24)
self.set_end_date(2015, 12, 24)
self.set_cash(100000)
equity = self.add_equity(self.underlying_ticker)
option = self.add_option(self.underlying_ticker)
self.option_symbol = option.symbol
# set our strike/expiry filter for this option chain
option.set_filter(lambda u: (u.standards_only().strikes(-2, +2)
# Expiration method accepts TimeSpan objects or integer for days.
# The following statements yield the same filtering criteria
.expiration(0, 180)))
#.expiration(TimeSpan.zero, TimeSpan.from_days(180))))
# use the underlying equity as the benchmark
self.set_benchmark(equity.symbol)
def on_data(self, slice):
if self.portfolio.invested or not self.is_market_open(self.option_symbol): return
chain = slice.option_chains.get(self.option_symbol)
if not chain:
return
# we sort the contracts to find at the money (ATM) contract with farthest expiration
contracts = sorted(sorted(sorted(chain, \
key = lambda x: abs(chain.underlying.price - x.strike)), \
key = lambda x: x.expiry, reverse=True), \
key = lambda x: x.right, reverse=True)
# if found, trade it
if len(contracts) == 0: return
symbol = contracts[0].symbol
self.market_order(symbol, 1)
self.market_on_close_order(symbol, -1)
def on_order_event(self, order_event):
self.log(str(order_event))
@@ -0,0 +1,66 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### A demonstration of consolidating options data into larger bars for your algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="benchmarks" />
### <meta name="tag" content="consolidating data" />
### <meta name="tag" content="options" />
class BasicTemplateOptionsConsolidationAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
self.set_cash(1000000)
# Subscribe and set our filter for the options chain
option = self.add_option('SPY')
# set our strike/expiry filter for this option chain
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
option.set_filter(-2, +2, 0, 180)
# option.set_filter(-2, +2, timedelta(0), timedelta(180))
self.consolidators = dict()
def on_quote_bar_consolidated(self, sender, quote_bar):
self.log("OnQuoteBarConsolidated called on " + str(self.time))
self.log(str(quote_bar))
def on_trade_bar_consolidated(self, sender, trade_bar):
self.log("OnTradeBarConsolidated called on " + str(self.time))
self.log(str(trade_bar))
def on_securities_changed(self, changes):
for security in changes.added_securities:
if security.type == SecurityType.EQUITY:
trade_bar_consolidator = TradeBarConsolidator(timedelta(minutes=5))
trade_bar_consolidator.data_consolidated += self.on_trade_bar_consolidated
self.subscription_manager.add_consolidator(security.symbol, trade_bar_consolidator)
self.consolidators[security.symbol] = trade_bar_consolidator
else:
quote_bar_consolidator = QuoteBarConsolidator(timedelta(minutes=5))
quote_bar_consolidator.data_consolidated += self.on_quote_bar_consolidated
self.subscription_manager.add_consolidator(security.symbol, quote_bar_consolidator)
self.consolidators[security.symbol] = quote_bar_consolidator
for security in changes.removed_securities:
consolidator = self.consolidators.pop(security.symbol)
self.subscription_manager.remove_consolidator(security.symbol, consolidator)
if security.type == SecurityType.EQUITY:
consolidator.data_consolidated -= self.on_trade_bar_consolidated
else:
consolidator.data_consolidated -= self.on_quote_bar_consolidated
@@ -0,0 +1,73 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add options for a given underlying equity security.
### It also shows how you can prefilter contracts easily based on strikes and expirations, and how you
### can inspect the option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionsDailyAlgorithm(QCAlgorithm):
underlying_ticker = "AAPL"
def initialize(self):
self.set_start_date(2015, 12, 15)
self.set_end_date(2016, 2, 1)
self.set_cash(100000)
self.option_expired = False
equity = self.add_equity(self.underlying_ticker, Resolution.DAILY)
option = self.add_option(self.underlying_ticker, Resolution.DAILY)
self.option_symbol = option.symbol
# set our strike/expiry filter for this option chain
option.set_filter(lambda u: (u.calls_only().expiration(0, 60)))
# use the underlying equity as the benchmark
self.set_benchmark(equity.symbol)
def on_data(self,slice):
if self.portfolio.invested: return
chain = slice.option_chains.get(self.option_symbol)
if not chain:
return
# Grab us the contract nearest expiry
contracts = sorted(chain, key = lambda x: x.expiry)
# if found, trade it
if len(contracts) == 0: return
symbol = contracts[0].symbol
self.market_order(symbol, 1)
def on_order_event(self, order_event):
self.log(str(order_event))
# Check for our expected OTM option expiry
if "OTM" in order_event.message:
# Assert it is at midnight 1/16 (5AM UTC)
if order_event.utc_time.month != 1 and order_event.utc_time.day != 16 and order_event.utc_time.hour != 5:
raise AssertionError(f"Expiry event was not at the correct time, {order_event.utc_time}")
self.option_expired = True
def on_end_of_algorithm(self):
# Assert we had our option expire and fill a liquidation order
if not self.option_expired:
raise AssertionError("Algorithm did not process the option expiration like expected")
@@ -0,0 +1,63 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add options for a given underlying equity security.
### It also shows how you can prefilter contracts easily based on strikes and expirations.
### It also shows how you can inspect the option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionsFilterUniverseAlgorithm(QCAlgorithm):
underlying_ticker = "GOOG"
def initialize(self):
self.set_start_date(2015, 12, 24)
self.set_end_date(2015, 12, 28)
self.set_cash(100000)
equity = self.add_equity(self.underlying_ticker)
option = self.add_option(self.underlying_ticker)
self.option_symbol = option.symbol
# Set our custom universe filter
option.set_filter(self.filter_function)
# use the underlying equity as the benchmark
self.set_benchmark(equity.symbol)
def filter_function(self, universe):
#Expires today, is a call, and is within 10 dollars of the current price
universe = universe.weeklys_only().expiration(0, 1)
return [symbol for symbol in universe
if symbol.id.option_right != OptionRight.PUT
and -10 < universe.underlying.price - symbol.id.strike_price < 10]
def on_data(self, slice):
if self.portfolio.invested: return
for kvp in slice.option_chains:
if kvp.key != self.option_symbol: continue
# Get the first call strike under market price expiring today
chain = kvp.value
contracts = [option for option in sorted(chain, key = lambda x:x.strike, reverse = True)
if option.expiry.date() == self.time.date()
and option.strike < chain.underlying.price]
if contracts:
self.market_order(contracts[0].symbol, 1)
@@ -0,0 +1,83 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from Alphas.ConstantAlphaModel import ConstantAlphaModel
from Selection.OptionUniverseSelectionModel import OptionUniverseSelectionModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Risk.NullRiskManagementModel import NullRiskManagementModel
### <summary>
### Basic template options framework algorithm uses framework components
### to define an algorithm that trades options.
### </summary>
class BasicTemplateOptionsFrameworkAlgorithm(QCAlgorithm):
def initialize(self):
self.universe_settings.resolution = Resolution.MINUTE
self.set_start_date(2014, 6, 5)
self.set_end_date(2014, 6, 9)
self.set_cash(100000)
# set framework models
self.set_universe_selection(EarliestExpiringWeeklyAtTheMoneyPutOptionUniverseSelectionModel(self.select_option_chain_symbols))
self.set_alpha(ConstantOptionContractAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(hours = 0.5)))
self.set_portfolio_construction(SingleSharePortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
def select_option_chain_symbols(self, utc_time):
new_york_time = Extensions.convert_from_utc(utc_time, TimeZones.NEW_YORK)
ticker = "TWX" if new_york_time.date() < date(2014, 6, 6) else "AAPL"
return [ Symbol.create(ticker, SecurityType.OPTION, Market.USA, f"?{ticker}") ]
class EarliestExpiringWeeklyAtTheMoneyPutOptionUniverseSelectionModel(OptionUniverseSelectionModel):
'''Creates option chain universes that select only the earliest expiry ATM weekly put contract
and runs a user defined option_chain_symbol_selector every day to enable choosing different option chains'''
def __init__(self, select_option_chain_symbols):
super().__init__(timedelta(1), select_option_chain_symbols)
def filter(self, filter):
'''Defines the option chain universe filter'''
return (filter.strikes(+1, +1)
# Expiration method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
.expiration(0, 7)
# .expiration(timedelta(0), timedelta(7))
.weeklys_only()
.puts_only()
.only_apply_filter_at_market_open())
class ConstantOptionContractAlphaModel(ConstantAlphaModel):
'''Implementation of a constant alpha model that only emits insights for option symbols'''
def __init__(self, type, direction, period):
super().__init__(type, direction, period)
def should_emit_insight(self, utc_time, symbol):
# only emit alpha for option symbols and not underlying equity symbols
if symbol.security_type != SecurityType.OPTION:
return False
return super().should_emit_insight(utc_time, symbol)
class SingleSharePortfolioConstructionModel(PortfolioConstructionModel):
'''Portfolio construction model that sets target quantities to 1 for up insights and -1 for down insights'''
def create_targets(self, algorithm, insights):
targets = []
for insight in insights:
targets.append(PortfolioTarget(insight.symbol, insight.direction))
return targets
@@ -0,0 +1,77 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Example demonstrating how to access to options history for a given underlying equity security.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
### <meta name="tag" content="history" />
class BasicTemplateOptionsHistoryAlgorithm(QCAlgorithm):
''' This example demonstrates how to get access to options history for a given underlying equity security.'''
def initialize(self):
# this test opens position in the first day of trading, lives through stock split (7 for 1), and closes adjusted position on the second day
self.set_start_date(2015, 12, 24)
self.set_end_date(2015, 12, 24)
self.set_cash(1000000)
option = self.add_option("GOOG")
# add the initial contract filter
# SetFilter method accepts timedelta objects or integer for days.
# The following statements yield the same filtering criteria
option.set_filter(-2, +2, 0, 180)
# option.set_filter(-2,2, timedelta(0), timedelta(180))
# set the pricing model for Greeks and volatility
# find more pricing models https://www.quantconnect.com/lean/documentation/topic27704.html
option.price_model = OptionPriceModels.black_scholes()
# set the warm-up period for the pricing model
self.set_warm_up(TimeSpan.from_days(4))
# set the benchmark to be the initial cash
self.set_benchmark(lambda x: 1000000)
def on_data(self,slice):
if self.is_warming_up: return
if not self.portfolio.invested:
for chain in slice.option_chains:
volatility = self.securities[chain.key.underlying].volatility_model.volatility
for contract in chain.value:
self.log("{0},Bid={1} Ask={2} Last={3} OI={4} sigma={5:.3f} NPV={6:.3f} \
delta={7:.3f} gamma={8:.3f} vega={9:.3f} beta={10:.2f} theta={11:.2f} IV={12:.2f}".format(
contract.symbol.value,
contract.bid_price,
contract.ask_price,
contract.last_price,
contract.open_interest,
volatility,
contract.theoretical_price,
contract.greeks.delta,
contract.greeks.gamma,
contract.greeks.vega,
contract.greeks.rho,
contract.greeks.theta / 365,
contract.implied_volatility))
def on_securities_changed(self, changes):
for change in changes.added_securities:
# only print options price
if change.symbol.value == "GOOG": return
history = self.history(change.symbol, 10, Resolution.MINUTE).sort_index(level='time', ascending=False)[:3]
for index, row in history.iterrows():
self.log("History: " + str(index[3])
+ ": " + index[4].strftime("%m/%d/%Y %I:%M:%S %p")
+ " > " + str(row.close))
@@ -0,0 +1,66 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add options for a given underlying equity security.
### It also shows how you can prefilter contracts easily based on strikes and expirations, and how you
### can inspect the option chain to pick a specific option contract to trade.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
class BasicTemplateOptionsHourlyAlgorithm(QCAlgorithm):
underlying_ticker = "AAPL"
def initialize(self):
self.set_start_date(2014, 6, 6)
self.set_end_date(2014, 6, 9)
self.set_cash(100000)
equity = self.add_equity(self.underlying_ticker, Resolution.HOUR)
option = self.add_option(self.underlying_ticker, Resolution.HOUR)
self.option_symbol = option.symbol
# set our strike/expiry filter for this option chain
option.set_filter(lambda u: (u.standards_only().strikes(-2, +2)
# Expiration method accepts TimeSpan objects or integer for days.
# The following statements yield the same filtering criteria
.expiration(0, 180)))
#.expiration(TimeSpan.zero, TimeSpan.from_days(180))))
# use the underlying equity as the benchmark
self.set_benchmark(equity.symbol)
def on_data(self,slice):
if self.portfolio.invested or not self.is_market_open(self.option_symbol): return
chain = slice.option_chains.get(self.option_symbol)
if not chain:
return
# we sort the contracts to find at the money (ATM) contract with farthest expiration
contracts = sorted(sorted(sorted(chain, \
key = lambda x: abs(chain.underlying.price - x.strike)), \
key = lambda x: x.expiry, reverse=True), \
key = lambda x: x.right, reverse=True)
# if found, trade it
if len(contracts) == 0 or not self.is_market_open(contracts[0].symbol): return
symbol = contracts[0].symbol
self.market_order(symbol, 1)
self.market_on_close_order(symbol, -1)
def on_order_event(self, order_event):
self.log(str(order_event))
@@ -0,0 +1,64 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Example demonstrating how to define an option price model.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="filter selection" />
### <meta name="tag" content="option price model" />
class BasicTemplateOptionsPriceModel(QCAlgorithm):
'''Example demonstrating how to define an option price model.'''
def initialize(self):
self.set_start_date(2020, 1, 1)
self.set_end_date(2020, 1, 5)
self.set_cash(100000)
# Add the option
option = self.add_option("AAPL")
self.option_symbol = option.symbol
# Add the initial contract filter
option.set_filter(-3, +3, 0, 31)
# Define the Option Price Model
option.price_model = OptionPriceModels.QuantLib.crank_nicolson_fd()
#option.price_model = OptionPriceModels.QuantLib.black_scholes()
#option.price_model = OptionPriceModels.QuantLib.additive_equiprobabilities()
#option.price_model = OptionPriceModels.QuantLib.barone_adesi_whaley()
#option.price_model = OptionPriceModels.QuantLib.binomial_cox_ross_rubinstein()
#option.price_model = OptionPriceModels.QuantLib.binomial_jarrow_rudd()
#option.price_model = OptionPriceModels.QuantLib.binomial_joshi()
#option.price_model = OptionPriceModels.QuantLib.binomial_leisen_reimer()
#option.price_model = OptionPriceModels.QuantLib.binomial_tian()
#option.price_model = OptionPriceModels.QuantLib.binomial_trigeorgis()
#option.price_model = OptionPriceModels.QuantLib.bjerksund_stensland()
#option.price_model = OptionPriceModels.QuantLib.integral()
# Set warm up with 30 trading days to warm up the underlying volatility model
self.set_warm_up(30, Resolution.DAILY)
def on_data(self,slice):
'''OnData will test whether the option contracts has a non-zero Greeks.delta'''
if self.is_warming_up or not slice.option_chains.contains_key(self.option_symbol):
return
chain = slice.option_chains[self.option_symbol]
if not any([x for x in chain if x.greeks.delta != 0]):
self.log(f'No contract with Delta != 0')
@@ -0,0 +1,61 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### This example demonstrates how to add and trade SPX index weekly options
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="options" />
### <meta name="tag" content="indexes" />
class BasicTemplateSPXWeeklyIndexOptionsAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2021, 1, 4)
self.set_end_date(2021, 1, 10)
self.set_cash(1000000)
# regular option SPX contracts
self.spx_options = self.add_index_option("SPX")
self.spx_options.set_filter(lambda u: (u.strikes(0, 1).expiration(0, 30)))
# weekly option SPX contracts
spxw = self.add_index_option("SPX", "SPXW")
# set our strike/expiry filter for this option chain
spxw.set_filter(lambda u: (u.strikes(0, 1)
# single week ahead since there are many SPXW contracts and we want to preserve performance
.expiration(0, 7)
.include_weeklys()))
self.spxw_option = spxw.symbol
def on_data(self,slice):
if self.portfolio.invested: return
chain = slice.option_chains.get(self.spxw_option)
if not chain:
return
# we sort the contracts to find at the money (ATM) contract with closest expiration
contracts = sorted(sorted(sorted(chain, \
key = lambda x: x.expiry), \
key = lambda x: abs(chain.underlying.price - x.strike)), \
key = lambda x: x.right, reverse=True)
# if found, buy until it expires
if len(contracts) == 0: return
symbol = contracts[0].symbol
self.market_order(symbol, 1)
def on_order_event(self, order_event):
self.debug(str(order_event))
@@ -0,0 +1,30 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License
from AlgorithmImports import *
from BasicTemplateIndexAlgorithm import BasicTemplateIndexAlgorithm
class BasicTemplateTradableIndexAlgorithm(BasicTemplateIndexAlgorithm):
ticket: OrderTicket | None = None
def initialize(self) -> None:
super().initialize()
self.securities[self.spx].is_tradable = True
def on_data(self, data: Slice):
super().on_data(data)
if not self.ticket:
self.ticket = self.market_order(self.spx, 1)
def on_end_of_algorithm(self) -> None:
if self.ticket and self.ticket.status != OrderStatus.FILLED:
raise AssertionError("Index is tradable.")
@@ -0,0 +1,33 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Benchmark Algorithm: The minimalist basic template algorithm benchmark strategy.
### </summary>
### <remarks>
### All new projects in the cloud are created with the basic template algorithm. It uses a minute algorithm
### </remarks>
class BasicTemplateBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2000, 1, 1)
self.set_end_date(2022, 1, 1)
self.set_benchmark(lambda x: 1)
self.add_equity("SPY")
def on_data(self, data):
if not self.portfolio.invested:
self.set_holdings("SPY", 1)
self.debug("Purchased Stock")
@@ -0,0 +1,63 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class CoarseFineUniverseSelectionBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2017, 11, 1)
self.set_end_date(2018, 3, 1)
self.set_cash(50000)
self.universe_settings.resolution = Resolution.MINUTE
self.add_universe(self.coarse_selection_function, self.fine_selection_function)
self.number_of_symbols = 150
self.number_of_symbols_fine = 40
self._changes = None
# sort the data by daily dollar volume and take the top 'NumberOfSymbols'
def coarse_selection_function(self, coarse):
selected = [x for x in coarse if (x.has_fundamental_data)]
# sort descending by daily dollar volume
sorted_by_dollar_volume = sorted(selected, key=lambda x: x.dollar_volume, reverse=True)
# return the symbol objects of the top entries from our sorted collection
return [ x.symbol for x in sorted_by_dollar_volume[:self.number_of_symbols] ]
# sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
def fine_selection_function(self, fine):
# sort descending by P/E ratio
sorted_by_pe_ratio = sorted(fine, key=lambda x: x.valuation_ratios.pe_ratio, reverse=True)
# take the top entries from our sorted collection
return [ x.symbol for x in sorted_by_pe_ratio[:self.number_of_symbols_fine] ]
def on_data(self, data):
# if we have no changes, do nothing
if self._changes is None: return
# liquidate removed securities
for security in self._changes.removed_securities:
if security.invested:
self.liquidate(security.symbol)
for security in self._changes.added_securities:
self.set_holdings(security.symbol, 0.02)
self._changes = None
def on_securities_changed(self, changes):
self._changes = changes
@@ -0,0 +1,70 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Benchmark Algorithm: Loading and synchronization of 500 equity minute symbols and their options.
### </summary>
class EmptyEquityAndOptions400Benchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2022, 5, 11)
self.set_end_date(2022, 5, 12)
self.equity_symbols = [
"MARK", "TSN", "DT", "RDW", "CVE", "NXPI", "FIVN", "CLX", "SPXL", "BKSY", "NUGT", "CF", "NEGG",
"RH", "SIRI", "ITUB", "CSX", "AUR", "LIDR", "CMPS", "DHI", "GLW", "NTES", "CIFR", "S", "HSBC",
"HIPO", "WTRH", "AMRN", "BIIB", "RIO", "EDIT", "TEAM", "CNK", "BUD", "MILE", "AEHR", "DOCN",
"CLSK", "BROS", "MLCO", "SBLK", "ICLN", "OPK", "CNC", "SKX", "SESN", "VRM", "ASML", "BBAI",
"HON", "MRIN", "BLMN", "NTNX", "POWW", "FOUR", "HOG", "GOGO", "MGNI", "GENI", "XPDI",
"DG", "PSX", "RRC", "CORT", "MET", "UMC", "INMD", "RBAC", "ISRG", "BOX", "DVAX", "CRVS", "HLT",
"BKNG", "BENE", "CLVS", "ESSC", "PTRA", "BE", "FPAC", "YETI", "DOCS", "DB", "EBON", "RDS.B",
"ERIC", "BSIG", "INTU", "MNTS", "BCTX", "BLU", "FIS", "MAC", "WMB", "TTWO", "ARDX", "SWBI",
"ELY", "INDA", "REAL", "ACI", "APRN", "BHP", "CPB", "SLQT", "ARKF", "TSP", "OKE", "NVTA", "META",
"CSTM", "KMX", "IBB", "AGEN", "WOOF", "MJ", "HYZN", "RSI", "JCI", "EXC", "HPE", "SI", "WPM",
"PRTY", "BBD", "FVRR", "CANO", "INDI", "MDLZ", "KOLD", "AMBA", "SOXS", "RSX", "ZEN", "PUBM",
"VLDR", "CI", "ISEE", "GEO", "BKR", "DHR", "GRPN", "NRXP", "ACN", "MAT", "BODY", "ENDP",
"SHPW", "AVIR", "GPN", "BILL", "BZ", "CERN", "ARVL", "DNMR", "NTR", "FSM", "BMBL", "PAAS",
"INVZ", "ANF", "CL", "XP", "CS", "KD", "WW", "AHT", "GRTX", "XLC", "BLDP", "HTA", "APT", "BYSI",
"ENB", "TRIT", "VTNR", "AVCT", "SLI", "CP", "CAH", "ALLY", "FIGS", "PXD", "TPX", "ZI", "BKLN", "SKIN",
"LNG", "NU", "CX", "GSM", "NXE", "REI", "MNDT", "IP", "BLOK", "IAA", "TIP", "MCHP", "EVTL", "BIGC",
"IGV", "LOTZ", "EWC", "DRI", "PSTG", "APLS", "KIND", "BBIO", "APPH", "FIVE", "LSPD", "SHAK",
"COMM", "NAT", "VFC", "AMT", "VRTX", "RGS", "DD", "GBIL", "LICY", "ACHR", "FLR", "HGEN", "TECL",
"SEAC", "NVS", "NTAP", "ML", "SBSW", "XRX", "UA", "NNOX", "SFT", "FE", "APP", "KEY", "CDEV",
"DPZ", "BARK", "SPR", "CNQ", "XL", "AXSM", "ECH", "RNG", "AMLP", "ENG", "BTI", "REKR",
"STZ", "BK", "HEAR", "LEV", "SKT", "HBI", "ALB", "CAG", "MNKD", "NMM", "BIRD", "CIEN", "SILJ",
"STNG", "GUSH", "GIS", "PRPL", "SDOW", "GNRC", "ERX", "GES", "CPE", "FBRX", "WM", "ESTC",
"GOED", "STLD", "LILM", "JNK", "BOIL", "ALZN", "IRBT", "KOPN", "AU", "TPR", "RWLK", "TROX",
"TMO", "AVDL", "XSPA", "JKS", "PACB", "LOGI", "BLK", "REGN", "CFVI", "EGHT", "ATNF", "PRU",
"URBN", "KMB", "SIX", "CME", "ENVX", "NVTS", "CELH", "CSIQ", "GSL", "PAA", "WU", "MOMO",
"TOL", "WEN", "GTE", "EXAS", "GDRX", "PVH", "BFLY", "SRTY", "UDOW", "NCR", "ALTO", "CRTD",
"GOCO", "ALK", "TTM", "DFS", "VFF", "ANTM", "FREY", "WY", "ACWI", "PNC", "SYY", "SNY", "CRK",
"SO", "XXII", "PBF", "AER", "RKLY", "SOL", "CND", "MPLX", "JNPR", "FTCV", "CLR", "XHB", "YY",
"POSH", "HIMS", "LIFE", "XENE", "ADM", "ROST", "MIR", "NRG", "AAP", "SSYS", "KBH", "KKR", "PLAN",
"DUK", "WIMI", "DBRG", "WSM", "LTHM", "OVV", "CFLT", "EWT", "UNFI", "TX", "EMR", "IMGN", "K",
"ONON", "UNIT", "LEVI", "ADTX", "UPWK", "DBA", "VOO", "FATH", "URI", "MPW", "JNUG", "RDFN",
"OSCR", "WOLF", "SYF", "GOGL", "HES", "PHM", "CWEB", "ALDX", "BTWN", "AFL", "PPL", "CIM"
]
self.set_warm_up(TimeSpan.from_days(1))
for ticker in self.equity_symbols:
option = self.add_option(ticker)
option.set_filter(1, 7, timedelta(0), timedelta(90))
self.add_equity("SPY")
def on_data(self, slice):
if self.is_warming_up: return
self.quit("The end!")
@@ -0,0 +1,383 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class EmptyMinute400EquityBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2015, 9, 1)
self.set_end_date(2015, 12, 1)
for symbol in Symbols().equity.all()[:400]:
self.add_security(SecurityType.EQUITY, symbol)
def on_data(self, data):
pass
class Symbols(object):
def __init__(self):
self.equity = self.Equity()
class Equity(object):
def all(self):
return [
"SPY",
"AAPL",
"FB",
"VXX",
"VRX",
"NFLX",
"UVXY",
"QQQ",
"IWM",
"BABA",
"GILD",
"XIV",
"XOM",
"CVX",
"MSFT",
"GE",
"SLB",
"JPM",
"XLE",
"DIS",
"AMZN",
"TWTR",
"PFE",
"C",
"BAC",
"ABBV",
"JNJ",
"HAL",
"XLV",
"INTC",
"WFC",
"V",
"YHOO",
"COP",
"MYL",
"AGN",
"WMT",
"KMI",
"MRK",
"TSLA",
"GDX",
"LLY",
"FCX",
"CAT",
"CELG",
"QCOM",
"MCD",
"CMCSA",
"XOP",
"CVS",
"AMGN",
"DOW",
"AAL",
"APC",
"SUNE",
"MU",
"VLO",
"SBUX",
"WMB",
"PG",
"EOG",
"DVN",
"BMY",
"APA",
"UNH",
"EEM",
"IBM",
"NKE",
"T",
"HD",
"UNP",
"DAL",
"ENDP",
"CSCO",
"OXY",
"MRO",
"MDT",
"TXN",
"WLL",
"ORCL",
"GOOGL",
"UAL",
"WYNN",
"MS",
"HZNP",
"BIIB",
"VZ",
"GM",
"NBL",
"TWX",
"SWKS",
"JD",
"HCA",
"AVGO",
"YUM",
"KO",
"GOOG",
"GS",
"PEP",
"AIG",
"EMC",
"BIDU",
"CLR",
"PYPL",
"LVS",
"SWN",
"AXP",
"ATVI",
"RRC",
"WBA",
"MPC",
"NXPI",
"ETE",
"NOV",
"FOXA",
"SNDK",
"DIA",
"UTX",
"DD",
"WDC",
"AA",
"M",
"FXI",
"RIG",
"MA",
"DUST",
"TGT",
"AET",
"EBAY",
"LUV",
"EFA",
"BRK.B",
"BA",
"MET",
"LYB",
"SVXY",
"UWTI",
"HON",
"HPQ",
"OAS",
"ABT",
"MO",
"ESRX",
"TEVA",
"STX",
"IBB",
"F",
"CBS",
"TLT",
"PM",
"ESV",
"NE",
"PSX",
"SCHW",
"MON",
"HES",
"GPRO",
"TVIX",
"MNK",
"NVDA",
"NFX",
"USO",
"NUGT",
"EWZ",
"LOW",
"UA",
"TNA",
"XLY",
"MMM",
"PXD",
"VIAB",
"MDLZ",
"NEM",
"USB",
"MUR",
"ETN",
"FEYE",
"PTEN",
"OIH",
"UPS",
"CHK",
"DHR",
"RAI",
"TQQQ",
"CCL",
"BRCM",
"DG",
"JBLU",
"CRM",
"ADBE",
"COG",
"PBR",
"HP",
"BHI",
"BK",
"TJX",
"DE",
"COF",
"INCY",
"DHI",
"ABC",
"XLI",
"ZTS",
"BP",
"IYR",
"PNC",
"CNX",
"XLF",
"LRCX",
"GG",
"RDS.A",
"WFM",
"TSO",
"ANTM",
"KSS",
"EA",
"PRU",
"RAD",
"WFT",
"XBI",
"THC",
"VWO",
"CTSH",
"ABX",
"VMW",
"CSX",
"ACN",
"EMR",
"SE",
"MJN",
"SKX",
"ACE",
"P",
"CMI",
"CL",
"CAH",
"EXC",
"DUK",
"AMAT",
"AEM",
"FTI",
"STT",
"ILMN",
"HOG",
"KR",
"EXPE",
"VRTX",
"IVV",
"CAM",
"GPS",
"MCK",
"ADSK",
"CMCSK",
"HTZ",
"MGM",
"DLTR",
"STI",
"CYH",
"MOS",
"CNQ",
"GLW",
"KEY",
"KORS",
"SIRI",
"EPD",
"SU",
"DFS",
"TMO",
"TAP",
"HST",
"NBR",
"EQT",
"XLU",
"BSX",
"COST",
"CTRP",
"HFC",
"VNQ",
"TRV",
"POT",
"CERN",
"LLTC",
"DO",
"ADI",
"BAX",
"AMT",
"URI",
"UCO",
"ECA",
"MAS",
"ALL",
"PCAR",
"VIPS",
"ATW",
"SPXU",
"LNKD",
"X",
"TSM",
"SO",
"BBT",
"SYF",
"VFC",
"CXO",
"IR",
"PWR",
"GLD",
"LNG",
"ETP",
"JNPR",
"MAT",
"KLAC",
"XLK",
"TRIP",
"AEP",
"VTR",
"ROST",
"RDC",
"CF",
"FAS",
"HCN",
"AR",
"SM",
"WPX",
"D",
"HOT",
"PRGO",
"ALXN",
"CNC",
"VALE",
"JCP",
"GDXJ",
"OKE",
"ADM",
"JOY",
"TSN",
"MAR",
"KHC",
"NSC",
"CMA",
"COH",
"GMCR",
"FL",
"FITB",
"BHP",
"JWN",
"DNR",
"PBF",
"XLNX"]
@@ -0,0 +1,23 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class EmptySPXOptionChainBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_end_date(2020, 6, 1)
self._index = self.add_index("SPX")
option = self.add_option(self._index)
option.set_filter(lambda u: u.include_weeklys().strikes(-30, 30).expiration(0, 7))
@@ -0,0 +1,31 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Benchmark Algorithm: Pure processing of 1 equity second resolution with the same benchmark.
### </summary>
### <remarks>
### This should eliminate the synchronization part of LEAN and focus on measuring the performance of a single datafeed and event handling system.
### </remarks>
class EmptySingleSecuritySecondEquityBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2008, 1, 1)
self.set_end_date(2008, 6, 1)
self.set_benchmark(lambda x: 1)
self.add_equity("SPY", Resolution.SECOND)
def on_data(self, data):
pass
@@ -0,0 +1,34 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class HistoryRequestBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2010, 1, 1)
self.set_end_date(2018, 1, 1)
self.set_cash(10000)
self._symbol = self.add_equity("SPY").symbol
def on_end_of_day(self, symbol):
minute_history = self.history([self._symbol], 60, Resolution.MINUTE)
last_hour_high = 0
for index, row in minute_history.loc["SPY"].iterrows():
if last_hour_high < row["high"]:
last_hour_high = row["high"]
daily_history = self.history([self._symbol], 1, Resolution.DAILY).loc["SPY"].head()
daily_history_high = daily_history["high"]
daily_history_low = daily_history["low"]
daily_history_open = daily_history["open"]
@@ -0,0 +1,43 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class IndicatorRibbonBenchmark(QCAlgorithm):
# Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
def initialize(self):
self.set_start_date(2010, 1, 1) #Set Start Date
self.set_end_date(2018, 1, 1) #Set End Date
self._spy = self.add_equity("SPY", Resolution.MINUTE).symbol
count = 50
offset = 5
period = 15
self._ribbon = []
# define our sma as the base of the ribbon
self._sma = SimpleMovingAverage(period)
for x in range(count):
# define our offset to the zero sma, these various offsets will create our 'displaced' ribbon
delay = Delay(offset*(x+1))
# define an indicator that takes the output of the sma and pipes it into our delay indicator
delayed_sma = IndicatorExtensions.of(delay, self._sma)
# register our new 'delayed_sma' for automatic updates on a daily resolution
self.register_indicator(self._spy, delayed_sma, Resolution.DAILY)
self._ribbon.append(delayed_sma)
def on_data(self, data):
# wait for our entire ribbon to be ready
if not all(x.is_ready for x in self._ribbon): return
for x in self._ribbon:
value = x.current.value
@@ -0,0 +1,33 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class ScheduledEventsBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(2011, 1, 1)
self.set_end_date(2022, 1, 1)
self.set_cash(100000)
self.add_equity("SPY")
for i in range(300):
self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.after_market_open("SPY", i), self.rebalance)
self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.before_market_close("SPY", i), self.rebalance)
def on_data(self, data):
pass
def rebalance(self):
pass
@@ -0,0 +1,57 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class StatefulCoarseUniverseSelectionBenchmark(QCAlgorithm):
def initialize(self):
self.universe_settings.resolution = Resolution.DAILY
self.set_start_date(2017, 1, 1)
self.set_end_date(2019, 1, 1)
self.set_cash(50000)
self.add_universe(self.coarse_selection_function)
self.number_of_symbols = 250
self._black_list = []
# sort the data by daily dollar volume and take the top 'NumberOfSymbols'
def coarse_selection_function(self, coarse):
selected = [x for x in coarse if (x.has_fundamental_data)]
# sort descending by daily dollar volume
sorted_by_dollar_volume = sorted(selected, key=lambda x: x.dollar_volume, reverse=True)
# return the symbol objects of the top entries from our sorted collection
return [ x.symbol for x in sorted_by_dollar_volume[:self.number_of_symbols] if not (x.symbol in self._black_list) ]
def on_data(self, slice):
if slice.has_data:
symbol = slice.keys()[0]
if symbol:
if len(self._black_list) > 50:
self._black_list.pop(0)
self._black_list.append(symbol)
def on_securities_changed(self, changes):
# if we have no changes, do nothing
if changes is None: return
# liquidate removed securities
for security in changes.removed_securities:
if security.invested:
self.liquidate(security.symbol)
for security in changes.added_securities:
self.set_holdings(security.symbol, 0.001)
@@ -0,0 +1,48 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class StatelessCoarseUniverseSelectionBenchmark(QCAlgorithm):
def initialize(self):
self.universe_settings.resolution = Resolution.DAILY
self.set_start_date(2017, 1, 1)
self.set_end_date(2019, 1, 1)
self.set_cash(50000)
self.add_universe(self.coarse_selection_function)
self.number_of_symbols = 250
# sort the data by daily dollar volume and take the top 'NumberOfSymbols'
def coarse_selection_function(self, coarse):
selected = [x for x in coarse if (x.has_fundamental_data)]
# sort descending by daily dollar volume
sorted_by_dollar_volume = sorted(selected, key=lambda x: x.dollar_volume, reverse=True)
# return the symbol objects of the top entries from our sorted collection
return [ x.symbol for x in sorted_by_dollar_volume[:self.number_of_symbols] ]
def on_securities_changed(self, changes):
# if we have no changes, do nothing
if changes is None: return
# liquidate removed securities
for security in changes.removed_securities:
if security.invested:
self.liquidate(security.symbol)
for security in changes.added_securities:
self.set_holdings(security.symbol, 0.001)
@@ -0,0 +1,64 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
from Alphas.HistoricalReturnsAlphaModel import HistoricalReturnsAlphaModel
from Portfolio.BlackLittermanOptimizationPortfolioConstructionModel import *
from Portfolio.UnconstrainedMeanVariancePortfolioOptimizer import UnconstrainedMeanVariancePortfolioOptimizer
from Risk.NullRiskManagementModel import NullRiskManagementModel
### <summary>
### Black-Litterman framework algorithm
### Uses the HistoricalReturnsAlphaModel and the BlackLittermanPortfolioConstructionModel
### to create an algorithm that rebalances the portfolio according to Black-Litterman portfolio optimization
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class BlackLittermanPortfolioOptimizationFrameworkAlgorithm(QCAlgorithm):
'''Black-Litterman Optimization algorithm.'''
def initialize(self):
# Set requested data resolution
self.universe_settings.resolution = Resolution.MINUTE
# Order margin value has to have a minimum of 0.5% of Portfolio value, allows filtering out small trades and reduce fees.
# Commented so regression algorithm is more sensitive
#self.settings.minimum_order_margin_portfolio_percentage = 0.005
self.set_start_date(2013,10,7) #Set Start Date
self.set_end_date(2013,10,11) #Set End Date
self.set_cash(100000) #Set Strategy Cash
self._symbols = [ Symbol.create(x, SecurityType.EQUITY, Market.USA) for x in [ 'AIG', 'BAC', 'IBM', 'SPY' ] ]
optimizer = UnconstrainedMeanVariancePortfolioOptimizer()
# set algorithm framework models
self.set_universe_selection(CoarseFundamentalUniverseSelectionModel(self.coarse_selector))
self.set_alpha(HistoricalReturnsAlphaModel(resolution = Resolution.DAILY))
self.set_portfolio_construction(BlackLittermanOptimizationPortfolioConstructionModel(optimizer = optimizer))
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
def coarse_selector(self, coarse):
# Drops SPY after the 8th
last = 3 if self.time.day > 8 else len(self._symbols)
return self._symbols[0:last]
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug(order_event)
@@ -0,0 +1,56 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Example algorithm to demostrate the event handlers of Brokerage activities
### </summary>
### <meta name="tag" content="using quantconnect" />
class BrokerageActivityEventHandlingAlgorithm(QCAlgorithm):
### <summary>
### Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
### </summary>
def initialize(self):
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
self.set_cash(100000)
self.add_equity("SPY", Resolution.MINUTE)
### <summary>
### on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
### </summary>
### <param name="data">Slice object keyed by symbol containing the stock data</param>
def on_data(self, data):
if not self.portfolio.invested:
self.set_holdings("SPY", 1)
### <summary>
### Brokerage message event handler. This method is called for all types of brokerage messages.
### </summary>
def on_brokerage_message(self, message_event):
self.debug(f"Brokerage meesage received - {message_event.to_string()}")
### <summary>
### Brokerage disconnected event handler. This method is called when the brokerage connection is lost.
### </summary>
def on_brokerage_disconnect(self):
self.debug(f"Brokerage disconnected!")
### <summary>
### Brokerage reconnected event handler. This method is called when the brokerage connection is restored after a disconnection.
### </summary>
def on_brokerage_reconnect(self):
self.debug(f"Brokerage reconnected!")
@@ -0,0 +1,72 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Demonstrate the usage of the BrokerageModel property to help improve backtesting
### accuracy through simulation of a specific brokerage's rules around restrictions
### on submitting orders as well as fee structure.
### </summary>
### <meta name="tag" content="trading and orders" />
### <meta name="tag" content="brokerage models" />
class BrokerageModelAlgorithm(QCAlgorithm):
def initialize(self):
self.set_cash(100000) # Set Strategy Cash
self.set_start_date(2013,10,7) # Set Start Date
self.set_end_date(2013,10,11) # Set End Date
self.add_equity("SPY", Resolution.SECOND)
# there's two ways to set your brokerage model. The easiest would be to call
# self.set_brokerage_model( BrokerageName ) # BrokerageName is an enum
# self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE)
# self.set_brokerage_model(BrokerageName.DEFAULT)
# the other way is to call SetBrokerageModel( IBrokerageModel ) with your
# own custom model. I've defined a simple extension to the default brokerage
# model to take into account a requirement to maintain 500 cash in the account at all times
self.set_brokerage_model(MinimumAccountBalanceBrokerageModel(self,500.00))
self.last = 1
def on_data(self, slice):
# Simple buy and hold template
if not self.portfolio.invested:
self.set_holdings("SPY", self.last)
if self.portfolio["SPY"].quantity == 0:
# each time we fail to purchase we'll decrease our set holdings percentage
self.debug(str(self.time) + " - Failed to purchase stock")
self.last *= 0.95
else:
self.debug("{} - Purchased Stock @ SetHoldings( {} )".format(self.time, self.last))
class MinimumAccountBalanceBrokerageModel(DefaultBrokerageModel):
'''Custom brokerage model that requires clients to maintain a minimum cash balance'''
def __init__(self, algorithm, minimum_account_balance):
self.algorithm = algorithm
self.minimum_account_balance = minimum_account_balance
def can_submit_order(self,security, order, message):
'''Prevent orders which would bring the account below a minimum cash balance'''
message = None
# we want to model brokerage requirement of minimum_account_balance cash value in account
order_cost = order.get_value(security)
cash = self.algorithm.portfolio.cash
cash_after_order = cash - order_cost
if cash_after_order < self.minimum_account_balance:
# return a message describing why we're not allowing this order
message = BrokerageMessageEvent(BrokerageMessageType.WARNING, "InsufficientRemainingCapital", "Account must maintain a minimum of ${0} USD at all times. Order ID: {1}".format(self.minimum_account_balance, order.id))
self.algorithm.error(str(message))
return False
return True
+193
View File
@@ -0,0 +1,193 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Strategy example algorithm using CAPE - a bubble indicator dataset saved in dropbox. CAPE is based on a macroeconomic indicator(CAPE Ratio),
### we are looking for entry/exit points for momentum stocks CAPE data: January 1990 - December 2014
### Goals:
### Capitalize in overvalued markets by generating returns with momentum and selling before the crash
### Capitalize in undervalued markets by purchasing stocks at bottom of trough
### </summary>
### <meta name="tag" content="strategy example" />
### <meta name="tag" content="custom data" />
class BubbleAlgorithm(QCAlgorithm):
def initialize(self):
self.set_cash(100000)
self.set_start_date(1998,1,1)
self.set_end_date(2014,6,1)
self._symbols = []
self._macd_dic, self._rsi_dic = {},{}
self._new_low, self._curr_cape = None, None
self._counter, self._counter2 = 0, 0
self._c, self._c_copy = np.empty([4]), np.empty([4])
self._symbols.append("SPY")
# add CAPE data
self.add_data(Cape, "CAPE")
# # Present Social Media Stocks:
# self._symbols.append("FB"), self._symbols.append("LNKD"),self._symbols.append("GRPN"), self._symbols.append("TWTR")
# self.set_start_date(2011, 1, 1)
# self.set_end_date(2014, 12, 1)
# # 2008 Financials
# self._symbols.append("C"), self._symbols.append("AIG"), self._symbols.append("BAC"), self._symbols.append("HBOS")
# self.set_start_date(2003, 1, 1)
# self.set_end_date(2011, 1, 1)
# # 2000 Dot.com
# self._symbols.append("IPET"), self._symbols.append("WBVN"), self._symbols.append("GCTY")
# self.set_start_date(1998, 1, 1)
# self.set_end_date(2000, 1, 1)
for stock in self._symbols:
self.add_security(SecurityType.EQUITY, stock, Resolution.MINUTE)
self._macd = self.macd(stock, 12, 26, 9, MovingAverageType.EXPONENTIAL, Resolution.DAILY)
self._macd_dic[stock] = self._macd
self._rsi = self.rsi(stock, 14, MovingAverageType.EXPONENTIAL, Resolution.DAILY)
self._rsi_dic[stock] = self._rsi
# Trying to find if current Cape is the lowest Cape in three months to indicate selling period
def on_data(self, data):
if self._curr_cape and self._new_low is not None:
try:
# Bubble territory
if self._curr_cape > 20 and self._new_low == False:
for stock in self._symbols:
# Order stock based on MACD
# During market hours, stock is trading, and sufficient cash
if self.securities[stock].holdings.quantity == 0 and self._rsi_dic[stock].current.value < 70 \
and self.securities[stock].price != 0 \
and self.portfolio.cash > self.securities[stock].price * 100 \
and self.time.hour == 9 and self.time.minute == 31:
self.buy_stock(stock)
# Utilize RSI for overbought territories and liquidate that stock
if self._rsi_dic[stock].current.value > 70 and self.securities[stock].holdings.quantity > 0 \
and self.time.hour == 9 and self.time.minute == 31:
self.sell_stock(stock)
# Undervalued territory
elif self._new_low:
for stock in self._symbols:
# Sell stock based on MACD
if self.securities[stock].holdings.quantity > 0 and self._rsi_dic[stock].current.value > 30 \
and self.time.hour == 9 and self.time.minute == 31:
self.sell_stock(stock)
# Utilize RSI and MACD to understand oversold territories
elif self.securities[stock].holdings.quantity == 0 and self._rsi_dic[stock].current.value < 30 \
and self.securities[stock].price != 0 and self.portfolio.cash > self.securities[stock].price * 100 \
and self.time.hour == 9 and self.time.minute == 31:
self.buy_stock(stock)
# Cape Ratio is missing from original data
# Most recent cape data is most likely to be missing
elif self._curr_cape == 0:
self.debug("Exiting due to no CAPE!")
self.quit("CAPE ratio not supplied in data, exiting.")
except:
# Do nothing
return None
if not data.contains_key("CAPE"): return
self._new_low = False
# Adds first four Cape Ratios to array c
self._curr_cape = data["CAPE"].cape
if self._counter < 4:
self._c[self._counter] = self._curr_cape
self._counter +=1
# Replaces oldest Cape with current Cape
# Checks to see if current Cape is lowest in the previous quarter
# Indicating a sell off
else:
self._c_copy = self._c
self._c_copy = np.sort(self._c_copy)
if self._c_copy[0] > self._curr_cape:
self._new_low = True
self._c[self._counter2] = self._curr_cape
self._counter2 += 1
if self._counter2 == 4: self._counter2 = 0
self.debug("Current Cape: " + str(self._curr_cape) + " on " + str(self.time))
if self._new_low:
self.debug("New Low has been hit on " + str(self.time))
# Buy this symbol
def buy_stock(self,symbol):
s = self.securities[symbol].holdings
if self._macd_dic[symbol].current.value>0:
self.set_holdings(symbol, 1)
self.debug("Purchasing: " + str(symbol) + " MACD: " + str(self._macd_dic[symbol]) + " RSI: " + str(self._rsi_dic[symbol])
+ " Price: " + str(round(self.securities[symbol].price, 2)) + " Quantity: " + str(s.quantity))
# Sell this symbol
def sell_stock(self,symbol):
s = self.securities[symbol].holdings
if s.quantity > 0 and self._macd_dic[symbol].current.value < 0:
self.liquidate(symbol)
self.debug("Selling: " + str(symbol) + " at sell MACD: " + str(self._macd_dic[symbol]) + " RSI: " + str(self._rsi_dic[symbol])
+ " Price: " + str(round(self.securities[symbol].price, 2)) + " Profit from sale: " + str(s.last_trade_profit))
# CAPE Ratio for SP500 PE Ratio for avg inflation adjusted earnings for previous ten years Custom Data from DropBox
# Original Data from: http://www.econ.yale.edu/~shiller/data.htm
class Cape(PythonData):
# Return the URL string source of the file. This will be converted to a stream
# <param name="config">Configuration object</param>
# <param name="date">Date of this source file</param>
# <param name="is_live_mode">true if we're in live mode, false for backtesting mode</param>
# <returns>String URL of source file.</returns>
def get_source(self, config, date, is_live_mode):
# Remember to add the "?dl=1" for dropbox links
return SubscriptionDataSource("https://www.dropbox.com/s/ggt6blmib54q36e/CAPE.csv?dl=1", SubscriptionTransportMedium.REMOTE_FILE)
''' Reader Method : using set of arguments we specify read out type. Enumerate until
the end of the data stream or file. E.g. Read CSV file line by line and convert into data types. '''
# <returns>BaseData type set by Subscription Method.</returns>
# <param name="config">Config.</param>
# <param name="line">Line.</param>
# <param name="date">Date.</param>
# <param name="is_live_mode">true if we're in live mode, false for backtesting mode</param>
def reader(self, config, line, date, is_live_mode):
if not (line.strip() and line[0].isdigit()): return None
# New Nifty object
index = Cape()
index.symbol = config.symbol
try:
# Example File Format:
# Date | Price | Div | Earning | CPI | FractionalDate | Interest Rate | RealPrice | RealDiv | RealEarnings | CAPE
# 2014.06 1947.09 37.38 103.12 238.343 2014.37 2.6 1923.95 36.94 101.89 25.55
data = line.split(',')
# Dates must be in the format YYYY-MM-DD. If your data source does not have this format, you must use
# DateTime.parse_exact() and explicit declare the format your data source has.
index.time = datetime.strptime(data[0], "%Y-%m")
index["Cape"] = float(data[10])
index.value = data[10]
except ValueError:
# Do nothing
return None
return index
@@ -0,0 +1,138 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from cmath import isclose
from AlgorithmImports import *
### <summary>
### Algorithm demonstrating and ensuring that Bybit crypto futures brokerage model works as expected
### </summary>
class BybitCryptoFuturesRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2022, 12, 13)
self.set_end_date(2022, 12, 13)
# Set strategy cash (USD)
self.set_cash(100000)
self.set_brokerage_model(BrokerageName.BYBIT, AccountType.MARGIN)
# Translate lines 44-59 to Python:
self.add_crypto("BTCUSDT", Resolution.MINUTE)
self.btc_usdt = self.add_crypto_future("BTCUSDT", Resolution.MINUTE)
self.btc_usd = self.add_crypto_future("BTCUSD", Resolution.MINUTE)
# create two moving averages
self.fast = self.ema(self.btc_usdt.symbol, 30, Resolution.MINUTE)
self.slow = self.ema(self.btc_usdt.symbol, 60, Resolution.MINUTE)
self.interest_per_symbol = {}
self.interest_per_symbol[self.btc_usd.symbol] = 0
self.interest_per_symbol[self.btc_usdt.symbol] = 0
# the amount of USDT we need to hold to trade 'BTCUSDT'
self.btc_usdt.quote_currency.set_amount(200)
# the amount of BTC we need to hold to trade 'BTCUSD'
self.btc_usd.base_currency.set_amount(0.005)
def on_data(self, data):
interest_rates = data.get[MarginInterestRate]()
for interest_rate in interest_rates:
self.interest_per_symbol[interest_rate.key] += 1
cached_interest_rate = self.securities[interest_rate.key].cache.get_data(MarginInterestRate)
if cached_interest_rate != interest_rate.value:
raise AssertionError(f"Unexpected cached margin interest rate for {interest_rate.key}!")
if not self.slow.is_ready:
return
if self.fast > self.slow:
if not self.portfolio.invested and self.transactions.orders_count == 0:
ticket = self.buy(self.btc_usd.symbol, 1000)
if ticket.status != OrderStatus.INVALID:
raise AssertionError(f"Unexpected valid order {ticket}, should fail due to margin not sufficient")
self.buy(self.btc_usd.symbol, 100)
margin_used = self.portfolio.total_margin_used
btc_usd_holdings = self.btc_usd.holdings
# Coin futures value is 100 USD
holdings_value_btc_usd = 100
if abs(btc_usd_holdings.total_sale_volume - holdings_value_btc_usd) > 1:
raise AssertionError(f"Unexpected TotalSaleVolume {btc_usd_holdings.total_sale_volume}")
if abs(btc_usd_holdings.absolute_holdings_cost - holdings_value_btc_usd) > 1:
raise AssertionError(f"Unexpected holdings cost {btc_usd_holdings.holdings_cost}")
if not isclose(self.btc_usd.buying_power_model.get_maintenance_margin(MaintenanceMarginParameters.for_current_holdings(self.btc_usd)).value, margin_used):
raise AssertionError(f"Unexpected margin used {margin_used}")
self.buy(self.btc_usdt.symbol, 0.01)
margin_used = self.portfolio.total_margin_used - margin_used
btc_usdt_holdings = self.btc_usdt.holdings
# USDT futures value is based on it's price
holdings_value_usdt = self.btc_usdt.price * self.btc_usdt.symbol_properties.contract_multiplier * 0.01
if abs(btc_usdt_holdings.total_sale_volume - holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected TotalSaleVolume {btc_usdt_holdings.total_sale_volume}")
if abs(btc_usdt_holdings.absolute_holdings_cost - holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {btc_usdt_holdings.holdings_cost}")
if not isclose(self.btc_usdt.buying_power_model.get_maintenance_margin(MaintenanceMarginParameters.for_current_holdings(self.btc_usdt)).value, margin_used):
raise AssertionError(f"Unexpected margin used {margin_used}")
# position just opened should be just spread here
unrealized_profit = self.portfolio.total_unrealized_profit
if (5 - abs(unrealized_profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
if self.portfolio.total_profit != 0:
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
# let's revert our position
elif self.transactions.orders_count == 3:
self.sell(self.btc_usd.symbol, 300)
btc_usd_holdings = self.btc_usd.holdings
if abs(btc_usd_holdings.absolute_holdings_cost - 100 * 2) > 1:
raise AssertionError(f"Unexpected holdings cost {btc_usd_holdings.holdings_cost}")
self.sell(self.btc_usdt.symbol, 0.03)
# USDT futures value is based on it's price
holdings_value_usdt = self.btc_usdt.price * self.btc_usdt.symbol_properties.contract_multiplier * 0.02
if abs(self.btc_usdt.holdings.absolute_holdings_cost - holdings_value_usdt) > 1:
raise AssertionError(f"Unexpected holdings cost {self.btc_usdt.holdings.holdings_cost}")
# position just opened should be just spread here
profit = self.portfolio.total_unrealized_profit
if (5 - abs(profit)) < 0:
raise AssertionError(f"Unexpected TotalUnrealizedProfit {self.portfolio.total_unrealized_profit}")
# we barely did any difference on the previous trade
if (5 - abs(self.portfolio.total_profit)) < 0:
raise AssertionError(f"Unexpected TotalProfit {self.portfolio.total_profit}")
def on_order_event(self, order_event):
self.debug("{} {}".format(self.time, order_event.to_string()))
def on_end_of_algorithm(self):
self.log(f"{self.time} - TotalPortfolioValue: {self.portfolio.total_portfolio_value}")
self.log(f"{self.time} - CashBook: {self.portfolio.cash_book}")
if any(x == 0 for x in self.interest_per_symbol.values()):
raise AssertionError("Expected interest rate data for all symbols")
@@ -0,0 +1,80 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Algorithm demonstrating and ensuring that Bybit crypto brokerage model works as expected
### </summary>
class BybitCryptoRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2022, 12, 13)
self.set_end_date(2022, 12, 13)
# Set account currency (USDT)
self.set_account_currency("USDT")
# Set strategy cash (USD)
self.set_cash(100000)
# Add some coin as initial holdings
# When connected to a real brokerage, the amount specified in SetCash
# will be replaced with the amount in your actual account.
self.set_cash("BTC", 1)
self.set_brokerage_model(BrokerageName.BYBIT, AccountType.CASH)
self.btc_usdt = self.add_crypto("BTCUSDT").symbol
# create two moving averages
self.fast = self.ema(self.btc_usdt, 30, Resolution.MINUTE)
self.slow = self.ema(self.btc_usdt, 60, Resolution.MINUTE)
self.liquidated = False
def on_data(self, data):
if self.portfolio.cash_book["USDT"].conversion_rate == 0 or self.portfolio.cash_book["BTC"].conversion_rate == 0:
self.log(f"USDT conversion rate: {self.portfolio.cash_book['USDT'].conversion_rate}")
self.log(f"BTC conversion rate: {self.portfolio.cash_book['BTC'].conversion_rate}")
raise AssertionError("Conversion rate is 0")
if not self.slow.is_ready:
return
btc_amount = self.portfolio.cash_book["BTC"].amount
if self.fast > self.slow:
if btc_amount == 1 and not self.liquidated:
self.buy(self.btc_usdt, 1)
else:
if btc_amount > 1:
self.liquidate(self.btc_usdt)
self.liquidated = True
elif btc_amount > 0 and self.liquidated and len(self.transactions.get_open_orders()) == 0:
# Place a limit order to sell our initial BTC holdings at 1% above the current price
limit_price = round(self.securities[self.btc_usdt].price * 1.01, 2)
self.limit_order(self.btc_usdt, -btc_amount, limit_price)
def on_order_event(self, order_event):
self.debug("{} {}".format(self.time, order_event.to_string()))
def on_end_of_algorithm(self):
self.log(f"{self.time} - TotalPortfolioValue: {self.portfolio.total_portfolio_value}")
self.log(f"{self.time} - CashBook: {self.portfolio.cash_book}")
btc_amount = self.portfolio.cash_book["BTC"].amount
if btc_amount > 0:
raise AssertionError(f"BTC holdings should be zero at the end of the algorithm, but was {btc_amount}")
@@ -0,0 +1,79 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import time
import os
from AlgorithmImports import *
### <summary>
### Algorithm demonstrating and ensuring that Bybit crypto brokerage model works as expected with custom data types
### </summary>
class BybitCustomDataCryptoRegressionAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2022, 12, 13)
self.set_end_date(2022, 12, 13)
self.set_account_currency("USDT")
self.set_cash(100000)
self.set_brokerage_model(BrokerageName.BYBIT, AccountType.CASH)
symbol = self.add_crypto("BTCUSDT").symbol
self._btc_usdt = self.add_data(CustomCryptoData, symbol, Resolution.MINUTE).symbol
# create two moving averages
self._fast = self.ema(self._btc_usdt, 30, Resolution.MINUTE)
self._slow = self.ema(self._btc_usdt, 60, Resolution.MINUTE)
def on_data(self, data: Slice) -> None:
if not self._slow.is_ready:
return
if self._fast.current.value > self._slow.current.value:
if self.transactions.orders_count == 0:
self.buy(self._btc_usdt, 1)
else:
if self.transactions.orders_count == 1:
self.liquidate(self._btc_usdt)
def on_order_event(self, order_event: OrderEvent) -> None:
self.debug(f"{self.time} {order_event}")
class CustomCryptoData(PythonData):
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
tick_type_string = Extensions.tick_type_to_lower(config.tick_type)
formatted_date = date.strftime("%Y%m%d")
source = os.path.join(Globals.data_folder, "crypto", "bybit", "minute",
config.symbol.value.lower(), f"{formatted_date}_{tick_type_string}.zip")
return SubscriptionDataSource(source, SubscriptionTransportMedium.LOCAL_FILE, FileFormat.CSV)
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
csv = line.split(',')
data = CustomCryptoData()
data.symbol = config.symbol
data_datetime = datetime.combine(date.date(), time()) + timedelta(milliseconds=int(csv[0]))
data.time = Extensions.convert_to(data_datetime, config.data_time_zone, config.exchange_time_zone)
data.end_time = data.time + timedelta(minutes=1)
data["Open"] = float(csv[1])
data["High"] = float(csv[2])
data["Low"] = float(csv[3])
data["Close"] = float(csv[4])
data["Volume"] = float(csv[5])
data.value = float(csv[4])
return data
@@ -0,0 +1,109 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class InvalidCommand():
variable = 10
class VoidCommand():
quantity = 0
target = []
parameters = {}
targettime = None
def run(self, algo: IAlgorithm) -> bool:
if not self.targettime or self.targettime != algo.time:
return False
tag = self.parameters["tag"]
algo.order(self.target[0], self.get_quantity(), tag=tag)
return True
def get_quantity(self):
return self.quantity
class BoolCommand(Command):
something_else = {}
array_test = []
result = False
def run(self, algo) -> bool:
trade_ibm = self.my_custom_method()
if trade_ibm:
algo.debug(f"BoolCommand.run: {str(self)}")
algo.buy("IBM", 1)
return trade_ibm
def my_custom_method(self):
return self.result
### <summary>
### Regression algorithm asserting the behavior of different callback commands call
### </summary>
class CallbackCommandRegressionAlgorithm(QCAlgorithm):
def initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.set_start_date(2013, 10, 7)
self.set_end_date(2013, 10, 11)
self.add_equity("SPY")
self.add_equity("IBM")
self.add_equity("BAC")
self.add_command(VoidCommand)
self.add_command(BoolCommand)
threw_exception = False
try:
self.add_command(InvalidCommand)
except:
threw_exception = True
if not threw_exception:
raise ValueError('InvalidCommand did not throw!')
bool_command = BoolCommand()
bool_command.result = True
bool_command.something_else = { "Property": 10 }
bool_command.array_test = [ "SPY", "BTCUSD" ]
link = self.link(bool_command)
if "&command%5barray_test%5d%5b0%5d=SPY&command%5barray_test%5d%5b1%5d=BTCUSD&command%5bresult%5d=True&command%5bsomething_else%5d%5bProperty%5d=10&command%5b%24type%5d=BoolCommand" not in link:
raise ValueError(f'Invalid link was generated! {link}')
potential_command = VoidCommand()
potential_command.target = [ "BAC" ]
potential_command.quantity = 10
potential_command.parameters = { "tag": "Signal X" }
command_link = self.link(potential_command)
if "&command%5btarget%5d%5b0%5d=BAC&command%5bquantity%5d=10&command%5bparameters%5d%5btag%5d=Signal+X&command%5b%24type%5d=VoidCommand" not in command_link:
raise ValueError(f'Invalid link was generated! {command_link}')
self.notify.email("email@address", "Trade Command Event", f"Signal X trade\nFollow link to trigger: {command_link}")
untyped_command_link = self.link({ "symbol": "SPY", "parameters": { "quantity": 10 } })
if "&command%5bsymbol%5d=SPY&command%5bparameters%5d%5bquantity%5d=10" not in untyped_command_link:
raise ValueError(f'Invalid link was generated! {untyped_command_link}')
self.notify.email("email@address", "Untyped Command Event", f"Signal Y trade\nFollow link to trigger: {untyped_command_link}")
# We need to create a project on QuantConnect to test the broadcast_command method
# and use the project_id in the broadcast_command call
self.project_id = 21805137
# All live deployments receive the broadcasts below
broadcast_result = self.broadcast_command(potential_command)
broadcast_result2 = self.broadcast_command({ "symbol": "SPY", "parameters": { "quantity": 10 } })
def on_command(self, data: object) -> bool:
self.debug(f"on_command: {str(data)}")
self.buy(data.symbol, data.parameters["quantity"])
return True # False, None
@@ -0,0 +1,39 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### Regression algorithm to test we can liquidate our portfolio holdings using order properties
### </summary>
class CanLiquidateWithOrderPropertiesRegressionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2014, 6, 5)
self.set_end_date(2014, 6, 6)
self.set_cash(100000)
self.open_exchange = datetime(2014, 6, 6, 10, 0, 0)
self.close_exchange = datetime(2014, 6, 6, 16, 0, 0)
self.add_equity("AAPL", resolution = Resolution.MINUTE)
def on_data(self, slice):
if self.time > self.open_exchange and self.time < self.close_exchange:
if not self.portfolio.invested:
self.market_order("AAPL", 10)
else:
order_properties = OrderProperties()
order_properties.time_in_force = TimeInForce.DAY
tickets = self.liquidate(asynchronous = True, order_properties = order_properties)
for ticket in tickets:
if ticket.submit_request.order_properties.time_in_force != TimeInForce.DAY:
raise AssertionError(f"The TimeInForce for all orders should be daily, but it was {ticket.submit_request.order_properties.time_in_force}")

Some files were not shown because too many files have changed in this diff Show More