Overall Statistics |
Total Trades 117 Average Win 0.02% Average Loss -0.02% Compounding Annual Return -98.082% Drawdown 0.500% Expectancy -0.488 Net Profit -0.472% Sharpe Ratio 0 Loss Rate 79% Win Rate 21% Profit-Loss Ratio 1.40 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $117.00 |
namespace QuantConnect { class MultisymbolAlgorithm : QCAlgorithm { #region "Variables" private DateTime startTime = DateTime.Now; private DateTime _startDate = new DateTime(2015, 8, 14); private DateTime _endDate = new DateTime(2015, 8, 14); private decimal _portfolioAmount = 25000; /* +-------------------------------------------------+ * |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. /* +-------------------------------------------------+*/ string[] symbolarray = new string[] {"AAPL", "NFLX", "AMZN", "SPY"}; List<Symbol> Symbols = new List<Symbol>(); // 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(); #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(new Symbol(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) { bool isMarketAboutToClose = !theMarket.DateTimeIsOpen(Time.AddMinutes(10)); OrderSignal actualOrder = OrderSignal.doNothing; foreach (string symbol in Symbols) { // 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(symbol); } // Check if the market is about to close and noOvernight is true. if (noOvernight && isMarketAboutToClose) { actualOrder = ClosePositions(symbol); } else { // Now check if there is some signal and execute the strategy. actualOrder = Strategy[symbol].ActualSignal; } ExecuteStrategy(symbol, actualOrder); } } } public override void OnOrderEvent(OrderEvent orderEvent) { string symbol = orderEvent.Symbol; int portfolioPosition = Portfolio[symbol].Quantity; var actualTicket = Transactions.GetOrderTickets(t => t.OrderId == orderEvent.OrderId).Single(); var actualOrder = Transactions.GetOrderById(orderEvent.OrderId); switch (orderEvent.Status) { case OrderStatus.Submitted: Strategy[symbol].Position = StockState.orderSent; Log("New order submitted: " + actualOrder.ToString()); break; case OrderStatus.PartiallyFilled: Log("Order partially filled: " + actualOrder.ToString()); Log("Canceling order"); actualTicket.Cancel(); //do { } //while (actualTicket.GetMostRecentOrderResponse().IsSuccess); goto case OrderStatus.Filled; case OrderStatus.Filled: if (portfolioPosition > 0) Strategy[symbol].Position = StockState.longPosition; else if (portfolioPosition < 0) Strategy[symbol].Position = StockState.shortPosition; else Strategy[symbol].Position = StockState.noInvested; Strategy[symbol].EntryPrice = actualTicket.AverageFillPrice; Log("Order filled: " + actualOrder.ToString()); break; case OrderStatus.Canceled: Log("Order successfully canceled: " + actualOrder.ToString()); break; default: break; } } /// <summary> /// Checks if the limits order are filled, and updates the ITrenStrategy object and the /// LastOrderSent dictionary. /// If the limit order aren't filled, then cancels the order and send a market order. /// </summary> /// <param name="symbol">The symbol.</param> /// <param name="lastOrder">The last order.</param> private void CheckLimitOrderStatus(string symbol) { // Pick the submitted limit tickets for the symbol. var actualSubmittedTicket = Transactions.GetOrderTickets(t => t.Symbol == symbol && t.OrderType == OrderType.Limit && t.Status == OrderStatus.Submitted); // If there is none, return. if (actualSubmittedTicket.Count() == 0) return; // if there is more than one, stop the algorithm, something is wrong. else if (actualSubmittedTicket.Count() != 1) throw new ApplicationException("More than one submitted limit order"); Log("||| Cancel Limit order and send a market order"); // Now, define the ticket to handle the actual OrderTicket. var actualTicket = actualSubmittedTicket.Single(); // Retrieve the operation quantity. int shares = actualTicket.Quantity; // Cancel the order. actualTicket.Cancel(); // Send a market order. MarketOrder(symbol, shares); } 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> /// <param name="data">The actual TradeBar data.</param> private void ExecuteStrategy(string symbol, OrderSignal actualOrder) { // Define the operation size. int shares = PositionShares(symbol, actualOrder); switch (actualOrder) { case OrderSignal.goLong: case OrderSignal.goShort: case OrderSignal.goLongLimit: case OrderSignal.goShortLimit: Log("===> Entry to Market"); 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. LimitOrder(symbol, shares, limitPrice); break; case OrderSignal.closeLong: case OrderSignal.closeShort: Log("<=== Closing Position"); // Send the order. MarketOrder(symbol, shares); break; case OrderSignal.revertToLong: case OrderSignal.revertToShort: Log("<===> Reverting Position"); // 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; } } }
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; #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 } }