Overall Statistics |
Total Trades 2854 Average Win 0.66% Average Loss -1.25% Compounding Annual Return 50.858% Drawdown 23.300% Expectancy 0.050 Net Profit 119.277% Sharpe Ratio 1.187 Loss Rate 31% Win Rate 69% Profit-Loss Ratio 0.53 Alpha 0.423 Beta -0.249 Annual Standard Deviation 0.33 Annual Variance 0.109 Information Ratio 0.766 Tracking Error 0.35 Treynor Ratio -1.573 Total Fees $35461.73 |
using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Orders.Fills; using QuantConnect.Securities; using System; using System.Linq; namespace QuantConnect.Algorithm.CSharp { public sealed class ImmediateOptimisticStopFillModel : ImmediateFillModel { /// <summary> /// Default limit order fill model in the base security class. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="MarketFill(Security, MarketOrder)"/> public override OrderEvent LimitFill(Security asset, LimitOrder order) { //Initialise; var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: var prices = GetPrices(asset, order.Direction); //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (prices.Low < order.LimitPrice) { //Set order fill: fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = order.LimitPrice; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (prices.High > order.LimitPrice) { fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = order.LimitPrice; } break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; } /// <summary> /// Default stop fill model implementation in base class security. (Stop Market Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="MarketFill(Security, MarketOrder)"/> /// <seealso cref="SecurityTransactionModel.LimitFill"/> public override OrderEvent StopMarketFill(Security asset, StopMarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: var prices = GetPrices(asset, order.Direction); //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (prices.Low < order.StopPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = order.StopPrice - slip; } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (prices.High > order.StopPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = order.StopPrice + slip; } break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; } /// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="direction">The order direction, decides whether to pick bid or ask</param> private Prices GetPrices(Security asset, OrderDirection direction) { var low = asset.Low; var high = asset.High; var open = asset.Open; var close = asset.Close; var current = asset.Price; if (direction == OrderDirection.Hold) { return new Prices(current, open, high, low, close); } // Only fill with data types we are subscribed to var subscriptionTypes = asset.Subscriptions.Select(x => x.Type).ToList(); // Tick var tick = asset.Cache.GetData<Tick>(); if (subscriptionTypes.Contains(typeof(Tick)) && tick != null) { var price = direction == OrderDirection.Sell ? tick.BidPrice : tick.AskPrice; if (price != 0m) { return new Prices(price, 0, 0, 0, 0); } // If the ask/bid spreads are not available for ticks, try the price price = tick.Price; if (price != 0m) { return new Prices(price, 0, 0, 0, 0); } } // Quote var quoteBar = asset.Cache.GetData<QuoteBar>(); if (subscriptionTypes.Contains(typeof(QuoteBar)) && quoteBar != null) { var bar = direction == OrderDirection.Sell ? quoteBar.Bid : quoteBar.Ask; if (bar != null) { return new Prices(bar); } } // Trade var tradeBar = asset.Cache.GetData<TradeBar>(); if (subscriptionTypes.Contains(typeof(TradeBar)) && tradeBar != null) { return new Prices(tradeBar); } return new Prices(current, open, high, low, close); } /// <summary> /// Determines if the exchange is open using the current time of the asset /// </summary> private static bool IsExchangeOpen(Security asset) { if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime)) { // if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days) var currentBar = asset.GetLastData(); if (asset.LocalTime.Date != currentBar.EndTime.Date || !asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false)) { return false; } } return true; } private class Prices { public readonly decimal Current; public readonly decimal Open; public readonly decimal High; public readonly decimal Low; public readonly decimal Close; public Prices(IBar bar) : this(bar.Close, bar.Open, bar.High, bar.Low, bar.Close) { } public Prices(decimal current, decimal open, decimal high, decimal low, decimal close) { Current = current; Open = open == 0 ? current : open; High = high == 0 ? current : high; Low = low == 0 ? current : low; Close = close == 0 ? current : close; } } } }
using Accord.MachineLearning.VectorMachines; using Accord.MachineLearning.VectorMachines.Learning; using Accord.Statistics.Kernels.Sparse; using QuantConnect.Securities; using System; using System.Collections.Generic; using System.Linq; using System.Net; using QuantConnect.Data; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Data.UniverseSelection; namespace QuantConnect.Algorithm.CSharp { public partial class GapReversalAlgorithm : QCAlgorithm { const Resolution _resolution = Resolution.Minute; const decimal _leverage = 4; //private const string _tickers = "AXP AAPL BA CAT CSCO CVX XOM GE GS HD IBM INTC JNJ KO JPM MCD MMM MRK MSFT NKE PFE PG TRV UNH UTX VZ V WMT DIS DWDP"; /*private const string _tickers = @" ADAP AKTX AMRN AMFW AZN AV BCS BBL BP BTI BT CUK DEO GSK GWPH HSBC IHG LYG LXFR MTP MTFB NGG PSO PUK RELX RIO RBS SHPG SNN SMMT UL VOD WPPGY ";*/ private readonly Dictionary<Symbol, Tradable> _tradables = new Dictionary<Symbol, Tradable>(); private static GapReversalAlgorithm _instance; public override void Initialize() { _instance = this; SetCash(100000); SetStartDate(2016, 1, 1); /* var tickers = _tickers.Split(new char[] { '\n', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (var t in tickers) { var ticker = t.Trim(); if (ticker == "") continue; var tradable = new Tradable(ticker); _tradables[tradable.Symbol] = tradable; } */ var someSecurity = AddEquity("SPY", _resolution, leverage: _leverage); Schedule.On(DateRules.EveryDay(someSecurity.Symbol), TimeRules.BeforeMarketClose(someSecurity.Symbol, 10), ClosePositions); Schedule.On(DateRules.EveryDay(someSecurity.Symbol), TimeRules.AfterMarketOpen(someSecurity.Symbol, -1), PrepareForNewDay); SetUpUniverse(); } private void SetUpUniverse() { UniverseSettings.MinimumTimeInUniverse = TimeSpan.FromDays(30); UniverseSettings.Leverage = _leverage; UniverseSettings.Resolution = _resolution; AddUniverse(CoarseUniverseSelection); } private IEnumerable<Symbol> CoarseUniverseSelection(IEnumerable<CoarseFundamental> coarse) { var eligible = coarse .Where(x => x.HasFundamentalData) .Where(x => x.Price > 30 && x.Price < 200) .Where(x => x.Volume > 10000) .OrderByDescending(x => x.DollarVolume) .Select(x => x.Symbol); Debug("Max coarse universe size " + eligible.Count()); return eligible.Take(100); } public override void OnSecuritiesChanged(SecurityChanges changes) { foreach (var change in changes.RemovedSecurities) { _tradables.Remove(change.Symbol); } foreach (var change in changes.AddedSecurities) { if (!_tradables.ContainsKey(change.Symbol)) { var tradable = new Tradable(change); _tradables[change.Symbol] = tradable; } } } public override void OnData(Slice slice) { foreach (var bar in slice.Bars) { Tradable tradable; if (_tradables.TryGetValue(bar.Key, out tradable)) { tradable.OnData(bar.Value); } } } private decimal _allocationsToday; private void PrepareForNewDay() { _allocationsToday = 0; } private decimal RequestAllocationToday() { if (_allocationsToday >= _leverage - 1) return 0; decimal allocationSize = 1; _allocationsToday += allocationSize; return allocationSize; } private void ClosePositions() { foreach (var tradable in _tradables.Values) tradable.ExitBeforeClose(); } private class Tradable { public readonly Security Security; public Symbol Symbol { get { return Security.Symbol; } } private readonly RollingWindow<Gap> _gaps = new RollingWindow<Gap>(2); private decimal _allocation; private bool _hasPosition; private bool _canEnter = false; private sealed class Gap { public readonly decimal Open; public readonly decimal PrevClose; public decimal Change { get { return Open / PrevClose - 1; } } public Gap(decimal open, decimal close) { Open = open; PrevClose = close; } } public Tradable(Security sec) { Security = sec; //Security.FeeModel = new ConstantFeeTransactionModel(0); /* var bars = _instance.History(Symbol, 250 * 3, Resolution.Daily); foreach (var bar in bars) { OnData(bar); } */ } public Tradable(string ticker) : this(_instance.AddEquity(ticker, _resolution, leverage: _leverage)) { } private decimal _lastClose; private DateTime _lastBar; private bool _anyBar; public void OnData(TradeBar bar) { if (!_anyBar) { _anyBar = true; } if (bar.Time.Day != _lastBar.Day || _lastBar == DateTime.MinValue) { _lastBar = bar.Time; OnNewDay(bar); } bool gapClosed = false; int dir = -Math.Sign(_gaps[0].Change); if (dir > 0) { if (bar.Close >= _gaps[0].PrevClose) { gapClosed = true; } } else if (dir < 0) { if (bar.Close <= _gaps[0].PrevClose) { gapClosed = true; } } if (_hasPosition) { if (gapClosed) { _hasPosition = false; _instance.Liquidate(Symbol); //TODO: relinquish allocation? } } else if (_canEnter) { if (gapClosed) _canEnter = false; if (_canEnter && _allocation == 0) { _canEnter = false; _allocation = _instance.RequestAllocationToday(); if (_allocation > 0) { _hasPosition = true; _instance.SetHoldings(Symbol, dir * _allocation); } } } _lastClose = bar.Close; } private void OnNewDay(TradeBar bar) { if (_lastClose == 0) _lastClose = bar.Open; _gaps.Add(new Gap(bar.Open, _lastClose)); if (_gaps.IsReady) { //hardcoded rule now for testing var change1 = _gaps[0].Change; var change2 = _gaps[1].Change; _canEnter = Math.Sign(change1) == Math.Sign(change2); if (_canEnter) { _canEnter = Math.Abs(change1 + change2) > 0.5m / 100 && Math.Abs(change1) > 0.25m / 100 && Math.Abs(change2) > 0.25m / 100; } } } public void ExitBeforeClose() { try { if (_hasPosition) { _instance.Liquidate(Symbol); } } finally { _canEnter = false; _hasPosition = false; _allocation = 0; } } } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using QuantConnect.Indicators; using System; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// This indicator computes the n-period population standard deviation. /// </summary> public class StandardDeviationOverflowSafe : Variance { /// <summary> /// Initializes a new instance of the StandardDeviation class with the specified period. /// /// Evaluates the standard deviation of samples in the lookback period. /// On a dataset of size N will use an N normalizer and would thus be biased if applied to a subset. /// </summary> /// <param name="period">The sample size of the standard deviation</param> public StandardDeviationOverflowSafe(int period) : this("STD" + period, period) { } /// <summary> /// Initializes a new instance of the StandardDeviation class with the specified name and period. /// /// Evaluates the standard deviation of samples in the lookback period. /// On a dataset of size N will use an N normalizer and would thus be biased if applied to a subset. /// </summary> /// <param name="name">The name of this indicator</param> /// <param name="period">The sample size of the standard deviation</param> public StandardDeviationOverflowSafe(string name, int period) : base(name, period) { } /// <summary> /// Gets a flag indicating when this indicator is ready and fully initialized /// </summary> public override bool IsReady { get { return Samples >= Period; } } /// <summary> /// Computes the next value of this indicator from the given state /// </summary> /// <param name="input">The input given to the indicator</param> /// <param name="window">The window for the input history</param> /// <returns>A new value for this indicator</returns> protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input) { double val = Math.Sqrt((double)base.ComputeNextValue(window, input)); return (decimal)val.Clamp(_min, _max); } private static readonly double _max = (double)decimal.MaxValue * 0.01; private static readonly double _min = (double)decimal.MinValue * 0.01; } }
using System; using System.Collections.Generic; using System.Reflection; namespace QuantConnect.Algorithm.CSharp { public static class Xtend { public static FieldInfo GetPrivateField(Type type, string name) { return type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance); } public static FieldInfo GetPrivateField<MyType>(this MyType dummy, string name) { return GetPrivateField(typeof(MyType), name); } public static object GetPrivateFieldValue(Type type, object instance, string name) { var fieldInfo = GetPrivateField(type, name); if (fieldInfo == null) throw new InvalidOperationException("Unable to find field with name " + name); return fieldInfo.GetValue(instance); } public static object GetPrivateFieldValue<MyType>(this MyType instance, string name) { return GetPrivateFieldValue(typeof(MyType), instance, name); } public static T Clamp<T>(this T val, T min, T max) where T : IComparable { XMath.Clamp(ref val, min, max); return val; } public static void Shuffle<T>(this IList<T> list, Random rng) { int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } public static int Argmax(this IList<int> list) { int maxIndex = list.Count - 1; var max = list[maxIndex]; for (int index = list.Count - 2; index >= 0; --index) { var x = list[index]; if (x > max) { maxIndex = index; max = x; } } return maxIndex; } public static int Argmin(this IList<int> list) { int minIndex = list.Count - 1; var min = list[minIndex]; for (int index = list.Count - 2; index >= 0; --index) { var x = list[index]; if (x < min) { minIndex = index; min = x; } } return minIndex; } public static int Argmax(this IList<double> list) { int maxIndex = list.Count - 1; var max = list[maxIndex]; for (int index = list.Count - 2; index >= 0; --index) { var x = list[index]; if (x > max) { maxIndex = index; max = x; } } return maxIndex; } public static int Argmin(this IList<double> list) { int minIndex = list.Count - 1; var min = list[minIndex]; for (int index = list.Count - 2; index >= 0; --index) { var x = list[index]; if (x < min) { minIndex = index; min = x; } } return minIndex; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace QuantConnect.Algorithm.CSharp { public static class XMath { public static bool IsPowerOfTwo(ulong x) { return (x & (x - 1)) == 0; } public static int Log2(int v) { int r = 0xFFFF - v >> 31 & 0x10; v >>= r; int shift = 0xFF - v >> 31 & 0x8; v >>= shift; r |= shift; shift = 0xF - v >> 31 & 0x4; v >>= shift; r |= shift; shift = 0x3 - v >> 31 & 0x2; v >>= shift; r |= shift; r |= (v >> 1); return r; } public static void Clamp<T>(ref T val, T min, T max) where T : IComparable { if (min.CompareTo(val) > 0) val = min; else if (max.CompareTo(val) < 0) val = max; } } }