| Overall Statistics |
|
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio NaN Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha NaN Beta NaN Annual Standard Deviation 0 Annual Variance 0 Information Ratio NaN Tracking Error NaN Treynor Ratio NaN Total Fees $0.00 |
namespace QuantConnect
{
using System;
using System.Linq;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Util;
/*
* QuantConnect University: Full Basic Template:
*
* The underlying QCAlgorithm class is full of helper methods which enable you to use QuantConnect.
* We have explained some of these here, but the full algorithm can be found at:
* https://github.com/QuantConnect/QCAlgorithm/blob/master/QuantConnect.Algorithm/QCAlgorithm.cs
*/
public class InstantaneousTrendAlgorithm : QCAlgorithm
{
private DateTime _startDate = new DateTime(2015, 8, 19);
private DateTime _endDate = new DateTime(2015, 8, 25);
private decimal _portfolioAmount = 22000;
private string symbol = "AAPL";
private int barcount = 0;
private RollingWindow<IndicatorDataPoint> Price;
private InstantaneousTrend trend;
private RollingWindow<IndicatorDataPoint> trendHistory;
private RollingWindow<IndicatorDataPoint> trendTrigger;
// Strategy
private InstantTrendStrategy iTrendStrategy;
private bool shouldSellOutAtEod = true;
private int orderId = 0;
private int tradesize;
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
//Initialize dates
SetStartDate(_startDate);
SetEndDate(_endDate);
SetCash(_portfolioAmount);
//Add as many securities as you like. All the data will be passed into the event handler:
AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
// Indicators
Price = new RollingWindow<IndicatorDataPoint>(14); // The price history
// ITrend
trend = new InstantaneousTrend(7);
trendHistory = new RollingWindow<IndicatorDataPoint>(14);
trendTrigger = new RollingWindow<IndicatorDataPoint>(14);
// The ITrendStrategy
iTrendStrategy = new InstantTrendStrategy(symbol, 14, this);
iTrendStrategy.ShouldSellOutAtEod = shouldSellOutAtEod;
}
//Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
public void OnData(TradeBars data)
{
barcount++;
// Add the history for the bar
var time = data.Time;
Price.Add(idp(time, (data[symbol].Close + data[symbol].Open) / 2));
// Update the indicators
trend.Update(idp(time, Price[0].Value));
trendHistory.Add(idp(time, trend.Current.Value)); //add last iteration value for the cycle
trendTrigger.Add(idp(time, trend.Current.Value));
if (barcount == 1)
{
tradesize = (int)(Portfolio.Cash / Convert.ToInt32(Price[0].Value + 1));
}
// iTrendStrategy starts on bar 3 because it uses trendHistory[0] - trendHistory[3]
if (barcount < 7 && barcount > 2)
{
trendHistory[0].Value = (Price[0].Value + 2 * Price[1].Value + Price[2].Value) / 4;
}
if (barcount > 2)
{
trendTrigger[0].Value = 2 * trendHistory[0].Value - trendHistory[2].Value;
}
Strategy(data);
if (data.Time.Hour == 16)
{
trend.Reset();
trendHistory.Reset();
trendTrigger.Reset();
barcount = 0;
Plot("Strategy Equity", "Portfolio", Portfolio.TotalPortfolioValue);
}
}
/// <summary>
/// Run the strategy associated with this algorithm
/// </summary>
/// <param name="data">TradeBars - the data received by the OnData event</param>
private void Strategy(TradeBars data)
{
string comment = string.Empty;
#region "Strategy Execution"
if (SellOutEndOfDay(data))
{
iTrendStrategy.Barcount = barcount; // for debugging
// if there were limit order tickets to cancel, wait a bar to execute the strategy
if (!CanceledUnfilledLimitOrder())
comment = iTrendStrategy.ExecuteStrategy(data, tradesize, trend.Current, trendTrigger[0]);
}
#endregion
}
/// <summary>
/// If the order did not fill within one bar, cancel it and assume the market moved away from the limit order
/// </summary>
private bool CanceledUnfilledLimitOrder()
{
#region "Unfilled Limit Orders"
bool retval = false;
var tickets = Transactions.GetOrderTickets(t => !t.Status.IsClosed());
if (tickets != null && tickets.Any())
{
foreach (var ticket in tickets)
{
ticket.Cancel();
retval = true;
}
}
#endregion
return retval;
}
/// <summary>
/// Handle order events
/// </summary>
/// <param name="orderEvent">the order event</param>
public override void OnOrderEvent(OrderEvent orderEvent)
{
base.OnOrderEvent(orderEvent);
ProcessOrderEvent(orderEvent);
}
/// <summary>
/// Local processing of the order event
/// </summary>
/// <param name="orderEvent">OrderEvent - the order event</param>
private void ProcessOrderEvent(OrderEvent orderEvent)
{
orderId = orderEvent.OrderId;
var tickets = Transactions.GetOrderTickets(t => t.OrderId == orderId);
if (tickets.Any())
{
foreach (OrderTicket ticket in tickets)
{
var status = ticket.Status;
if (ticket.Status == OrderStatus.Canceled)
{
iTrendStrategy.orderFilled = false;
}
if (ticket.Status == OrderStatus.Filled)
{
iTrendStrategy.orderFilled = true;
if (Portfolio[orderEvent.Symbol].Invested)
{
iTrendStrategy.nEntryPrice = orderEvent.FillPrice;
}
}
}
}
}
public bool SellOutEndOfDay(TradeBars data)
{
if (shouldSellOutAtEod)
{
if (data.Time.Hour == 15 && data.Time.Minute > 49 || data.Time.Hour == 16)
{
if (Portfolio[symbol].IsLong)
{
Sell(symbol, Portfolio[symbol].AbsoluteQuantity);
}
if (Portfolio[symbol].IsShort)
{
Buy(symbol, Portfolio[symbol].AbsoluteQuantity);
}
return false;
}
}
return true;
}
/// <summary>
/// Convenience function which creates an IndicatorDataPoint
/// </summary>
/// <param name="time">DateTime - the bar time for the IndicatorDataPoint</param>
/// <param name="value">decimal - the value for the IndicatorDataPoint</param>
/// <returns>a new IndicatorDataPoint</returns>
/// <remarks>I use this function to shorten the a Add call from
/// new IndicatorDataPoint(data.Time, value)
/// Less typing.</remarks>
private IndicatorDataPoint idp(DateTime time, decimal value)
{
return new IndicatorDataPoint(time, value);
}
}
}namespace QuantConnect {
using System;
/// <summary>
/// InstanteaousTrend Indicator
/// </summary>
public class InstantaneousTrend : WindowIndicator<IndicatorDataPoint>
{
// the alpha for the formula
private readonly decimal a = 0.5m;
private readonly int _period;
private readonly RollingWindow<IndicatorDataPoint> _trend;
private readonly RollingWindow<IndicatorDataPoint> _price;
private int barcount;
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="period"></param>
public InstantaneousTrend(string name, int period)
: base(name, period)
{
// InstantaneousTrend history
_trend = new RollingWindow<IndicatorDataPoint>(period);
_price = new RollingWindow<IndicatorDataPoint>(period);
_period = period;
barcount = 0;
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="period">int - the number of periods in the indicator warmup</param>
public InstantaneousTrend(int period)
: this("CCy" + period, period)
{
}
/// <summary>
/// Gets a flag indicating when this indicator is ready and fully initialized
/// </summary>
public override bool IsReady
{
get { return _trend.IsReady; }
}
/// <summary>
/// Calculates the next value for the ITrend
/// </summary>
/// <param name="window">the window for this indicator</param>
/// <param name="input">the latest price to input into the trend</param>
/// <returns>the computed value</returns>
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
{
// for convenience
var time = input.Time;
_price.Add(input);
if (barcount < _period)
{
_trend.Add(input);
}
else
{
// Calc the low pass filter _trend value and add it to the _trend
var lfp = (a - ((a / 2) * (a / 2))) * input.Value + ((a * a) / 2) * _price[1].Value
- (a - (3 * (a * a) / 4)) * _price[2].Value + 2 * (1 - a) * _trend[0].Value
- ((1 - a) * (1 - a)) * _trend[1].Value;
_trend.Add(idp(time, lfp));
}
barcount++;
return _trend[0].Value;
}
/// <summary>
/// Factory function which creates an IndicatorDataPoint
/// </summary>
/// <param name="time">DateTime - the bar time for the IndicatorDataPoint</param>
/// <param name="value">decimal - the value for the IndicatorDataPoint</param>
/// <returns>a new IndicatorDataPoint</returns>
/// <remarks>I use this function to shorten the a Add call from
/// new IndicatorDataPoint(data.Time, value)
/// Less typing.</remarks>
private IndicatorDataPoint idp(DateTime time, decimal value)
{
return new IndicatorDataPoint(time, value);
}
}
}namespace QuantConnect {
using System;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Orders;
/// <summary>
/// From Ehlers Cybernetics page 27 on Trading the trend
/// </summary>
public class InstantTrendStrategy
{
/// <summary>
/// The entry price for the latest trade
/// </summary>
public decimal nEntryPrice { get; set; }
public int Barcount { get; set; }
private bool bReverseTrade = false;
private string _symbol { get; set; }
private decimal RevPct = 1.0025m;
private decimal RngFac = .35m;
private decimal nLimitPrice = 0;
private int nStatus = 0;
private int xOver = 0;
private RollingWindow<IndicatorDataPoint> trendHistory;
/// <summary>
/// Flag to determine if the algo should go flat overnight.
/// </summary>
public bool ShouldSellOutAtEod;
/// <summary>
/// the Algorithm being run.
/// </summary>
public QCAlgorithm _algorithm;
/// <summary>
/// The flag as to whether the order has been filled.
/// </summary>
public Boolean orderFilled { get; set; }
/// <summary>
/// Constructor initializes the symbol and period of the RollingWindow
/// </summary>
/// <param name="symbol">string - ticker symbol</param>
/// <param name="period">int - the period of the Trend History Rolling Window</param>
/// <param name="algorithm"></param>
public InstantTrendStrategy(string symbol, int period, QCAlgorithm algorithm)
{
_symbol = symbol;
trendHistory = new RollingWindow<IndicatorDataPoint>(period);
_algorithm = algorithm;
orderFilled = true;
}
/// <summary>
/// Executes the Instant Trend strategy
/// </summary>
/// <param name="data">TradeBars - the current OnData</param>
/// <param name="tradesize"></param>
/// <param name="trendCurrent">IndicatorDataPoint - the current trend value trend</param>
/// <param name="triggerCurrent">IndicatorDataPoint - the current trigger</param>
public string ExecuteStrategy(TradeBars data, int tradesize, IndicatorDataPoint trendCurrent, IndicatorDataPoint triggerCurrent)
{
OrderTicket ticket;
string comment = string.Empty;
trendHistory.Add(trendCurrent);
nStatus = 0;
if (_algorithm.Portfolio[_symbol].IsLong) nStatus = 1;
if (_algorithm.Portfolio[_symbol].IsShort) nStatus = -1;
if (!trendHistory.IsReady)
{
return "Trend Not Ready";
}
if (!SellOutEndOfDay(data))
{
#region "Strategy Execution"
bReverseTrade = false;
try
{
var nTrig = 2 * trendHistory[0].Value - trendHistory[2].Value;
if (nStatus == 1 && nTrig < (nEntryPrice / RevPct)){
comment = string.Format("Long Reverse to short. Close < {0} / {1}", nEntryPrice, RevPct);
ticket = ReverseToShort();
orderFilled = ticket.OrderId > 0;
bReverseTrade = true;
}
else
{
if (nStatus == -1 && nTrig > (nEntryPrice * RevPct))
{
comment = string.Format("Short Reverse to Long. Close > {0} * {1}", nEntryPrice, RevPct);
ticket = ReverseToLong();
orderFilled = ticket.OrderId > 0;
bReverseTrade = true;
}
}
if (!bReverseTrade)
{
if (nTrig > trendHistory[0].Value)
{
if (xOver == -1 && nStatus != 1)
{
if (!orderFilled)
{
ticket = _algorithm.Buy(_symbol, tradesize);
comment = string.Format("Enter Long after cancel trig xover price up");
}
else
{
nLimitPrice = Math.Max(data[_symbol].Low, (data[_symbol].Close - (data[_symbol].High - data[_symbol].Low) * RngFac));
ticket = _algorithm.LimitOrder(_symbol, tradesize, nLimitPrice, "Long Limit");
comment = string.Format("Enter Long Limit trig xover price up", nLimitPrice);
}
}
if (comment.Length == 0)
comment = "Trigger over Trend";
xOver = 1;
}
else
{
if (nTrig < trendHistory[0].Value)
{
if (xOver == 1 && nStatus != -1)
{
if (!orderFilled)
{
ticket = _algorithm.Sell(_symbol, tradesize);
comment = string.Format("Enter Short after cancel trig xunder price down");
}
else
{
nLimitPrice = Math.Min(data[_symbol].High, (data[_symbol].Close + (data[_symbol].High - data[_symbol].Low) * RngFac));
ticket = _algorithm.LimitOrder(_symbol, -tradesize, nLimitPrice, "Short Limit");
comment = string.Format("Enter Short Limit at {0} trig xover price down", nLimitPrice);
}
}
if (comment.Length == 0)
comment = "Trigger under trend";
xOver = -1;
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.StackTrace);
}
#endregion
}
return comment;
}
private OrderTicket ReverseToLong()
{
nLimitPrice = 0;
nStatus = 1;
return _algorithm.Buy(_symbol, _algorithm.Portfolio[_symbol].Quantity * 2);
}
private OrderTicket ReverseToShort()
{
nLimitPrice = 0;
nStatus = -1;
return _algorithm.Sell(_symbol, _algorithm.Portfolio[_symbol].Quantity * 2);
}
private bool SellOutEndOfDay(TradeBars data)
{
if (ShouldSellOutAtEod)
{
if (data.Time.Hour == 15 && data.Time.Minute > 55 || data.Time.Hour == 16)
{
if (_algorithm.Portfolio[_symbol].IsLong)
{
_algorithm.Sell(_symbol, _algorithm.Portfolio[_symbol].AbsoluteQuantity);
}
if (_algorithm.Portfolio[_symbol].IsShort)
{
_algorithm.Buy(_symbol, _algorithm.Portfolio[_symbol].AbsoluteQuantity);
}
return true;
}
}
return false;
}
}
}