Overall Statistics |
Total Trades 990 Average Win 0.23% Average Loss -0.22% Compounding Annual Return -1.616% Drawdown 5.300% Expectancy -0.014 Net Profit -1.619% Sharpe Ratio -0.18 Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.04 Alpha -0.01 Beta -0.024 Annual Standard Deviation 0.054 Annual Variance 0.003 Information Ratio -0.089 Tracking Error 0.145 Treynor Ratio 0.404 Total Fees $0.00 |
namespace QuantConnect { // Tracking all the Trade information in a single class public class TradeProfile { //Ticket tracking the open order public OrderTicket OpenTicket, StopTicket; private decimal _risk; private decimal _currentPrice; private int _maximumTradeQuantity; protected decimal _volatility; // Calclate the quantity based on the target risk in dollars. public int Quantity { get { if(_volatility == 0) return 0; long quantity = (long)(_risk / _volatility); if (quantity > _maximumTradeQuantity) return _maximumTradeQuantity; return (int)quantity; } } //What is the stoploss move from current price public decimal DeltaStopLoss { get { return _risk / Quantity; } } //Create a new tradeProfile and limit the maximum risk. public TradeProfile(decimal volatility, decimal risk, decimal currentPrice, decimal maximumTradeSize) { _volatility = volatility; _risk = risk; _currentPrice = currentPrice; _maximumTradeQuantity = (int)(maximumTradeSize/_currentPrice); } } }
namespace QuantConnect { public class BasicTemplateAlgorithm : QCAlgorithm { //Configure which securities you'd like to use: public string[] Symbols = { "EURUSD", "GBPUSD" } ; //Risk in dollars per trade ($ or the quote currency of the assets) public decimal RiskPerTrade = 10; //Roughly how long does our "alpha" take to run public TimeSpan AverageInvestmentPeriod = TimeSpan.FromHours(6.5); //Cap the investment maximum size ($). public decimal MaximumTradeSize = 10000; private Resolution _dataResolution = Resolution.Minute; private Dictionary<Symbol, TradeProfile> _trades; private Slice _data; private Random random = new Random(131); public override void Initialize() { SetStartDate(2015, 1, 1); SetEndDate(2016, 01, 01); SetCash(2500); //Hold a trade profile per asset we're trading. _trades = new Dictionary<Symbol, TradeProfile>(); //Add as many securities as you like. All the data will be passed into the event handler: foreach (var symbol in Symbols) { AddSecurity(SecurityType.Forex, symbol, _dataResolution); //Roughly 4c per 1000 traded, roughly 8c fees per trade. Securities[symbol].FeeModel = new ConstantFeeModel(0.0m); //Set our volatility model: the expected price movement for the day is 3 sigma of the standard deviation Securities[symbol].VolatilityModel = new ThreeSigmaVolatilityModel(_dataResolution.ToTimeSpan(), (int)AverageInvestmentPeriod.TotalMinutes); //Initialize the default _trades.Add(symbol, new TradeProfile(Securities[symbol].VolatilityModel.Volatility, RiskPerTrade, 1m, MaximumTradeSize)); } //Might but bug with the FXCM model? Fees way higher than normal? // SetBrokerageModel(BrokerageName.FxcmBrokerage); var dates = new [] {DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday}; // SUPER SECRET ALHA INDICATOR!!!!! Trade every day at 930, exit at 4pm :D Schedule.On(DateRules.Every(dates), TimeRules.At(09, 30), () => { foreach (var symbol in Symbols) { // ENTER THE MARKET RANDOM DIRECTION...! if (Securities[symbol].VolatilityModel.Volatility == 0) continue; _trades[symbol] = Enter(symbol, RandomOrderDirection()); } }); Schedule.On(DateRules.Every(dates), TimeRules.At(16, 00), () => { foreach (var symbol in Symbols) { // If the stoploss hasn't triggered, quit if (!Portfolio[symbol].Invested) continue; Exit(_trades[symbol]); } }); } /// <summary> /// Generate a random order direction for the "super secret alpha signal" /// </summary> private OrderDirection RandomOrderDirection() { if(random.NextDouble() > 0.5) return OrderDirection.Buy; return OrderDirection.Sell; } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> public override void OnData(Slice data) { _data = data; } /// <summary> /// Enter the market, for a given direction, and symbol: add a stop loss to the trade we make. /// </summary> public TradeProfile Enter(string symbol, OrderDirection orderDirection) { var price = _data[symbol].Close; var trade = new TradeProfile(Securities[symbol].VolatilityModel.Volatility, RiskPerTrade, price, MaximumTradeSize); var direction = (orderDirection == OrderDirection.Buy) ? 1 : -1; trade.OpenTicket = MarketOrder(symbol, direction*trade.Quantity); // Attach a stop loss at the average fill price - stopPrice. var stopPrice = trade.OpenTicket.AverageFillPrice - (direction*trade.DeltaStopLoss); trade.StopTicket = StopMarketOrder(symbol, -direction * trade.Quantity, stopPrice); return trade; } /// <summary> /// Exit the market for a given trade profile /// </summary> public void Exit(TradeProfile trade) { Liquidate(trade.OpenTicket.Symbol); if (trade.StopTicket != null && trade.StopTicket.Status != OrderStatus.Filled) { //If we haven't hit the stop loss then kill it. trade.StopTicket.Cancel(); } } } }
using System; using MathNet.Numerics.Statistics; using QuantConnect.Data; using QuantConnect.Indicators; namespace QuantConnect { public class ThreeSigmaVolatilityModel : IVolatilityModel { private bool _needsUpdate; private decimal _volatility; private DateTime _lastUpdate; private readonly TimeSpan _periodSpan; private readonly object _sync = new object(); private readonly RollingWindow<double> _window; /// <summary> /// Gets the volatility of the security as a percentage /// </summary> public decimal Volatility { get { lock (_sync) { if (_window.Count < 2) { return 0m; } if (_needsUpdate) { _needsUpdate = false; // volatility here is supposed to be a percentage var std = _window.StandardDeviation().SafeDecimalCast(); _volatility = std*3; } } return _volatility; } } /// <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(TimeSpan periodSpan, int periods) { if (periods < 2) throw new ArgumentOutOfRangeException("periods", "'periods' must be greater than or equal to 2."); _periodSpan = periodSpan; _window = new RollingWindow<double>(periods); _lastUpdate = DateTime.MinValue + TimeSpan.FromMilliseconds(periodSpan.TotalMilliseconds * periods); } /// <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) { var timeSinceLastUpdate = data.EndTime - _lastUpdate; if (timeSinceLastUpdate >= _periodSpan) { lock (_sync) { _needsUpdate = true; // we purposefully use security.Price for consistency in our reporting // some streams of data will have trade/quote data, so if we just use // data.Value we could be mixing and matching data streams _window.Add((double)security.Price); } _lastUpdate = data.EndTime; } } } }