| 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);
if (price == 0)
{
continue;
}
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);
if (riskPerUnit == 0)
{
continue;
}
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)
{
if (target.Quantity != 0)
{
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);
}
}
}
}
}
}
}