# 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 * ### ### Regression test for history and warm up using the data available in open source. ### ### ### ### ### class IndicatorWarmupAlgorithm(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(1000000) #Set Strategy Cash # Find more symbols here: http://quantconnect.com/data self.add_equity("SPY") self.add_equity("IBM") self.add_equity("BAC") self.add_equity("GOOG", Resolution.DAILY) self.add_equity("GOOGL", Resolution.DAILY) self.__sd = { } for security in self.securities: self.__sd[security.key] = self.SymbolData(security.key, self) # we want to warm up our algorithm self.set_warmup(self.SymbolData.REQUIRED_BARS_WARMUP) 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 ''' # we are only using warmup for indicator spooling, so wait for us to be warm then continue if self.is_warming_up: return for sd in self.__sd.values(): last_price_time = sd.close.current.time if self.round_down(last_price_time, sd.security.subscription_data_config.increment): sd.update() def on_order_event(self, fill): sd = self.__sd.get(fill.symbol, None) if sd is not None: sd.on_order_event(fill) def round_down(self, time, increment): if increment.days != 0: return time.hour == 0 and time.minute == 0 and time.second == 0 else: return time.second == 0 class SymbolData: REQUIRED_BARS_WARMUP = 40 PERCENT_TOLERANCE = 0.001 PERCENT_GLOBAL_STOP_LOSS = 0.01 LOT_SIZE = 10 def __init__(self, symbol, algorithm): self.symbol = symbol self.__algorithm = algorithm # if we're receiving daily self.__current_stop_loss = None self.security = algorithm.securities[symbol] self.close = algorithm.identity(symbol) self._adx = algorithm.adx(symbol, 14) self._ema = algorithm.ema(symbol, 14) self._macd = algorithm.macd(symbol, 12, 26, 9) self.is_ready = self.close.is_ready and self._adx.is_ready and self._ema.is_ready and self._macd.is_ready self.is_uptrend = False self.is_downtrend = False def update(self): self.is_ready = self.close.is_ready and self._adx.is_ready and self._ema.is_ready and self._macd.is_ready tolerance = 1 - self.PERCENT_TOLERANCE self.is_uptrend = self._macd.signal.current.value > self._macd.current.value * tolerance and\ self._ema.current.value > self.close.current.value * tolerance self.is_downtrend = self._macd.signal.current.value < self._macd.current.value * tolerance and\ self._ema.current.value < self.close.current.value * tolerance self.try_enter() self.try_exit() def try_enter(self): # can't enter if we're already in if self.security.invested: return False qty = 0 limit = 0.0 if self.is_uptrend: # 100 order lots qty = self.LOT_SIZE limit = self.security.low elif self.is_downtrend: qty = -self.LOT_SIZE limit = self.security.high if qty != 0: ticket = self.__algorithm.limit_order(self.symbol, qty, limit, "TryEnter at: {0}".format(limit)) def try_exit(self): # can't exit if we haven't entered if not self.security.invested: return limit = 0 qty = self.security.holdings.quantity exit_tolerance = 1 + 2 * self.PERCENT_TOLERANCE if self.security.holdings.is_long and self.close.current.value * exit_tolerance < self._ema.current.value: limit = self.security.high elif self.security.holdings.is_short and self.close.current.value > self._ema.current.value * exit_tolerance: limit = self.security.low if limit != 0: ticket = self.__algorithm.limit_order(self.symbol, -qty, limit, "TryExit at: {0}".format(limit)) def on_order_event(self, fill): if fill.status != OrderStatus.FILLED: return qty = self.security.holdings.quantity # if we just finished entering, place a stop loss as well if self.security.invested: stop = fill.fill_price*(1 - self.PERCENT_GLOBAL_STOP_LOSS) if self.security.holdings.is_long \ else fill.fill_price*(1 + self.PERCENT_GLOBAL_STOP_LOSS) self.__current_stop_loss = self.__algorithm.stop_market_order(self.symbol, -qty, stop, "StopLoss at: {0}".format(stop)) # check for an exit, cancel the stop loss elif (self.__current_stop_loss is not None and self.__current_stop_loss.status is not OrderStatus.FILLED): # cancel our current stop loss self.__current_stop_loss.cancel("Exited position") self.__current_stop_loss = None