Overall Statistics |
Total Trades 129 Average Win 2.80% Average Loss -2.36% Compounding Annual Return 200.845% Drawdown 22.100% Expectancy 0.266 Net Profit 44.052% Sharpe Ratio 2.198 Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.19 Alpha 0.832 Beta -0.109 Annual Standard Deviation 0.378 Annual Variance 0.143 Information Ratio 2.065 Tracking Error 0.395 Treynor Ratio -7.592 Total Fees $377.15 |
namespace QuantConnect { /// <summary> /// Basic template algorithm simply initializes the date range and cash /// </summary> public class ForexRiskManagerExample : QCAlgorithm { List<Symbol> forexPairs = new List<Symbol>(); Dictionary<Symbol, CrossingMovingAverages> MovingAverageCross = new Dictionary<QuantConnect.Symbol, CrossingMovingAverages>(); private const decimal _leverage = 50m; // How much of the total strategy equity can be at risk as maximum in all trades. private const decimal _maxExposure = 0.8m; // How much of the total strategy equity can be at risk in a single trade. private const decimal _maxExposurePerTrade = 0.25m; // The max strategy equity proportion to put at risk in a single operation. private const decimal _riskPerTrade = 0.03m; // Smallest lot private LotSize _lotSize = LotSize.Mini; FxRiskManagment RiskManager; public override void Initialize() { SetStartDate(2015, 01, 01); //Set Start Date SetEndDate(2015, 04, 30); //Set End Date SetCash(10000); //Set Strategy Cash forexPairs.Add(AddForex("EURUSD", Resolution.Minute, market: "oanda", leverage: 50).Symbol); forexPairs.Add(AddForex("USDJPY", Resolution.Minute, market: "oanda", leverage: 50).Symbol); RiskManager = new FxRiskManagment(Portfolio, _riskPerTrade, _maxExposurePerTrade, _maxExposure, _lotSize); foreach (var pair in forexPairs) { Securities[pair].VolatilityModel = new ThreeSigmaVolatilityModel(STD(pair, 12 * 60, Resolution.Minute)); var fast_moving_average = new ExponentialMovingAverage(1440).Of(Identity(pair)); var slow_moving_average = new LinearWeightedMovingAverage(1440).Of(Identity(pair)); MovingAverageCross[pair] = new CrossingMovingAverages(fast_moving_average, slow_moving_average); } SetWarmUp(TimeSpan.FromMinutes(1440)); SetBrokerageModel(Brokerages.BrokerageName.OandaBrokerage); } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { if (IsWarmingUp) return; foreach (var pair in forexPairs) { if (!data.ContainsKey(pair) || !MovingAverageCross[pair].IsReady) continue; var signal = MovingAverageCross[pair].Signal; if ( signal == CrossingMovingAveragesSignals.FastCrossSlowFromAbove || signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow) { if ((Portfolio[pair].IsLong && signal == CrossingMovingAveragesSignals.FastCrossSlowFromAbove) || (Portfolio[pair].IsShort && signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow)) { Liquidate(pair); } else if (!Portfolio[pair].Invested) { var actualAction = (signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow) ? AgentAction.GoLong : AgentAction.GoShort; var entryValues = RiskManager.CalculateEntryOrders(pair, actualAction); if (entryValues.Item1 == 0) continue; var ticket = MarketOrder(pair, entryValues.Item1); StopMarketOrder(pair, -entryValues.Item1, entryValues.Item2); } } } RiskManager.UpdateTrailingStopOrders(); } } }
namespace QuantConnect { public enum CrossingMovingAveragesSignals { Bullish = 1, FastCrossSlowFromAbove = -2, Bearish = -1, FastCrossSlowFromBelow = 2, } public class CrossingMovingAverages { CompositeIndicator<IndicatorDataPoint> _moving_average_difference; public CrossingMovingAveragesSignals Signal { get; private set; } private bool _isReady; int _lastSignal; public bool IsReady { get { return _isReady; } } public CrossingMovingAverages(IndicatorBase<IndicatorDataPoint> fast_moving_average, IndicatorBase<IndicatorDataPoint> slow_moving_average) { _moving_average_difference = fast_moving_average.Minus(slow_moving_average); _moving_average_difference.Updated += ma_Updated; } private void ma_Updated(object sender, IndicatorDataPoint updated) { if (!_isReady) { _isReady = _moving_average_difference.Right.IsReady; return; } var actualSignal = Math.Sign(_moving_average_difference); if (actualSignal == _lastSignal || _lastSignal == 0) { Signal = (CrossingMovingAveragesSignals)actualSignal; } else if (_lastSignal == -1 && actualSignal == 1) { Signal = CrossingMovingAveragesSignals.FastCrossSlowFromBelow; } else if (_lastSignal == 1 && actualSignal == -1) { Signal = CrossingMovingAveragesSignals.FastCrossSlowFromAbove; } _lastSignal = actualSignal; } } }
namespace QuantConnect { public enum LotSize { Standard = 100000, Mini = 10000, Micro = 1000, Nano = 100, } public enum AgentAction { GoShort = -1, DoNothing = 0, GoLong = 1 } public class FxRiskManagment { // Maximum equity proportion to put at risk in a single operation. private decimal _riskPerTrade; // Maximum equity proportion at risk in open positions in a given time. private decimal _maxExposure; // Maximum equity proportion at risk in a single trade. private decimal _maxExposurePerTrade; private int _lotSize; private int _minQuantity; private SecurityPortfolioManager _portfolio; /// <summary> /// Initializes a new instance of the <see cref="FxRiskManagment"/> class. /// </summary> /// <param name="portfolio">The QCAlgorithm Portfolio.</param> /// <param name="riskPerTrade">The max risk per trade.</param> /// <param name="maxExposurePerTrade">The maximum exposure per trade.</param> /// <param name="maxExposure">The maximum exposure in all trades.</param> /// <param name="lotsize">The minimum quantity to trade.</param> /// <exception cref="System.NotImplementedException">The pairs should be added to the algorithm before initialize the risk manager.</exception> public FxRiskManagment(SecurityPortfolioManager portfolio, decimal riskPerTrade, decimal maxExposurePerTrade, decimal maxExposure, LotSize lotsize = LotSize.Micro, int minQuantity = 5) { _portfolio = portfolio; if (_portfolio.Securities.Count == 0) { throw new NotImplementedException("The pairs should be added to the algorithm before initialize the risk manager."); } this._riskPerTrade = riskPerTrade; _maxExposurePerTrade = maxExposurePerTrade; this._maxExposure = maxExposure; _lotSize = (int)lotsize; _minQuantity = minQuantity; } /// <summary> /// Calculates the entry orders and stop-loss price. /// </summary> /// <param name="pair">The Forex pair Symbol.</param> /// <param name="action">The order direction.</param> /// <returns>a Tuple with the quantity as Item1 and the stop-loss price as Item2. If quantity is zero, then means that no trade must be done.</returns> public Tuple<int, decimal> CalculateEntryOrders(Symbol pair, AgentAction action) { // If exposure is greater than the max exposure, then return zero. if (_portfolio.TotalMarginUsed > _portfolio.TotalPortfolioValue * _maxExposure) { return Tuple.Create(0, 0m); } var closePrice = _portfolio.Securities[pair].Price; var leverage = _portfolio.Securities[pair].Leverage; var exchangeRate = _portfolio.Securities[pair].QuoteCurrency.ConversionRate; var volatility = _portfolio.Securities[pair].VolatilityModel.Volatility; // Estimate the maximum entry order quantity given the risk per trade. var moneyAtRisk = _portfolio.TotalPortfolioValue * _riskPerTrade; var maxQuantitybyRisk = moneyAtRisk / (volatility * exchangeRate); // Estimate the maximum entry order quantity given the exposure per trade. var maxBuySize = Math.Min(_portfolio.MarginRemaining, _portfolio.TotalPortfolioValue * _maxExposurePerTrade) * leverage; var maxQuantitybyExposure = maxBuySize / (closePrice * exchangeRate); // The final quantity is the lowest of both. var quantity = (int)(Math.Round(Math.Min(maxQuantitybyRisk, maxQuantitybyExposure) / _lotSize, 0) * _lotSize); // If the final quantity is lower than the minimum quantity of the given lot size, then return zero. if (quantity < _lotSize * _minQuantity) return Tuple.Create(0, 0m); quantity = action == AgentAction.GoLong ? quantity : -quantity; var stopLossPrice = closePrice + (action == AgentAction.GoLong ? -volatility : volatility); return Tuple.Create(quantity, stopLossPrice); } /// <summary> /// Updates the stop-loss price of all open StopMarketOrders. /// </summary> public void UpdateTrailingStopOrders() { // Get all the spot-loss orders. var openStopLossOrders = _portfolio.Transactions.GetOrderTickets(o => o.OrderType == OrderType.StopMarket && o.Status == OrderStatus.Submitted); foreach (var ticket in openStopLossOrders) { var stopLossPrice = ticket.SubmitRequest.StopPrice; var volatility = _portfolio.Securities[ticket.Symbol].VolatilityModel.Volatility; var actualPrice = _portfolio.Securities[ticket.Symbol].Price; // The StopLossOrder has the opposite direction of the original order. var originalOrderDirection = ticket.Quantity > 0 ? OrderDirection.Sell : OrderDirection.Buy; var newStopLossPrice = actualPrice + (volatility * (originalOrderDirection == OrderDirection.Buy ? -1 : 1)); if ((originalOrderDirection == OrderDirection.Buy && newStopLossPrice > stopLossPrice) || (originalOrderDirection == OrderDirection.Sell && newStopLossPrice < stopLossPrice)) { ticket.Update(new UpdateOrderFields { StopPrice = newStopLossPrice }); } } } } }
namespace QuantConnect { /// <summary> /// Provides an implementation of <see cref="IVolatilityModel"/> that computes the /// relative standard deviation as the volatility of the security /// </summary> public class ThreeSigmaVolatilityModel : IVolatilityModel { private readonly TimeSpan _periodSpan; private StandardDeviation _standardDeviation; /// <summary> /// Gets the volatility of the security as a percentage /// </summary> public decimal Volatility { get { return _standardDeviation * 2.5m; } } /// <summary> /// Initializes a new instance of the <see cref="QuantConnect.Securities.RelativeStandardDeviationVolatilityModel"/> class /// </summary> /// <param name="periodSpan">The time span representing one 'period' length</param> /// <param name="periods">The nuber of 'period' lengths to wait until updating the value</param> public ThreeSigmaVolatilityModel(StandardDeviation standardDeviation) { _standardDeviation = standardDeviation; _periodSpan = TimeSpan.FromMinutes(standardDeviation.Period); } /// <summary> /// Updates this model using the new price information in /// the specified security instance /// </summary> /// <param name="security">The security to calculate volatility for</param> /// <param name="data"></param> public void Update(Security security, BaseData data) { } public IEnumerable<HistoryRequest> GetHistoryRequirements(Security security, DateTime utcTime) { return Enumerable.Empty<HistoryRequest>(); } } }