Overall Statistics |
Total Trades 6 Average Win 2.82% Average Loss -0.43% Compounding Annual Return 26.854% Drawdown 5.300% Expectancy 1.542 Net Profit 1.974% Sharpe Ratio 1.473 Probabilistic Sharpe Ratio 53.824% Loss Rate 67% Win Rate 33% Profit-Loss Ratio 6.62 Alpha 0.545 Beta -0.939 Annual Standard Deviation 0.168 Annual Variance 0.028 Information Ratio -0.367 Tracking Error 0.186 Treynor Ratio -0.264 Total Fees $5.98 Estimated Strategy Capacity $640000.00 Lowest Capacity Asset EURUSD 8G |
using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// </summary> public class ExampleForexAlphaModel : AlphaModel { private readonly int _fastPeriod; private readonly int _slowPeriod; private readonly Resolution _resolution; private readonly Dictionary<Symbol, SymbolData> _symbolData; /// <summary> /// Initializes a new instance of the <see cref="ExampleForexAlphaModel"/> class /// </summary> /// <param name="fastPeriod">The fast EMA period</param> /// <param name="slowPeriod">The slow EMA period</param> /// <param name="resolution">The resolution of data sent into the EMA indicators</param> public ExampleForexAlphaModel( int fastPeriod = 8, int slowPeriod = 21, Resolution resolution = Resolution.Hour ) { _fastPeriod = fastPeriod; _slowPeriod = slowPeriod; _resolution = resolution; _symbolData = new Dictionary<Symbol, SymbolData>(); Name = $"{nameof(ExampleForexAlphaModel)}({fastPeriod},{slowPeriod},{resolution})"; } /// <summary> /// Determines an insight for each security based on it's current EMA signals /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="data">The new data available</param> /// <returns>The new insights generated</returns> public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data) { foreach (var sd in _symbolData.Values) { if (sd.Security.Price == 0) { continue; } var direction = InsightDirection.Flat; if (sd.EMAFast > sd.EMASlow) { direction = InsightDirection.Up; } // ignore signal for same direction as previous signal if (direction == sd.PreviousDirection) { continue; } var insightPeriod = _resolution.ToTimeSpan().Multiply(_fastPeriod); var insight = Insight.Price(sd.Security.Symbol, insightPeriod, direction); sd.PreviousDirection = insight.Direction; yield return insight; } } /// <summary> /// Event fired each time the we add/remove securities from the data feed. /// This initializes the EMAs for each added security and cleans up the indicator for each removed security. /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { foreach (var added in changes.AddedSecurities) { if (_symbolData.ContainsKey(added.Symbol)) { continue; } _symbolData.Add(added.Symbol, new SymbolData(algorithm, added, _fastPeriod, _slowPeriod, _resolution)); } foreach (var removed in changes.RemovedSecurities) { SymbolData data; if (_symbolData.TryGetValue(removed.Symbol, out data)) { // clean up our consolidator algorithm.SubscriptionManager.RemoveConsolidator(data.Security.Symbol, data.Consolidator); _symbolData.Remove(removed.Symbol); } } } class SymbolData { public InsightDirection? PreviousDirection { get; set; } public readonly Security Security; public readonly IDataConsolidator Consolidator; public readonly ExponentialMovingAverage EMAFast; public readonly ExponentialMovingAverage EMASlow; public SymbolData(QCAlgorithm algorithm, Security security, int fastPeriod, int slowPeriod, Resolution resolution) { Security = security; Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution); algorithm.SubscriptionManager.AddConsolidator(security.Symbol, Consolidator); EMAFast = new ExponentialMovingAverage(fastPeriod); EMASlow = new ExponentialMovingAverage(slowPeriod); algorithm.RegisterIndicator(security.Symbol, EMAFast, Consolidator); algorithm.RegisterIndicator(security.Symbol, EMASlow, Consolidator); } } } }
using System; using System.Collections.Generic; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; namespace QuantConnect.Algorithm.Framework.Portfolio { public class ExampleForexPortfolioConstructionModel : IPortfolioConstructionModel { private readonly decimal _profitTargetPercentage; public readonly decimal _maxEquityPerTradePercentage; private readonly decimal _maxLeverage; private readonly Resolution _resolution; private readonly Dictionary<Symbol, SymbolData> _symbolData; private readonly int _pricePrecision = 4; public ExampleForexPortfolioConstructionModel(decimal profitTargetPercentage, decimal maxEquityPerTradePercentage, decimal maxLeverage, Resolution resolution = Resolution.Hour, int pricePrecision = 4) { _profitTargetPercentage = profitTargetPercentage; _maxEquityPerTradePercentage = maxEquityPerTradePercentage; _maxLeverage = maxLeverage; _resolution = resolution; _symbolData = new Dictionary<Symbol, SymbolData>(); _pricePrecision = pricePrecision; } public IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights) { var targets = new List<IPortfolioTarget>(); foreach (var insight in insights) { var sd = _symbolData[insight.Symbol]; if (sd.Window.Count < SymbolData.QuoteBarWindowLength) { continue; } if (insight.Direction == InsightDirection.Up) { var price = Math.Round(algorithm.Securities[sd.Symbol].Price, _pricePrecision); var stopLoss = Math.Round(sd.Window[1].Low, _pricePrecision); var takeProfit = Math.Round(sd.Window[1].High * (1 + _profitTargetPercentage), _pricePrecision); var maxEquityPerTrade = algorithm.Portfolio.TotalPortfolioValue * _maxLeverage * _maxEquityPerTradePercentage; var maxQuantityEquity = Math.Round(maxEquityPerTrade / price, 0); targets.Add(new ForexPortfolioTarget(sd.Symbol, maxQuantityEquity, price, takeProfit, stopLoss)); } } return targets; } public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { foreach (var added in changes.AddedSecurities) { if (_symbolData.ContainsKey(added.Symbol)) { continue; } _symbolData.Add(added.Symbol, new SymbolData(algorithm, added.Symbol, _resolution)); } foreach (var removed in changes.RemovedSecurities) { if (_symbolData.TryGetValue(removed.Symbol, out SymbolData data)) { algorithm.SubscriptionManager.RemoveConsolidator(data.Symbol, data.Consolidator); _symbolData.Remove(removed.Symbol); } } } private class SymbolData { public const int QuoteBarWindowLength = 2; public Symbol Symbol { get; } public RollingWindow<QuoteBar> Window { get; } public IDataConsolidator Consolidator { get; } public SymbolData(QCAlgorithm algorithm, Symbol symbol, Resolution resolution) { Symbol = symbol; Consolidator = algorithm.ResolveConsolidator(symbol, resolution); algorithm.SubscriptionManager.AddConsolidator(symbol, Consolidator); Window = new RollingWindow<QuoteBar>(QuoteBarWindowLength); Consolidator.DataConsolidated += DataConsolidated; } private void DataConsolidated(object sender, IBaseData consolidated) { var bar = (QuoteBar)consolidated; Window.Add(bar); } } } }
namespace QuantConnect.Algorithm.Framework.Portfolio { public class ForexPortfolioTarget : IPortfolioTarget { public Symbol Symbol { get; } public decimal Quantity { get; } public decimal TakeProfit { get; } public decimal StopLoss { get; } public decimal Price { get; } public ForexPortfolioTarget(Symbol symbol, decimal quantity, decimal price, decimal takeProfit, decimal stopLoss) { Symbol = symbol; Quantity = quantity; Price = price; TakeProfit = takeProfit; StopLoss = stopLoss; } } }
using System.Collections.Generic; namespace QuantConnect.Algorithm.Framework.Selection { public class ExampleForexCurrencySelectionModel : ManualUniverseSelectionModel { public ExampleForexCurrencySelectionModel() : base(new List<Symbol> { Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda) }) { } } }
using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Brokerages; using QuantConnect.Orders; using QuantConnect.Parameters; namespace QuantConnect.Algorithm { public class ExampleForexAlgoFramework : QCAlgorithm { /// <summary> /// Defines our target profit for each trade. /// </summary> [Parameter("ProfitTargetPercentage")] public decimal ProfitTargetPercentage { get; set; } // 0.1m; /// <summary> /// Defines the maximum amount of leverage to apply. /// </summary> [Parameter("MaxLeverage")] public int MaxLeverage { get; set; } // 20; /// <summary> /// Defines the maximum amount of our portfolio's equity to use for a trade. /// </summary> [Parameter("MaxEquityPerTradePercentage")] public decimal MaxEquityPerTradePercentage { get; set; } // 0.25m; /// <summary> /// Defines the maximum percentage of our portfolio's value to risk in a trade. /// </summary> [Parameter("MaxRiskPerTradePercentage")] public decimal MaxRiskPerTradePercentage { get; set; } // 0.05m; private IUniverseSelectionModel _forexSelectionModel; private ExampleForexAlphaModel _forexAlphaModel; private ExampleForexBracketExecutionModel _forexExecutionModel; private ExampleForexPortfolioConstructionModel _forexPortfolioConstructionModel; private ExampleForexRiskManagementModel _forexRiskManagementModel; public override void Initialize() { SetCash(100000); SetStartDate(2019, 4, 1); SetEndDate(2019, 4, 30); SetBrokerageModel(new AlphaStreamsBrokerageModel(AccountType.Margin)); var resolution = Resolution.Hour; UniverseSettings.Resolution = resolution; _forexSelectionModel = new ExampleForexCurrencySelectionModel(); _forexAlphaModel = new ExampleForexAlphaModel(); _forexExecutionModel = new ExampleForexBracketExecutionModel(); _forexPortfolioConstructionModel = new ExampleForexPortfolioConstructionModel(ProfitTargetPercentage, MaxEquityPerTradePercentage, MaxLeverage, resolution); _forexRiskManagementModel = new ExampleForexRiskManagementModel(MaxRiskPerTradePercentage); SetUniverseSelection(_forexSelectionModel); SetAlpha(_forexAlphaModel); SetExecution(_forexExecutionModel); SetPortfolioConstruction(_forexPortfolioConstructionModel); SetRiskManagement(_forexRiskManagementModel); } public override void OnOrderEvent(OrderEvent orderEvent) { _forexExecutionModel.OnOrderEvent(orderEvent); } public override void OnEndOfAlgorithm() { Liquidate(); } } }
using QuantConnect.Algorithm.Framework.Portfolio; using System; using System.Linq; using System.Collections.Generic; namespace QuantConnect.Algorithm.Framework.Risk { public class ExampleForexRiskManagementModel : RiskManagementModel { private readonly decimal _maxRiskPerTradePercentage; private readonly int _pricePrecision = 4; public ExampleForexRiskManagementModel(decimal maxRiskPerTradePercentage, int pricePrecision = 4) { _maxRiskPerTradePercentage = maxRiskPerTradePercentage; _pricePrecision = pricePrecision; } public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets) { var adjustedTargets = new List<IPortfolioTarget>(); var forexTargets = targets.OfType<ForexPortfolioTarget>().ToList(); foreach (var target in forexTargets) { var maxRiskPerTrade = Math.Round(algorithm.Portfolio.TotalPortfolioValue * _maxRiskPerTradePercentage, 0); var riskPerUnit = Math.Round(target.Price - target.StopLoss, _pricePrecision); var maxQuantityRisk = Math.Round(maxRiskPerTrade / riskPerUnit, 0); var quantity = Math.Round(Math.Min(target.Quantity, maxQuantityRisk), 0); adjustedTargets.Add(new ForexPortfolioTarget(target.Symbol, quantity, target.Price, target.TakeProfit, target.StopLoss)); } return adjustedTargets; } } }
using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Orders; namespace QuantConnect.Algorithm.Framework.Execution { public class ExampleForexBracketExecutionModel : ExecutionModel { public const string BuyOrderTag = "ForexBuyOrder"; public const string TakeProfitOrderTag = "ForexTakeProfit"; public const string StopLossOrderTag = "ForexStopLoss"; private QCAlgorithm _algorithm; public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets) { _algorithm = algorithm; foreach (var target in targets) { var forexTarget = (target as ForexPortfolioTarget); if (target == null) { continue; } if (!algorithm.Portfolio[target.Symbol].Invested) { var marketBuyTicket = algorithm.MarketOrder(forexTarget.Symbol, forexTarget.Quantity, false, BuyOrderTag); algorithm.LimitOrder(forexTarget.Symbol, -forexTarget.Quantity, forexTarget.TakeProfit, TakeProfitOrderTag); algorithm.StopMarketOrder(forexTarget.Symbol, -forexTarget.Quantity, forexTarget.StopLoss, StopLossOrderTag); algorithm.Debug($"Order time: {algorithm.Time}, tag: {marketBuyTicket.Tag}, quantity: {marketBuyTicket.Quantity}, fill price: {marketBuyTicket.AverageFillPrice}, status: {marketBuyTicket.Status}"); } } } public void OnOrderEvent(OrderEvent orderEvent) { var orderFromEvent = _algorithm.Transactions.GetOrderById(orderEvent.OrderId); var orderTicketFromEvent = _algorithm.Transactions.GetOrderTicket(orderEvent.OrderId); var openOrderTickets = _algorithm.Transactions.GetOrderTickets(); // OCO if (orderTicketFromEvent.Status == OrderStatus.Filled) { // Cancel stop loss order if (orderTicketFromEvent.Tag == TakeProfitOrderTag) { foreach (var ticket in openOrderTickets) { if (ticket.Tag == StopLossOrderTag) { _algorithm.Debug($"event time: {orderEvent.UtcTime}, event: {orderTicketFromEvent.Tag}, cancelled order: {ticket.Tag}"); _algorithm.Transactions.CancelOrder(ticket.OrderId); } } } // Cancel take profit order else if (orderTicketFromEvent.Tag == StopLossOrderTag) { foreach (var ticket in openOrderTickets) { if (ticket.Tag == TakeProfitOrderTag) { _algorithm.Debug($"event time: {orderEvent.UtcTime}, event: {orderTicketFromEvent.Tag}, cancelled order: {ticket.Tag}"); _algorithm.Transactions.CancelOrder(ticket.OrderId); } } } } } } }