Overall Statistics
Total Trades
54
Average Win
11.64%
Average Loss
-4.64%
Compounding Annual Return
7.358%
Drawdown
61.00%
Expectancy
0.365
Net Profit
65.781%
Sharpe Ratio
0.416
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
2.51
Alpha
0.114
Beta
-0.147
Annual Standard Deviation
0.24
Annual Variance
0.058
Information Ratio
0.012
Tracking Error
0.354
Treynor Ratio
-0.679
namespace QuantConnect 
{   
    /*
    
    King Keltner - trend following algorithm from "Building Winning Trading Systems" by George Pruitt.
    Buys when price action above Keltner channel, shorts when below.
    Exits when price action crosses average.
    
    */
    //---------------------------------------------------------------------------- ALGO
    public class KingKeltner : QCAlgorithm
    {
        
        //primary instrument to trade
        string symbol = "USO";
        
        //indicators
        SimpleMovingAverage _sma;
        SimpleMovingAverage _exit;
        AverageTrueRange _atr;
        
        //other
        decimal _pSMA;
        decimal upBand;
        decimal dnBand;
        decimal action;
        
        //conso0lidating
        private TimeSpan _barPeriod = TimeSpan.FromDays(1);
        private Consolidator _consolidator;
        
        //---------------------------------------------------------------------------- INIT
        public override void Initialize()
        {
            //Start and End Date range for the backtest:
            SetStartDate(2008, 1, 1);
            SetEndDate(DateTime.Now);
            
            //Cash allocation
            SetCash(25000);
            
            AddSecurity(SecurityType.Equity, symbol, Resolution.Minute, true, 1, false);
            Securities[symbol].Model = new CustomTransactionModel();
            
            //Setup Consolidator bar bar
            _consolidator = new Consolidator(_barPeriod);
            
            //Custom Data Indicators:
            _sma = new SimpleMovingAverage(40);
            
            //exit sma
            _exit = new SimpleMovingAverage(40);
            
            //etr for Keltner channel
            _atr = ATR(symbol, 40,  MovingAverageType.Simple, Resolution.Daily);
            
        }

        public void OnData(TradeBars data)
        {
            if (_consolidator.Update(data[symbol]))
            {
                try
                {
                    //weighting current price to avoid outliers
                    action = ((data[symbol].High+data[symbol].Low+data[symbol].Close)/3);
                    
                    //updating custom indies
                    TradeBar bar;
                    if (data.TryGetValue(symbol, out bar))
                    {
                        // pump the daily data into our sma
                        _sma.Update(bar.Time, action);
                        _exit.Update(bar.Time, action);
                    }
                    
                    //making channel
                    upBand = _sma + _atr;
                    dnBand = _sma - _atr;
                    
                    //Plot indicators
                    Plot("Indicators", "UpBand", upBand);
                    Plot("Indicators", "DnBand", dnBand);
                    Plot("Indicators", "Price", data[symbol].Close);
                    
                    //quantity for Order()
                    //int qty = (int)Math.Floor(Portfolio.Cash / data[symbol].Close);
                    
                    //---------------------------------------------------------------------------- EXITS
                    if (Portfolio.HoldStock)
                    {
                        if(Portfolio[symbol].IsLong && action <= _exit)
                        {
                            Liquidate(symbol);
                        } else
                        if(Portfolio[symbol].IsShort && action >= _exit)
                        {
                            Liquidate(symbol);
                        }
                    }
                
                    //---------------------------------------------------------------------------- ENTRIES
                    if(!Portfolio.HoldStock && _pSMA < _sma && action > upBand)
                    {
                        //Order(symbol, qty);
                        SetHoldings(symbol, 1);
                    } else
                    if(!Portfolio.HoldStock && _pSMA > _sma && action < dnBand)
                    {
                        //Order(symbol, -qty);
                        SetHoldings(symbol, -1);
                    }
                 
                 _pSMA = _sma;   
                    
                } catch(Exception err)
                {
                    Debug(err.Message);
                }
                
        
                
            }// end of consolidator
        
        } // end of trabers ond
        
    }// end of algo
    
}//end of namespace
namespace QuantConnect.Securities 
{
    public class CustomTransactionModel : ISecurityTransactionModel 
    {
        /// <summary> Initialize the default transaction model class </summary>
        public CustomTransactionModel() 
        {  }
        
        /// <summary>
        /// Default market fill model for the base security class. Fills at the last traded price.
        /// </summary>
        /// <param name="asset">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/>
        /// <seealso cref="LimitFill(Security, LimitOrder)"/>
        public OrderEvent MarketFill(Security asset, MarketOrder order)
        {
            //Default order event to return.
            var fill = new OrderEvent(order);
            //Order [fill]price for a market order model is the current security price.
            order.Price = asset.Price;
            order.Status = OrderStatus.Filled;

            //For backtesting, we assuming the order is 100% filled on first attempt.
            fill.FillPrice = asset.Price;
            fill.FillQuantity = order.Quantity;
            fill.Status = order.Status;
            return fill;
        }


        /// <summary>
        /// Default stop fill model implementation in base class security. (Stop Market Order Type)
        /// </summary>
        /// <param name="asset">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        /// <seealso cref="LimitFill(Security, LimitOrder)"/>
        public OrderEvent StopMarketFill(Security asset, StopMarketOrder order)
        {
            //Default order event to return.
            var fill = new OrderEvent(order);

            //If its cancelled don't need anymore checks:
            if (fill.Status == OrderStatus.Canceled) return fill;

            //Check if the Stop Order was filled: opposite to a limit order
            switch (order.Direction)
            {
                case OrderDirection.Sell:
                    //-> 1.1 Sell Stop: If Price below setpoint, Sell:
                    if (asset.Price < order.StopPrice)
                    {
                        order.Status = OrderStatus.Filled;
                        order.Price = asset.Price;
                    }
                    break;
                case OrderDirection.Buy:
                    //-> 1.2 Buy Stop: If Price Above Setpoint, Buy:
                    if (asset.Price > order.StopPrice)
                    {
                        order.Status = OrderStatus.Filled;
                        order.Price = asset.Price;
                    }
                    break;
            }

            if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled)
            {
                fill.FillQuantity = order.Quantity;
                fill.FillPrice = asset.Price;        //Stop price as security price because can gap past stop price.
                fill.Status = order.Status;
            }

            return fill;
        }


        /// <summary>
        /// Default limit order fill model in the base security class.
        /// </summary>
        /// <param name="asset">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        public OrderEvent LimitFill(Security asset, LimitOrder order)
        {
            //Initialise;
            var fill = new OrderEvent(order);

            //If its cancelled don't need anymore checks:
            if (fill.Status == OrderStatus.Canceled) return fill;

            //Depending on the resolution, return different data types:
            var marketData = asset.GetLastData();

            decimal marketDataMinPrice;
            decimal marketDataMaxPrice;
            if (marketData.DataType == MarketDataType.TradeBar)
            {
                marketDataMinPrice = ((TradeBar)marketData).Low;
                marketDataMaxPrice = ((TradeBar)marketData).High;
            }
            else
            {
                marketDataMinPrice = marketData.Value;
                marketDataMaxPrice = marketData.Value;
            }

            //-> Valid Live/Model Order: 
            switch (order.Direction)
            {
                case OrderDirection.Buy:
                    //Buy limit seeks lowest price
                    if (marketDataMinPrice < order.LimitPrice)
                    {
                        //Set order fill:
                        order.Status = OrderStatus.Filled;
                        order.Price = asset.Price;
                    }
                    break;
                case OrderDirection.Sell:
                    //Sell limit seeks highest price possible
                    if (marketDataMaxPrice > order.LimitPrice)
                    {
                        order.Status = OrderStatus.Filled;
                        order.Price = asset.Price;
                    }
                    break;
            }

            if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled)
            {
                fill.FillQuantity = order.Quantity;
                fill.FillPrice = asset.Price;
                fill.Status = order.Status;
            }
            
            return fill;
        }


        /// <summary>
        /// Get the slippage approximation for this order
        /// </summary>
        /// <param name="security">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>decimal approximation for slippage</returns>
        public virtual decimal GetSlippageApproximation(Security security, Order order)
        {
            return 0;
        }


        /// <summary>
        /// Default security transaction model - no fees.
        /// </summary>
        public virtual decimal GetOrderFee(decimal quantity, decimal price)
        {
            return 0;
        }


        /// <summary>
        /// Process an order to see if it has been filled and return the matching order event.
        /// </summary>
        /// <param name="vehicle">Asset we're working with</param>
        /// <param name="order">Order class to check if filled.</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        [Obsolete("Fill method has been made obsolete, use order type fill methods directly.")]
        public virtual OrderEvent Fill(Security vehicle, Order order)
        {
            return new OrderEvent(order);
        }


        /// <summary>
        /// Default market fill model for the base security class. Fills at the last traded price.
        /// </summary>
        /// <param name="security">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/>
        /// <seealso cref="LimitFill(Security, LimitOrder)"/>
        [Obsolete("MarketFill(Security, Order) method has been made obsolete, use MarketFill(Security, MarketOrder) method instead.")]
        public virtual OrderEvent MarketFill(Security security, Order order)
        {
            return MarketFill(security, order as MarketOrder);
        }


        /// <summary>
        /// Default stop fill model implementation in base class security. (Stop Market Order Type)
        /// </summary>
        /// <param name="security">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="LimitFill(Security, LimitOrder)"/>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        [Obsolete("StopFill(Security, Order) method has been made obsolete, use StopMarketFill(Security, StopMarketOrder) method instead.")]
        public virtual OrderEvent StopFill(Security security, Order order)
        {
            return StopMarketFill(security, order as StopMarketOrder);
        }


        /// <summary>
        /// Default limit order fill model in the base security class.
        /// </summary>
        /// <param name="security">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill informaton detailing the average price and quantity filled.</returns>
        /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        [Obsolete("LimitFill(Security, Order) method has been made obsolete, use LimitFill(Security, LimitOrder) method instead.")]
        public virtual OrderEvent LimitFill(Security security, Order order)
        {
            return LimitFill(security, order as LimitOrder);
        }

    } // End Algorithm Transaction Filling Classes

} // End QC Namespace
using System;
using System.Collections;
using System.Collections.Generic;
using QuantConnect.Securities;
using QuantConnect.Models;

namespace QuantConnect 
{
    
    /*
    *   TimeSpanConsolidator Helper Routine: Assemble generic timespan bar lengths: e.g. 10 minutes:
    *
    *   1. Setup the new Consolidator class with the timespan period:
    *   var _consolidator = new Consolidator(TimeSpan.FromMinutes(10));
    *
    *   2. Add in the data with the update routine. It will return true when bar ready
    *   if (_consolidator.Update(data["MSFT"])) {   UseBar    }
    */
    public class Consolidator 
    {
        private TradeBar _resultBar;
        private TradeBar _workingBar;
        private DateTime _start;
        private TimeSpan _period;
        
        //Result:
        public TradeBar Bar
        {
            get
            {
                return _resultBar;
            }
        }
        
        //Constructor: Set the period we'd like to scan
        public Consolidator(TimeSpan span) 
        {
            this._period = span;
            this._resultBar = new TradeBar();
            this._workingBar = new TradeBar(new DateTime(), "", Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
        }
        
        //Submit this bar, return true if we've started a new one.
        public bool Update(TradeBar newBar)
        {
            //Intialize:
            if (_start == new DateTime()) 
            {
                _start = newBar.Time;
            }
            
            //While we're less than end date, keep adding to this bar:
            if (newBar.Time < (_start + _period))
            {
                //Building bar:
                AddToBar(newBar);
                return false;
            } 
            else 
            {
                //Completed bar: start new one:
                _resultBar = _workingBar;
                //Create a new bar:
                _workingBar = new TradeBar(newBar.Time, newBar.Symbol, Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
                //Start of this bar:
                _start = newBar.Time;
                AddToBar(newBar);
                return true;
            }
        }
        
        //Add to a tradebar
        private void AddToBar(TradeBar newBar)
        {
            //Add this data to working bar:
            if (_workingBar.Time == new DateTime()) _workingBar.Time = newBar.Time;
            if (_workingBar.Symbol == "") _workingBar.Symbol = newBar.Symbol;
            if (_workingBar.Open == Decimal.Zero) _workingBar.Open = newBar.Open;
            if (newBar.High > _workingBar.High) _workingBar.High = newBar.High;
            if (newBar.Low < _workingBar.Low) _workingBar.Low = newBar.Low;
            _workingBar.Close = newBar.Close;
            _workingBar.Volume = newBar.Volume;
        }
    }

}