Overall Statistics
Total Trades
3
Average Win
0%
Average Loss
-1.82%
Compounding Annual Return
-5.356%
Drawdown
33.800%
Expectancy
-1
Net Profit
-5.351%
Sharpe Ratio
0.019
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.382
Beta
1.364
Annual Standard Deviation
0.346
Annual Variance
0.12
Information Ratio
-0.886
Tracking Error
0.314
Treynor Ratio
0.005
Total Fees
$5.10
namespace QuantConnect
{
    public partial class TestingTrailingOrders : QCAlgorithm
    {
        string symbol = "IBM";
        TrailingStopOrder trailingOrder;
        
        public override void Initialize()
        {
            SetStartDate(2013, 1, 1);
            SetEndDate(2013, 12, 31);

            SetCash(25000);

            AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
        }

        public void OnData(TradeBars data)
        {
            if (!Portfolio.HoldStock)
            {
                int quantity = (int)Math.Floor(Portfolio.Cash / data[symbol].Close);

                Order(symbol, quantity);
                trailingOrder = FixedTrailingStopOrder(symbol, quantity, data[symbol].Price, 0.95m, Time);
            }
            
            trailingOrder.ActualizeStopPrice(data[symbol].Price);
            
            if(trailingOrder.StopOrder != null && trailingOrder.StopOrder.Status == OrderStatus.Update)
            {
                Log(String.Format("Updating {0}", trailingOrder.StopOrder));
                Transactions.UpdateOrder(trailingOrder.StopOrder);
            }
        }
        
        public override void OnOrderEvent(OrderEvent fill)
        {
            var order = Transactions.GetOrderById(fill.OrderId);
            Console.WriteLine(Time + " - " + order.Type + " - " + fill.Status + ":: " + fill);
        }
    }
}
namespace QuantConnect
{
    /// <summary>
    /// Method used to trailing the stop loss price.
    /// </summary>
    public enum TrailingMethod
    {
        FixedPercentage,
        StandardDeviationStop, // not fully implemented yet
        // WIP
    }

    /// <summary>
    /// Stop Market Order Type Definition
    /// </summary>
    public class TrailingStopOrder // :QCAlgorithm we don't need another algorithm if you need access to the Log function, pass in the algortihm instance
                                   // or for debug you can use Console.WriteLine, or for errors you can use Console.Error.WriteLine
    {
        #region Fields and properties
        
        /// <summary>
        /// Algoritm reference for updating orders and logging
        /// </summary>
        private readonly QCAlgorithm _algorithm;
        
        /// <summary>
        /// The symbol which this order will track.
        /// </summary>
        private string _symbol;

        /// <summary>
        /// The fixed percentage to determine the trailing stop price.
        /// </summary>
        private decimal _stopPricePercentage;

        /// <summary>
        /// The window lookback to estimate the standar deviations stop prices.
        /// </summary>
        private int _windowLookback;

        /// <summary>
        /// The StopMarketOrder object to be used.
        /// </summary>
        public StopMarketOrder StopOrder;

        /// <summary>
        /// The series history used to estimate the standar deviations stop prices.
        /// </summary>
        private RollingWindow<decimal> _seriesHistory;

        /// <summary>
        /// Stop price for this stop market order.
        /// </summary>
        public decimal StopPrice;

        /// <summary>
        /// Is the stop price allowed to move backwards?
        /// </summary>
        public bool AllowStopPriceRetreat; // not implemented yet

        /// <summary>
        /// The trailing method used to determine the stop price.
        /// </summary>
        public TrailingMethod trailingMethod;

        /// <summary>
        /// Maximum value of the order at is the stop limit price
        /// </summary>
        public decimal Value
        {
            get { return StopOrder.Quantity * StopOrder.StopPrice; }
        }
        #endregion

        #region Constructor and destructor
        /// <summary>
        /// Initializes a new instance of the <see cref="TrailingStopOrder"/> class.
        /// </summary>
        /// <param name="stopOrder">The stop order.</param>
        public TrailingStopOrder(QCAlgorithm algorithm, string symbol, int quantity, decimal price, decimal pricePercentage, DateTime time)
        {
            _symbol = symbol;
            _algorithm = algorithm;
            _stopPricePercentage = CheckPercentage(quantity, pricePercentage);
            trailingMethod = TrailingMethod.FixedPercentage;
            StopPrice = price * _stopPricePercentage;
            StopOrder = new StopMarketOrder(symbol, quantity, StopPrice, time);
            RegisterTrailingOrder(_symbol);
            algorithm.Log("Trailing Stop Order ID " + StopOrder.Id + " set for " + _symbol + " at stop price " + StopOrder.StopPrice);
        }

        public TrailingStopOrder(QCAlgorithm algorithm, string symbol, int quantity, List<decimal> series, int lookBackWindow, DateTime time)
        {
            _symbol = symbol;
            _algorithm = algorithm;
            _windowLookback = lookBackWindow;
            _seriesHistory = new RollingWindow<decimal>(_windowLookback);
            foreach (decimal seriesValue in series)
            {
                _seriesHistory.Add(seriesValue);
            }

            decimal SDBand = EstimateSDBand(_seriesHistory);
            StopPrice = (quantity > 0) ? series[series.Count] - SDBand : series[series.Count] + SDBand;
            StopOrder = new StopMarketOrder(symbol, quantity, StopPrice, time);
            RegisterTrailingOrder(_symbol);
        }
        
        #endregion

        #region Methods
        /// <summary>
        /// Registers the trailing order to recieve automatic prices updates.
        /// </summary>
        /// <param name="symbol">Symbol asset prices to be updated.</param>
        private void RegisterTrailingOrder(string symbol)
        {
            // create and register the consolidator for updates
            var consolidator = new IdentityDataConsolidator<TradeBar>();
            _algorithm.SubscriptionManager.AddConsolidator(symbol, consolidator);
            consolidator.DataConsolidated += (sender, consolidated) =>
            {
                // if we always change the stop every single time to the close value
                // then it will always execute immediately!!
                this.ActualizeOrder(consolidated.Price);
            };
        }

        /// <summary>
        /// Check the order status and dispose the object if is filled.
        /// If not, estimates the new stop price and update the order if needed.
        /// </summary>
        /// <param name="lastPrice">The last price observation.</param>
        public void ActualizeStopPrice(decimal lastPrice)
        {
            decimal newStopPrice;
            bool priceChanged = false;

            // If the order is filled, dispose the instance.
            if (StopOrder.Status == OrderStatus.Filled)
            {
                //this.Dispose();
                return;
            }

            switch (trailingMethod)
            {
                case TrailingMethod.FixedPercentage:
                    newStopPrice = lastPrice * _stopPricePercentage;
                    if ((StopOrder.Direction == OrderDirection.Buy && newStopPrice > StopPrice) ||
                        (StopOrder.Direction == OrderDirection.Sell && newStopPrice < StopPrice))
                    {
                        StopPrice = newStopPrice;
                        priceChanged = true;
                    }
                    break;

                case TrailingMethod.StandardDeviationStop:
                    _seriesHistory.Add(lastPrice);
                    decimal SDBand = EstimateSDBand(_seriesHistory);
                    decimal direction = (StopOrder.Direction == OrderDirection.Buy) ? new decimal(-1) : new decimal(1);
                    StopPrice = lastPrice + SDBand * direction;
                    priceChanged = true;
                    break;
            }
            if (priceChanged) ActualizeOrder(StopPrice);
        }

        /// <summary>
        /// Actualizes the order.
        /// </summary>
        /// <param name="StopPrice">The updated stop price.</param>
        private void ActualizeOrder(decimal StopPrice)
        {
            decimal newPrice;
            if (StopOrder.Direction == OrderDirection.Buy)
            {
                // when buying we want the lowest price
                newPrice = Math.Min(StopOrder.StopPrice, StopPrice);
            }
            else
            {
                // when selling we want the highest price
                newPrice = Math.Max(StopOrder.StopPrice, StopPrice);
            }
            StopOrder.StopPrice = StopPrice;
            // update the order by sending to transaction manager
            _algorithm.Transactions.UpdateOrder(StopOrder);
            _algorithm.Log("Trailing Stop Order ID " + StopOrder.Id + " for " + _symbol + " update stop price to " + StopOrder.StopPrice);
        }

        /// <summary>
        /// Gets or sets the stop price percentage, adjusting the value if needed.
        /// </summary>
        /// <value>
        /// The stop price percentage.
        /// </value>
        private decimal CheckPercentage(int quantity, decimal pricePercentage)
        {
            if (quantity > 0 && pricePercentage > 1m) return pricePercentage - 1m;
            else if (quantity < 0 && pricePercentage < 1m) return pricePercentage + 1m;
            else return pricePercentage;
        }

        /// <summary>
        /// Estimates the standar deviation of the historical prices.
        /// </summary>
        /// <param name="seriesHistory">The price series history.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        private decimal EstimateSDBand(RollingWindow<decimal> seriesHistory)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
namespace QuantConnect
{
    public partial class TestingTrailingOrders : QCAlgorithm
    {
        #region Wrapper methods
        /// <summary>
        /// Instantiate a stop trailing order with stop price estiamted as a price fixed percentage 
        /// </summary>
        /// <param name="symbol">Symbol asset we're seeking to trade.</param>
        /// <param name="quantity">Quantity of the asset we're seeking to trade.</param>
        /// <param name="price">The actual symbol's price.</param>
        /// <param name="pricePercentage">The price fixed percentage used to estiamte the stop price.</param>
        /// <param name="time">Time the order was placed.</param>
        /// <returns>A trailing stop order</returns>
        public TrailingStopOrder FixedTrailingStopOrder(string symbol, int quantity, decimal price, decimal pricePercentage, DateTime time)
        {
            var order = new TrailingStopOrder(this, symbol, quantity, price, pricePercentage, time);
            Transactions.AddOrder(order.StopOrder);
            return order;
        }

        /// <summary>
        /// Instantiate a stop trailing order with stop price estiamted as price +/- a standar deviation.
        /// </summary>
        /// <param name="symbol">Symbol asset we're seeking to trade</param>
        /// <param name="quantity">Quantity of the asset we're seeking to trade.</param>
        /// <param name="series">Historical prices used to estimate the standar deviation</param>
        /// <param name="lookBackWindow">The look back window used to estimate the standar deviation.</param>
        /// <param name="time">Time the order was placed.</param>
        /// <returns></returns>
        public TrailingStopOrder SDTrailingStopOrder(string symbol, int quantity, List<decimal> series, int lookBackWindow, DateTime time)
        {
            var order = new TrailingStopOrder(this, symbol, quantity, series, lookBackWindow, time);
            Transactions.AddOrder(order.StopOrder);
            return order;
        }
        #endregion
    }
}