chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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}")
|
||||
+118
@@ -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 firm’s assets
|
||||
σ (sigma) = standard deviation of firm value changes (returns in V)
|
||||
τ (tau) = time to debt’s 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. 90–91.
|
||||
#
|
||||
# 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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user