| Overall Statistics |
|
Total Trades 278 Average Win 0.08% Average Loss -0.04% Compounding Annual Return 39.396% Drawdown 2.400% Expectancy -0.035 Net Profit 0.609% Sharpe Ratio 1.87 Loss Rate 70% Win Rate 30% Profit-Loss Ratio 2.21 Alpha -0.045 Beta -0.842 Annual Standard Deviation 0.141 Annual Variance 0.02 Information Ratio 3.68 Tracking Error 0.171 Treynor Ratio -0.312 Total Fees $278.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
}
}