Overall Statistics |
Total Trades 271 Average Win 0.06% Average Loss -0.03% Compounding Annual Return 31.147% Drawdown 0.900% Expectancy -0.112 Net Profit 0.285% Sharpe Ratio 1.426 Loss Rate 73% Win Rate 27% Profit-Loss Ratio 2.27 Alpha 0.185 Beta 3.577 Annual Standard Deviation 0.13 Annual Variance 0.017 Information Ratio 1.498 Tracking Error 0.124 Treynor Ratio 0.052 Total Fees $271.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, 11); private DateTime _endDate = new DateTime(2015, 8, 14); 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 } }