Overall Statistics
Total Trades
280
Average Win
0.08%
Average Loss
-0.04%
Compounding Annual Return
62.908%
Drawdown
2.100%
Expectancy
-0.005
Net Profit
0.918%
Sharpe Ratio
2.964
Loss Rate
69%
Win Rate
31%
Profit-Loss Ratio
2.26
Alpha
0.716
Beta
1.262
Annual Standard Deviation
0.132
Annual Variance
0.017
Information Ratio
5.903
Tracking Error
0.11
Treynor Ratio
0.31
Total Fees
$280.00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuantConnect.Algorithm;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Securities.Equity;

namespace QuantConnect
{
    class MultisymbolAlgorithm : QCAlgorithm
    {

        #region "Variables"

        private DateTime startTime = DateTime.Now;
        private DateTime _startDate = new DateTime(2015, 8, 1);
        private DateTime _endDate = new DateTime(2015, 8, 8);
        private decimal _portfolioAmount = 26000;

        /* +-------------------------------------------------+
         * |Algorithm Control Panel                          |
         * +-------------------------------------------------+*/
        private static int SMAPeriod = 22;            // Instantaneous Trend period.
        private static decimal Tolerance = 0.0001m;      // Trigger - Trend crossing tolerance.
        private static decimal RevertPCT = 1.0015m;     // Percentage tolerance before revert position.
        private static decimal maxLeverage = 1m;        // Maximum Leverage.
        private decimal leverageBuffer = 0.25m;         // Percentage of Leverage left unused.
        private int maxOperationQuantity = 500;         // Maximum shares per operation.
        private decimal RngFac = 0.35m;                 // Percentage of the bar range used to estimate limit prices.
        private bool noOvernight = true;                // Close all positions before market close.
        /* +-------------------------------------------------+*/

        readonly string[] symbolarray = new string[] { "AAPL", "NFLX", "AMZN", "SPY" };
        readonly List<string> Symbols = new List<string>();

        // Dictionary used to store the RSIStrategy object for each symbol.
        private Dictionary<string, MultiSymbolStrategy> Strategy = new Dictionary<string, MultiSymbolStrategy>();

        // Dictionary used to store the portfolio sharesize for each symbol.
        private Dictionary<string, decimal> ShareSize = new Dictionary<string, decimal>();

        private EquityExchange theMarket = new EquityExchange();

        private int barcount;


        #endregion

        public override void Initialize()
        {
            SetStartDate(_startDate);       //Set Start Date
            SetEndDate(_endDate);           //Set End Date
            SetCash(_portfolioAmount);      //Set Strategy Cash

            foreach (string t in symbolarray)
            {
                Symbols.Add(t);
            }

            foreach (string symbol in Symbols)
            {
                AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
                var priceIdentity = Identity(symbol, selector: Field.Close);

                Strategy.Add(symbol, new MultiSymbolStrategy(priceIdentity, SMAPeriod, Tolerance, RevertPCT));

                // Equally weighted portfolio.
                ShareSize.Add(symbol, (maxLeverage * (1 - leverageBuffer)) / Symbols.Count());
            }

        }
        public void OnData(TradeBars data)
        {
            
            barcount++;
            foreach (KeyValuePair<Symbol, TradeBar> kvp in data)
            {
                OnDataForSymbol(kvp);
            }
        }
        private void OnDataForSymbol(KeyValuePair<Symbol, TradeBar> data)
        {
            bool isMarketAboutToClose = !theMarket.DateTimeIsOpen(Time.AddMinutes(10));

            // Operate only if the market is open
            if (theMarket.DateTimeIsOpen(Time))
            {
                // First check if there are some limit orders not filled yet.
                if (Transactions.LastOrderId > 0)
                {
                    CheckLimitOrderStatus(data);
                }
                // Check if the market is about to close and noOvernight is true.
                OrderSignal actualOrder = OrderSignal.doNothing;
                if (noOvernight && isMarketAboutToClose)
                {
                    actualOrder = ClosePositions(data.Key);
                }
                else
                {
                    // Now check if there is some signal and execute the strategy.
                    actualOrder = Strategy[data.Key].ActualSignal;
                }
                // Only execute an order if the strategy is unlocked
                if (actualOrder != OrderSignal.doNothing && Strategy[data.Key].IsActive)
                {
                    // set now because MarketOrder fills can happen before ExecuteStrategy returns.
                    Strategy[data.Key].Status = OrderStatus.New;
                    Strategy[data.Key].IsActive = false;
                    ExecuteStrategy(data.Key, actualOrder);
                }
            }
        }
        /// <summary>
        /// If the limit order aren't filled, then cancels the order and send a market order.
        /// </summary>
        private void CheckLimitOrderStatus(KeyValuePair<Symbol, TradeBar> data)
        {
            // GetOrderTickets should return only 1 ticket, but since it returns an Enumerable 
            //   the code needs to iterate it.
            foreach (var liveticket in Transactions.GetOrderTickets(
                t => t.Symbol == data.Key && t.Status == OrderStatus.Submitted))
            {
                CheckNumberOfTradeAttempts(data, liveticket);
            }
        }
        private void CheckNumberOfTradeAttempts(KeyValuePair<Symbol, TradeBar> data, OrderTicket liveticket)
        {
            //Log(string.Format("Trade Attempts: {0} OrderId {1}", currentSignalInfo.TradeAttempts, liveticket.OrderId));
            if (Strategy[data.Key].TradeAttempts++ > 3)
            {
                liveticket.Cancel();
                //Log(string.Format("Order {0} cancellation sent. Trade attempts > 3.", liveticket.OrderId));
            }
        }
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            string symbol = orderEvent.Symbol.Value;
            Strategy[symbol].Status = orderEvent.Status;
            switch (orderEvent.Status)
            {
                case OrderStatus.New:
                case OrderStatus.None:
                case OrderStatus.Submitted:
                case OrderStatus.Invalid:
                    break;
                case OrderStatus.PartiallyFilled:
                    if (Strategy[symbol] != null)
                    {
                        // Do not unlock the strategy
                        Strategy[symbol].TradeAttempts++;
                        //Log(string.Format("Trade Attempts: {0} OrderId {1}", currentSignalInfo.TradeAttempts, orderEvent.OrderId));
                    }

                    break;
                case OrderStatus.Canceled:
                    if (Strategy[symbol] != null)
                    {
                        //Log(string.Format("Order {0} cancelled.", orderEvent.OrderId));
                        Strategy[symbol].IsActive = true;       // Unlock the strategy for the next bar 
                        Strategy[symbol].TradeAttempts = 0;     // Reset the number of trade attempts.
                    }
                    break;
                case OrderStatus.Filled:
                    if (Strategy[symbol] != null)
                    {
                        //Log(string.Format("Order Filled OrderId {0} on attempt {1}", orderEvent.OrderId, currentSignalInfo.TradeAttempts));
                        Strategy[symbol].IsActive = true;
                        Strategy[symbol].TradeAttempts = 0;

                    }
                    break;
            }
        }


        private OrderSignal ClosePositions(string symbol)
        {
            OrderSignal actualOrder;
            if (Strategy[symbol].Position == StockState.longPosition) actualOrder = OrderSignal.closeLong;
            else if (Strategy[symbol].Position == StockState.shortPosition) actualOrder = OrderSignal.closeShort;
            else actualOrder = OrderSignal.doNothing;
            return actualOrder;
        }
        /// <summary>
        /// Executes the ITrend strategy orders.
        /// </summary>
        /// <param name="symbol">The symbol to be traded.</param>
        /// <param name="actualOrder">The actual order to be execute.</param>
        private void ExecuteStrategy(string symbol, OrderSignal actualOrder)
        {
            // Define the operation size.  PositionShares can sometimes return 0
            //   if your position gets overextended.  
            // If that happens the code avoids an invalid order, by just returning.
            int shares = PositionShares(symbol, actualOrder);
            if (shares == 0)
            {
                Strategy[symbol].IsActive = true;
                return;
            }

            switch (actualOrder)
            {
                case OrderSignal.goLong:
                case OrderSignal.goShort:
                case OrderSignal.goLongLimit:
                case OrderSignal.goShortLimit:
                    decimal limitPrice;
                    var barPrices = Securities[symbol];

                    // Define the limit price.
                    if (actualOrder == OrderSignal.goLong ||
                        actualOrder == OrderSignal.goLongLimit)
                    {
                        limitPrice = Math.Max(barPrices.Low,
                                    (barPrices.Close - (barPrices.High - barPrices.Low) * RngFac));
                    }
                    else
                    {
                        limitPrice = Math.Min(barPrices.High,
                                    (barPrices.Close + (barPrices.High - barPrices.Low) * RngFac));
                    }
                    // Send the order.
                    limitPrice = Math.Round(limitPrice, 2);
                    Log(string.Format("===> Limit Order {0} {1} {2} shares at {3}",symbol, actualOrder, shares, limitPrice ));

                    LimitOrder(symbol, shares, limitPrice);
                    break;

                case OrderSignal.closeLong:
                case OrderSignal.closeShort:
                    Log("<=== Closing Position " + symbol);
                    // Send the order.
                    MarketOrder(symbol, shares);
                    break;

                case OrderSignal.revertToLong:
                case OrderSignal.revertToShort:
                    Log("<===> Reverting Position" + symbol);
                    // Send the order.
                    MarketOrder(symbol, shares);
                    break;

                default: break;
            }
        }
        /// <summary>
        /// Estimate number of shares, given a kind of operation.
        /// </summary>
        /// <param name="symbol">The symbol to operate.</param>
        /// <param name="order">The kind of order.</param>
        /// <returns>The signed number of shares given the operation.</returns>
        public int PositionShares(string symbol, OrderSignal order)
        {
            int quantity;
            int operationQuantity;

            switch (order)
            {
                case OrderSignal.goLong:
                case OrderSignal.goLongLimit:
                    operationQuantity = CalculateOrderQuantity(symbol, ShareSize[symbol]);
                    quantity = Math.Min(maxOperationQuantity, operationQuantity);
                    break;

                case OrderSignal.goShort:
                case OrderSignal.goShortLimit:
                    operationQuantity = CalculateOrderQuantity(symbol, -ShareSize[symbol]);
                    quantity = Math.Max(-maxOperationQuantity, operationQuantity);
                    break;

                case OrderSignal.closeLong:
                case OrderSignal.closeShort:
                    quantity = -Portfolio[symbol].Quantity;
                    break;

                case OrderSignal.revertToLong:
                case OrderSignal.revertToShort:
                    quantity = -2 * Portfolio[symbol].Quantity;
                    break;

                default:
                    quantity = 0;
                    break;
            }
            return quantity;
        }
        /// <summary>
        /// Handles the On end of algorithm 
        /// </summary>
        public override void OnEndOfAlgorithm()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var s in Symbols)
            {
                sb.Append(s);
                sb.Append(",");
            }
            string symbolsstring = sb.ToString();
            symbolsstring = symbolsstring.Substring(0, symbolsstring.LastIndexOf(",", System.StringComparison.Ordinal));
            string debugstring =
                string.Format(
                    "\nAlgorithm Name: {0}\n Symbols: {1}\n Ending Portfolio Value: {2}\n Start Time: {3} \t {4} \n End Time: {5} \t {6}",
                    this.GetType().Name, symbolsstring, Portfolio.TotalPortfolioValue, startTime, _startDate,
                    DateTime.Now, _endDate);
            Log(debugstring);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QuantConnect.Indicators;
using QuantConnect.Orders;

namespace QuantConnect
{
    class MultiSymbolStrategy : BaseStrategy
    {
        #region Fields

        private decimal _tolerance;
        private decimal _revertPCT;
        private RevertPositionCheck _checkRevertPosition;

        #region made public for debug

        public bool TriggerCrossOverITrend = false;
        public bool TriggerCrossUnderITrend = false;
        public bool ExitFromLong = false;
        public bool ExitFromShort = false;

        Indicator _price;
        public SimpleMovingAverage sma;
        public Momentum SMAMomentum;
        public RollingWindow<decimal> MomentumWindow;
        public bool IsActive = true;
        public int TradeAttempts = 0;
        public OrderStatus Status = OrderStatus.None;

        #endregion made public for debug


        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ITrendStrategy"/> class.
        /// </summary>
        /// <param name="period">The period of the Instantaneous trend.</param>
        public MultiSymbolStrategy(Indicator price, int period, decimal tolerance = 0.001m, decimal revetPct = 1.0015m,
            RevertPositionCheck checkRevertPosition = RevertPositionCheck.vsTrigger)
        {
            _price = price;
            sma = new SimpleMovingAverage(period).Of(price);
            SMAMomentum = new Momentum(2).Of(sma);
            MomentumWindow = new RollingWindow<decimal>(2);

            Position = StockState.noInvested;
            EntryPrice = null;
            ActualSignal = OrderSignal.doNothing;

            _tolerance = tolerance;
            _revertPCT = revetPct;
            _checkRevertPosition = checkRevertPosition;



            SMAMomentum.Updated += (object sender, IndicatorDataPoint updated) =>
            {
                if (SMAMomentum.IsReady) MomentumWindow.Add(SMAMomentum.Current.Value);
                if (MomentumWindow.IsReady) CheckSignal();
            };
        }


        #endregion Constructors
        #region Methods

        /// <summary>
        /// Checks If the strategy throws a operation signal.
        /// </summary>
        /// <returns>An OrderSignal with the proper actualSignal to operate.</returns>
        public override void CheckSignal()
        {
            TriggerCrossOverITrend = MomentumWindow[1] < 0 &&
                                     MomentumWindow[0] > 0 &&
                                     Math.Abs(MomentumWindow[0] - MomentumWindow[1]) >= _tolerance;

            TriggerCrossUnderITrend = MomentumWindow[1] > 0 &&
                                      MomentumWindow[0] < 0 &&
                                      Math.Abs(MomentumWindow[0] - MomentumWindow[1]) >= _tolerance;

            if (_checkRevertPosition == RevertPositionCheck.vsTrigger)
            {
                ExitFromLong = (EntryPrice != null) ? sma + SMAMomentum < EntryPrice / _revertPCT : false;
                ExitFromShort = (EntryPrice != null) ? sma + SMAMomentum > EntryPrice * _revertPCT : false;
            }
            else if (_checkRevertPosition == RevertPositionCheck.vsClosePrice)
            {
                ExitFromLong = (EntryPrice != null) ? _price < EntryPrice / _revertPCT : false;
                ExitFromShort = (EntryPrice != null) ? _price > EntryPrice * _revertPCT : false;
            }

            OrderSignal actualSignal;

            switch (Position)
            {
                case StockState.noInvested:
                    if (TriggerCrossOverITrend) actualSignal = OrderSignal.goLongLimit;
                    else if (TriggerCrossUnderITrend) actualSignal = OrderSignal.goShortLimit;
                    else actualSignal = OrderSignal.doNothing;
                    break;

                case StockState.longPosition:
                    if (TriggerCrossUnderITrend) actualSignal = OrderSignal.closeLong;
                    else if (ExitFromLong) actualSignal = OrderSignal.revertToShort;
                    else actualSignal = OrderSignal.doNothing;
                    break;

                case StockState.shortPosition:
                    if (TriggerCrossOverITrend) actualSignal = OrderSignal.closeShort;
                    else if (ExitFromShort) actualSignal = OrderSignal.revertToLong;
                    else actualSignal = OrderSignal.doNothing;
                    break;

                default: actualSignal = OrderSignal.doNothing;
                    break;
            }
            ActualSignal = actualSignal;
        }

        public void Reset()
        {
            // Not resetting the ITrend increases returns
            sma.Reset();
            SMAMomentum.Reset();
            MomentumWindow.Reset();
        }

        #endregion Methods

    }

}
namespace QuantConnect {

    public interface IStrategy
    {      
        /// <summary>
        /// Checks the for signals.
        /// </summary>
        /// <returns></returns>
        void CheckSignal();
    }

    public abstract class BaseStrategy : IStrategy
    {
        /// <summary>
        /// Indicates what is the actual investing status for the strategy.
        /// </summary>
        public StockState Position;

        /// <summary>
        /// In case the strategy has an position taken, this is the entry price. Null otherwise.
        /// </summary>
        public Nullable<decimal> EntryPrice;

        /// <summary>
        /// The actual signal.
        /// </summary>
        public OrderSignal ActualSignal;

        /// <summary>
        /// Checks the for signals.
        /// </summary>
        public abstract void CheckSignal();
    }
}
namespace QuantConnect {

    public enum StockState
    {
        shortPosition,  // The Portfolio has short position in this bar.
        longPosition,   // The Portfolio has long position in this bar.
        noInvested,     // The Portfolio hasn't any position in this bar.
        orderSent       // An order has been sent in this same bar, skip analysis.
    };

    public enum OrderSignal
    {
        goShort, goLong,                // Entry to the market orders.
        goShortLimit, goLongLimit,      // Entry with limit order.
        closeShort, closeLong,          // Exit from the market orders.
        revertToShort, revertToLong,    // Reverse a position when in the wrong side of the trade.
        doNothing
    };

    public enum RevertPositionCheck
    {
        vsTrigger,
        vsClosePrice,
    }

    public enum PositionInventoryMethod
    {
        Lifo, Fifo
    }

}