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; } } }