/*
* 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.
*/
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
///
/// Regression test for history and warm up using the data available in open source.
///
///
///
///
///
public class IndicatorWarmupAlgorithm : QCAlgorithm
{
private const string SPY = "SPY";
private const string GOOG = "GOOG";
private const string IBM = "IBM";
private const string BAC = "BAC";
private const string GOOGL = "GOOGL";
private readonly Dictionary _sd = new Dictionary();
public override void Initialize()
{
SetStartDate(2013, 10, 08);
SetEndDate(2013, 10, 11);
SetCash(1000000);
AddSecurity(SecurityType.Equity, SPY, Resolution.Minute);
AddSecurity(SecurityType.Equity, IBM, Resolution.Minute);
AddSecurity(SecurityType.Equity, BAC, Resolution.Minute);
AddSecurity(SecurityType.Equity, GOOG, Resolution.Daily);
AddSecurity(SecurityType.Equity, GOOGL, Resolution.Daily);
foreach (var security in Securities)
{
_sd.Add(security.Key, new SymbolData(security.Key, this));
}
// we want to warm up our algorithm
SetWarmup(SymbolData.RequiredBarsWarmup);
}
public override void OnData(Slice slice)
{
// we are only using warmup for indicator spooling, so wait for us to be warm then continue
if (IsWarmingUp) return;
foreach (var sd in _sd.Values)
{
var lastPriceTime = sd.Close.Current.Time;
// only make decisions when we have data on our requested resolution
if (lastPriceTime.RoundDown(sd.Security.Resolution.ToTimeSpan()) == lastPriceTime)
{
sd.Update();
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
SymbolData sd;
if (_sd.TryGetValue(orderEvent.Symbol, out sd))
{
sd.OnOrderEvent(orderEvent);
}
}
class SymbolData
{
public const int RequiredBarsWarmup = 40;
public const decimal PercentTolerance = 0.001m;
public const decimal PercentGlobalStopLoss = 0.01m;
private const int LotSize = 10;
public readonly Symbol Symbol;
public readonly Security Security;
public decimal Quantity
{
get { return Security.Holdings.Quantity; }
}
public readonly Identity Close;
public readonly AverageDirectionalIndex ADX;
public readonly ExponentialMovingAverage EMA;
public readonly MovingAverageConvergenceDivergence MACD;
private readonly QCAlgorithm _algorithm;
private OrderTicket _currentStopLoss;
public SymbolData(Symbol symbol, QCAlgorithm algorithm)
{
Symbol = symbol;
Security = algorithm.Securities[symbol];
Close = algorithm.Identity(symbol);
ADX = algorithm.ADX(symbol, 14);
EMA = algorithm.EMA(symbol, 14);
MACD = algorithm.MACD(symbol, 12, 26, 9, MovingAverageType.Simple);
// if we're receiving daily
_algorithm = algorithm;
}
public bool IsReady
{
get { return Close.IsReady && ADX.IsReady & EMA.IsReady && MACD.IsReady; }
}
public bool IsUptrend
{
get
{
const decimal tolerance = 1 + PercentTolerance;
return MACD.Signal > MACD*tolerance
&& EMA > Close*tolerance;
}
}
public bool IsDowntrend
{
get
{
const decimal tolerance = 1 - PercentTolerance;
return MACD.Signal < MACD*tolerance
&& EMA < Close*tolerance;
}
}
public void OnOrderEvent(OrderEvent fill)
{
if (fill.Status != OrderStatus.Filled)
{
return;
}
// if we just finished entering, place a stop loss as well
if (Security.Invested)
{
var stop = Security.Holdings.IsLong
? fill.FillPrice*(1 - PercentGlobalStopLoss)
: fill.FillPrice*(1 + PercentGlobalStopLoss);
_currentStopLoss = _algorithm.StopMarketOrder(Symbol, -Quantity, stop, "StopLoss at: " + stop);
}
// check for an exit, cancel the stop loss
else
{
if (_currentStopLoss != null && _currentStopLoss.Status.IsOpen())
{
// cancel our current stop loss
_currentStopLoss.Cancel("Exited position");
_currentStopLoss = null;
}
}
}
public void Update()
{
OrderTicket ticket;
TryEnter(out ticket);
TryExit(out ticket);
}
public bool TryEnter(out OrderTicket ticket)
{
ticket = null;
if (Security.Invested)
{
// can't enter if we're already in
return false;
}
int qty = 0;
decimal limit = 0m;
if (IsUptrend)
{
// 100 order lots
qty = LotSize;
limit = Security.Low;
}
else if (IsDowntrend)
{
limit = Security.High;
qty = -LotSize;
}
if (qty != 0)
{
ticket = _algorithm.LimitOrder(Symbol, qty, limit, "TryEnter at: " + limit);
}
return qty != 0;
}
public bool TryExit(out OrderTicket ticket)
{
const decimal exitTolerance = 1 + 2 * PercentTolerance;
ticket = null;
if (!Security.Invested)
{
// can't exit if we haven't entered
return false;
}
decimal limit = 0m;
if (Security.Holdings.IsLong && Close*exitTolerance < EMA)
{
limit = Security.High;
}
else if (Security.Holdings.IsShort && Close > EMA*exitTolerance)
{
limit = Security.Low;
}
if (limit != 0)
{
ticket = _algorithm.LimitOrder(Symbol, -Quantity, limit, "TryExit at: " + limit);
}
return -Quantity != 0;
}
}
}
}