Overall Statistics
Total Trades
58
Average Win
0.09%
Average Loss
-0.23%
Compounding Annual Return
-12.201%
Drawdown
2.600%
Expectancy
-0.379
Net Profit
-2.487%
Sharpe Ratio
-4.238
Loss Rate
55%
Win Rate
45%
Profit-Loss Ratio
0.38
Alpha
-0.104
Beta
-0.015
Annual Standard Deviation
0.024
Annual Variance
0.001
Information Ratio
0.626
Tracking Error
0.096
Treynor Ratio
6.985
Total Fees
$185.65
/*
 * 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 System.Collections.Generic;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.CSharp;
using System.Linq;
using System;

namespace QuantConnect {
	public partial class QuantFramework : QCAlgorithm {

		//Cap the investment maximum size ($).
		public decimal maxTradeValueSizePerAsset = 5000;

		private Resolution _dataResolution = Resolution.Minute;
		private Dictionary<Symbol, TradingAsset> _tradingAssets = new Dictionary<Symbol, TradingAsset>();

		BackTestParameters backTestParameters;

		UniverseSetup universeSetup;

		public override void Initialize() {
			bool isLocal = false;

			backTestParameters = new BackTestParameters(this, _dataResolution);

			universeSetup = new UniverseSetup(this, IncludeTradingAssets);

			if (isLocal) {
				backTestParameters.setUpLocal();
				universeSetup.setUpLocal();
			} else {
				backTestParameters.setUp();
				universeSetup.setUp();
			}
		}

		IEnumerable<Symbol> IncludeTradingAssets() {
			return _tradingAssets.Values.Where(x => x.OpenOrders).Select(x => x.Symbol);
		}

		/// <summary>
		/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
		/// </summary>
		public void OnData(TradeBars data) {
			if (IsWarmingUp) return;
			foreach (var bar in data.Values) {
				try {
					if (_tradingAssets.ContainsKey(bar.Symbol)) {
						_tradingAssets[bar.Symbol].Scan(bar);
					}
				} catch (Exception exp) {
					Log(string.Format("Exception {0}\n{1}", exp, exp.StackTrace));
				}
			}
		}

		public override void OnSecuritiesChanged(SecurityChanges changes) {
			foreach (Security s in changes.RemovedSecurities) {
				if (_tradingAssets.ContainsKey(s.Symbol)) {
					if (!_tradingAssets[s.Symbol].OpenOrders) {
						_tradingAssets.Remove(s.Symbol);
					}
				}

			}

			foreach (Security s in changes.AddedSecurities) {
				if (_tradingAssets.ContainsKey(s.Symbol)) {
					continue;
				}

				_tradingAssets.Add(
						s.Symbol,
						new TradingAsset(this, s.Symbol, new BounceSignal(this, s.Symbol), maxTradeValueSizePerAsset));
			}

		}

		public override void OnOrderEvent(OrderEvent orderEvent) {
			_tradingAssets[orderEvent.Symbol].OnOrderEvent(orderEvent);
		}

	}
}
using System;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp {
	public class BounceSignal : TradeSignal {
		int atrPeriod = 22;

		decimal buyDiscount = 0.97m;
		decimal sellPremium = 1.03m;
		decimal stopPercentage = 0.02m;
		decimal minimumAtrPercentage = 0.01m;

		decimal adjustStopPrice = 0.01m;

		AverageTrueRange _atr;


		public BounceSignal(QCAlgorithm algorithm, Symbol symbol) : base(algorithm, symbol) {
			_atr = algorithm.ATR(symbol, atrPeriod, MovingAverageType.Exponential, Resolution.Minute);
		}

		private decimal stopPrice(decimal lastPrice) {
			return lastPrice - _atr / 2.0m;
		}


		public override void Scan(TradeBar bar) {
			if (!_atr.IsReady) return;


			if (TradeProfile == null) {
				if (_atr / bar.Close > minimumAtrPercentage) {
					var buyPrice = bar.Close * buyDiscount;
					EntrySignal = new Signal(SignalType.Long, buyPrice, stopPrice(buyPrice));
				}
			} else if (TradeProfile.OpenTicket.Status == Orders.OrderStatus.Filled) {
				//ExitSignal = new Signal(SignalType.Exit, _atr * sellPremium, 0);

				var fillPrice = TradeProfile.OpenTicket.AverageFillPrice;
				var lastStopPrice = Math.Max(EntrySignal.StopPrice, ExitSignal.StopPrice);

				var newStopPrice = stopPrice(bar.Close);

				if (newStopPrice > (1 + adjustStopPrice) * lastStopPrice) {
					ExitSignal = new Signal(SignalType.AdjustStop, 0, newStopPrice);
				} else {
					ExitSignal = new Signal(SignalType.NoSignal, 0, lastStopPrice);
				}
			}
		}
	}
}
using System;
using System.Collections.Generic;

namespace QuantConnect.Algorithm.CSharp {
	public class AssetType {
		string _etf = "UVXY,XIV,NUGT,DUST,JNUG,JDUST,LABU,LABD,GUSH,DRIP,TVIX,GASL,GASX,DWTI,UWTI,DGAZ,UGAZ,UBIO,ZBIO,BRZU,RUSS,SCO,UCO,RUSL,ERY,ERX,BIOL,SVXY,VXX,SILJ,BIB,BIS,VIXY,SOXL,VIIX,SOXS,BZQ,USLV,SLVP,DSLV,GDXJ,GLDX";

		public string[] _forex = { "EURUSD", "GBPUSD", "USDCAD", "AUDUSD", "USDCHF" };


		public string[] Forex {
			get {
				return _forex;
			}
		}
		HashSet<string> _etfSet;

		QCAlgorithm _algorithm;

		public AssetType(QCAlgorithm algorithm) {
			_algorithm = algorithm;
			_etfSet = new HashSet<string>(_etf.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries));
		}

		public bool isVolatileEtf(Symbol symbol) {
			return _etfSet.Contains(symbol.Value);
		}
	}
}
using QuantConnect.Data.Market;
using QuantConnect.Orders;

namespace QuantConnect.Algorithm.CSharp {
	public enum SignalType {
		Long = 1,
		Short = -1,
		Exit = 2,
		AdjustStop = 3,
		NoSignal = 0
	}

	public struct Signal {
		public Signal(SignalType signalType, decimal buyPrice, decimal stopPrice) : this() {
			SignalType = signalType;
			BuyPrice = buyPrice;
			StopPrice = stopPrice;
		}

		public SignalType SignalType { get; set; }
		public decimal BuyPrice { get; set; }
		public decimal StopPrice { get; set; }
	}

	public abstract class TradeSignal {
		public Symbol Symbol { get; private set; }
		public QCAlgorithm Algorithm { get; private set; }

		public Signal EntrySignal { get; set; }
		public Signal ExitSignal { get; set; }

		public TradeProfile TradeProfile { get; set; }

		public OrderDirection EntryDirection {
			get {
				if (EntrySignal.SignalType == SignalType.Long) {
					return OrderDirection.Buy;
				}
				if (EntrySignal.SignalType == SignalType.Short) {
					return OrderDirection.Sell;
				}
				return OrderDirection.Hold;
			}
		}

		public TradeSignal(QCAlgorithm algorithm, Symbol symbol) {
			Algorithm = algorithm;
			Symbol = symbol;
			EntrySignal = new Signal(SignalType.NoSignal, 0, 0);
			ExitSignal = new Signal(SignalType.NoSignal, 0, 0);
			TradeProfile = null;
		}

		public abstract void Scan(TradeBar bar);


	}

}
using System;
using QuantConnect.Data.Market;
using QuantConnect.Orders;

namespace QuantConnect.Algorithm.CSharp {
	public class TradeProfile {
		private decimal minimumPurchase = 500m;

		//Ticket tracking the open order
		public OrderTicket OpenTicket, StopTicket, ExitTicket;

		//Keeps track of the current price and the direction of the trade

		public Symbol Symbol { get; private set; }

		TradeBar _lastBar;

		Signal _entrySignal;

		public Signal EntrySignal {
			get {
				return _entrySignal;
			}

			set {
				_entrySignal = value;
			}
		}

		private readonly int _tradeQuantity;

		public int Quantity {
			get {
				if (EntrySignal.SignalType == SignalType.Long) {
					return _tradeQuantity;
				}

				if (EntrySignal.SignalType == SignalType.Short) {
					return -1 * _tradeQuantity;
				}

				return 0;
			}
		}


		public TradeProfile(Symbol symbol, TradeBar lastBar, Signal entrySignal, decimal purchasingPower) {
			Symbol = symbol;
			_lastBar = lastBar;
			EntrySignal = entrySignal;
			_tradeQuantity = 0;
			if (purchasingPower > minimumPurchase) {
				_tradeQuantity = (int)(purchasingPower / entrySignal.BuyPrice);
			}
		}

		internal void Update(TradeBar bar) {
			if (bar.Symbol == Symbol) {
				_lastBar = bar;
			}
		}

		public bool isOpen() {
			if (OpenTicket != null && OpenTicket.Status == OrderStatus.Filled) {
				return !isClosed();
			}
			return false;
		}

		public bool isClosed() {
			if (OpenTicket == null || OpenTicket.Status != OrderStatus.Filled) {
				return false;
			}

			if (ExitTicket != null && ExitTicket.Status == OrderStatus.Filled) {
				return true;
			}
			if (StopTicket != null && StopTicket.Status == OrderStatus.Filled) {
				return true;
			}

			return false;
		}

		internal void AdjustStopPrice(decimal stopPrice) {

			if (StopTicket != null) {
				var updateOrderFields = new UpdateOrderFields();
				updateOrderFields.StopPrice = stopPrice;
				StopTicket.Update(updateOrderFields);
			} else {
				_entrySignal.StopPrice = stopPrice;
			}
		}
	}
}
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using System;
using System.Diagnostics.Contracts;

namespace QuantConnect.Algorithm.CSharp {
	class TradingAsset {
		//public IExitSignal ExitSignal;
		public TradeSignal _tradeSignal;
		private readonly QCAlgorithm _algorithm;


		Object orderLock = new Object();

		TradeProfile _tradeProfile;

		public Symbol Symbol { get; private set; }

		decimal _maxTradeValue;

		decimal _lastTradePrice;

		decimal _lastTradeMarginPercent = 0.25m; // 50 % 

		public bool OpenOrders {
			get {
				return _tradeProfile != null;
			}
		}

		public TradingAsset(QCAlgorithm algorithm, Symbol symbol, TradeSignal tradeSignal, decimal maxTradeValue) {
			Symbol = symbol;
			_tradeSignal = tradeSignal;
			_algorithm = algorithm;
			_maxTradeValue = maxTradeValue;
			_tradeProfile = null;
			_lastTradePrice = -1;
		}

		public void Scan(TradeBar bar) {
			_tradeSignal.Scan(bar);

			if (_tradeProfile != null) {
				_tradeProfile.Update(bar);
			}

			ScanEntrySignal(bar);
			ScanExitSignal(bar);
		}

		decimal buyingPower(OrderDirection direction) {
			return Math.Min(_maxTradeValue, _algorithm.Portfolio.GetBuyingPower(Symbol, direction));
		}

		void ScanEntrySignal(TradeBar bar) {
			if (_tradeProfile != null) return;

			// debouncing to make sure trade don't happen in same price ranges
			if (_lastTradePrice != -1) {
				var high = _lastTradePrice * (1 + _lastTradeMarginPercent);
				var low = _lastTradePrice * (1 - _lastTradeMarginPercent);

				if (!(bar.Close > high || bar.Close < low)) {
					return;
				}
			}
			if (_tradeSignal.EntrySignal.SignalType == SignalType.Long
				|| _tradeSignal.EntrySignal.SignalType == SignalType.Short) {

				//Creates a new trade profile once it enters a trade
				_tradeProfile = new TradeProfile(Symbol, bar, _tradeSignal.EntrySignal, buyingPower(_tradeSignal.EntryDirection));

				if (_tradeProfile.Quantity != 0) {
					lock (orderLock) {
						_tradeProfile.OpenTicket = _algorithm.LimitOrder(Symbol, _tradeProfile.Quantity, _tradeSignal.EntrySignal.BuyPrice);

						if (_tradeProfile.OpenTicket.Status != OrderStatus.Invalid) {
							_tradeSignal.TradeProfile = _tradeProfile;
						} else {
							_tradeProfile = null;
						}
					}
				}
			}
		}

		void ScanExitSignal(TradeBar bar) {
			if (_tradeProfile == null) return;
			var exitSignal = _tradeSignal.ExitSignal;
			if (exitSignal.SignalType != SignalType.NoSignal) {
				Contract.Ensures(exitSignal.SignalType == SignalType.Exit
								 || exitSignal.SignalType == SignalType.AdjustStop);
				if (_tradeProfile.isOpen()) {
					if (exitSignal.SignalType == SignalType.AdjustStop) {
						_tradeProfile.AdjustStopPrice(exitSignal.StopPrice);
						_algorithm.Log(string.Format("Adjusting Stop Order {0} {1}", Symbol, exitSignal.StopPrice));
					} else {
						var exitPrice = bar.Close;
						if (exitSignal.BuyPrice > 0) {
							exitPrice = exitSignal.BuyPrice;
						}
						_algorithm.Log(string.Format("Exit Ticket {0} ", Symbol));
						if (_tradeProfile.ExitTicket != null) {
							var updateOrderFields = new UpdateOrderFields();
							updateOrderFields.LimitPrice = bar.Close;
							_tradeProfile.ExitTicket.Update(updateOrderFields);
						} else {
							_tradeProfile.ExitTicket = _algorithm.LimitOrder(bar.Symbol, -1 * (int)_tradeProfile.OpenTicket.QuantityFilled, bar.Close);
						}
					}
				}
			}
		}

		public void OnOrderEvent(OrderEvent orderEvent) {
			// This can be good way to avoid lots of loop but for now this is not working for me
			lock (orderLock) {
				if (orderEvent.Status == OrderStatus.Filled) {
					_lastTradePrice = orderEvent.FillPrice;
					if (orderEvent.OrderId == _tradeProfile.OpenTicket.OrderId) {
						createStopOrder(_tradeProfile);
					} else {
						if (_tradeProfile.StopTicket != null && _tradeProfile.StopTicket.OrderId == orderEvent.OrderId) {
							if (_tradeProfile.ExitTicket != null) {
								_tradeProfile.ExitTicket.Cancel();
							}
							_tradeProfile = null;
							return;
						}
						if (_tradeProfile.ExitTicket != null && _tradeProfile.ExitTicket.OrderId == orderEvent.OrderId) {
							if (_tradeProfile.StopTicket != null) {
								_tradeProfile.StopTicket.Cancel();
							}
							_tradeProfile = null;
							return;
						}
					}
				}
			}
		}

		void createStopOrder(TradeProfile profile) {
			if (profile.StopTicket != null) return;
			//_algorithm.Log(string.Format("Creating Stop Order for {0}", profile.OpenTicket.OrderId));

			var quantity = -1 * (int)profile.OpenTicket.QuantityFilled;
			var stopPrice = profile.EntrySignal.StopPrice;

			profile.StopTicket = _algorithm.StopMarketOrder(Symbol, quantity, stopPrice);
		}
	}
}
using System;
using QuantConnect.Brokerages;

namespace QuantConnect.Algorithm.CSharp {
	public class BackTestParameters {
		QCAlgorithm _algorithm;
		Resolution _resolution;

		TimeSpan warmUpTime = TimeSpan.FromDays(5);


		public BackTestParameters(QCAlgorithm algorithm, Resolution resolution) {
			_algorithm = algorithm;
			_resolution = resolution;
		}

		public void setUp() {
			_algorithm.SetWarmUp(warmUpTime);

			_algorithm.SetStartDate(2016, 9, 1);
			_algorithm.SetEndDate(2016, 11, 7);
			_algorithm.SetCash(25000);

			_algorithm.UniverseSettings.Resolution = _resolution;

			_algorithm.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
		}

		public void setUpLocal() {
			decimal cash = 25000;

			_algorithm.SetWarmUp(warmUpTime);

			_algorithm.SetStartDate(2015, 06, 01);  //Set Start Date
			_algorithm.SetEndDate(2016, 05, 01);    //Set End Date

			_algorithm.SetCash(cash);

			_algorithm.SetBenchmark(delegate (DateTime dateTime) {
				return cash;
			});

			_algorithm.UniverseSettings.Resolution = Resolution.Minute;

			_algorithm.SetBrokerageModel(BrokerageName.FxcmBrokerage, AccountType.Margin);
		}
	}
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Forex;

namespace QuantConnect.Algorithm.CSharp {
	public class UniverseSetup {

		static int NumberOfSymbols = 100;

		decimal minDollarVolume = 500000;
		decimal maxDollarVolume = 10000000;

		QCAlgorithm _algorithm;

		AssetType _assetType;
		Func<IEnumerable<Symbol>> _includeTradingAssets;

		public UniverseSetup(QCAlgorithm algorithm, Func<IEnumerable<Symbol>> includeTradingAssets) {
			_algorithm = algorithm;
			_assetType = new AssetType(_algorithm);
			_includeTradingAssets = includeTradingAssets;

		}

		public void setUpLocal() {
			foreach (var security in _algorithm.Securities.Values) {
				_algorithm.RemoveSecurity(security.Symbol);
			}
			foreach (string symbol in _assetType.Forex) {
				_algorithm.AddSecurity(SecurityType.Forex, symbol, Resolution.Minute);
			}
		}

		public void setUp() {
			_algorithm.AddUniverse(CoarseSelectionFunction);
		}


		// sort the data by daily dollar volume and take the top 'NumberOfSymbols'
		public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) {
			var filterVolatileEtfs = coarse.Where(x => !_assetType.isVolatileEtf(x.Symbol));

			var dollarVolumeFilter = filterVolatileEtfs.Where(
				x => x.DollarVolume > minDollarVolume && x.DollarVolume < maxDollarVolume);

			// sort descending by daily dollar volume
			var sortedByDollarVolume = dollarVolumeFilter.OrderByDescending(x => x.DollarVolume);

			// take the top entries from our sorted collection
			var topN = sortedByDollarVolume.Take(NumberOfSymbols).Select(x => x.Symbol);

			if (_includeTradingAssets != null) {
				topN = topN.Union(_includeTradingAssets.Invoke());
			}

			return topN;
		}

	}
}