Overall Statistics
Total Trades
3180
Average Win
0.16%
Average Loss
-0.14%
Compounding Annual Return
446.467%
Drawdown
4.300%
Expectancy
0.774
Net Profit
446.467%
Sharpe Ratio
9.527
Loss Rate
19%
Win Rate
81%
Profit-Loss Ratio
1.19
Alpha
1.399
Beta
0.025
Annual Standard Deviation
0.147
Annual Variance
0.022
Information Ratio
6.575
Tracking Error
0.207
Treynor Ratio
56.006
Total Fees
$3180.00
//Copyright HardingSoftware.com 2019, granted to the public domain.
//Use at your own risk. Do not remove this copyright notice.
namespace QuantConnect.Algorithm.CSharp
{
    public class Pair : QCAlgorithm
    {
		Symbol symbol = QuantConnect.Symbol.Create("MLAB", SecurityType.Equity, Market.USA);
		decimal limitRatio = 0m;
		int priceDecimals = 2;
		int period = 60 * 60;
		decimal valueWeightExponent = 1m;
		decimal timeWeightExponent = 1m;
		Resolution resolution = Resolution.Second;
		TimeSpan orderExpiryTime = new TimeSpan(0,0,0,59);
		List<TradeBar> history = new List<TradeBar>();
		decimal startCash = 5000;
		
		
        public override void Initialize()
        {
            SetStartDate(2018, 9, 6);  //Set Start Date
            SetEndDate(2019, 9, 6);
            SetCash(startCash);             //Set Strategy Cash
            
            AddEquity(symbol, resolution);
        }

        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice data)
        {
        	CancelExpiredOrders();
        	if (data.Bars.ContainsKey(symbol) && data.Bars[symbol] != null)
        	{
        		history.Add(data.Bars[symbol]);
        		if (history.Count > period)
        		{
        			history.RemoveAt(0);
        		}
        		else
        		{
        			return;
        		}
				List<decimal> lows = history.Select(x => x.Low).ToList();
				decimal buyPrice = Reversal.CalculateBuyPrice(lows, timeWeightExponent, valueWeightExponent);
				List<decimal> highs = history.Select(x => x.High).ToList();
				decimal sellPrice = Reversal.CalculateSellPrice(highs, timeWeightExponent, valueWeightExponent);
				decimal range = sellPrice - buyPrice;				
    			if (Portfolio[symbol].Quantity == 0)
    			{

					decimal price = buyPrice - range * limitRatio;
    				price = Math.Round(price, priceDecimals);
    				if (price > 0)
    				{
	    				//decimal quantity = tradeQuantity;
	    				//decimal quantity = Math.Floor((Portfolio.Cash - 2) / price);
	    				decimal quantity = startCash / price;
	    				if (OrderIsPlaced(symbol, quantity) == false)
	    				{
	    					Transactions.CancelOpenOrders();
	        				LimitOrder(symbol, quantity, price);
	    				}
    				}
    			}
    			else if (Portfolio[symbol].Quantity > 0)
    			{

					decimal price = sellPrice + range * limitRatio;
    				price = Math.Round(price, priceDecimals);
    				if (price > 0)
    				{
	    				decimal quantity = -Portfolio[symbol].Quantity;
	    				if (OrderIsPlaced(symbol, quantity) == false)
	    				{
	    					Transactions.CancelOpenOrders();
	        				LimitOrder(symbol, quantity, price);
	    				}
    				}
    			}
        	}
        }
        
        public bool OrderIsPlaced(Symbol symbol, decimal quantity)
        {
        	List<Order> orders = Transactions.GetOpenOrders(symbol);
        	foreach (Order order in orders)
        	{
        		if (order.Symbol == symbol)
        		{
        			if (Math.Sign(quantity) == Math.Sign(order.Quantity))
        			{
        				return true;
        			}
        		}
        	}
        	return false;
        }
        
        public void CancelExpiredOrders()
        {
        	List<Order> orders = Transactions.GetOpenOrders();
        	foreach (Order order in orders)
        	{
        		if (Time > order.Time + orderExpiryTime)
        		{
        			Transactions.CancelOrder(order.Id);
        		}
        	}
        }

	    public class Reversal
	    {
	    	//Copyright HardingSoftware.com 2019, granted to the public domain.
			//Use at your own risk. Do not remove this copyright notice.
	        public int Index;
	        public decimal Value;
	        
	        public static decimal CalculateBuyPrice(List<decimal> lows, decimal timeWeightExponent, decimal valueWeightExponent)
	        {
				List<Reversal> minima = Reversal.Minima(lows).OrderByDescending(x => x.Value).ToList();
				decimal[] timeWeights = minima.Select(x => (decimal)Math.Pow((double)x.Index, (double)timeWeightExponent)).ToArray();
				decimal[] valueWeights = ExponentialWeights(minima.Count, valueWeightExponent);
				decimal[] totalWeights = timeWeights.Zip(valueWeights, (t, v) => t * v).ToArray();
				decimal[] values = minima.Select(x => x.Value).ToArray();
				return WeightedAverage(values, totalWeights);
	        }
	        
	        public static decimal CalculateSellPrice(List<decimal> highs, decimal timeWeightExponent, decimal valueWeightExponent)
	        {
				List<Reversal> maxima = Reversal.Maxima(highs).OrderBy(x => x.Value).ToList();
				decimal[] timeWeights = maxima.Select(x => (decimal)Math.Pow((double)x.Index, (double)timeWeightExponent)).ToArray();
				decimal[] valueWeights = ExponentialWeights(maxima.Count, 2);
				decimal[] totalWeights = timeWeights.Zip(valueWeights, (t, v) => t * v).ToArray();
				decimal[] values = maxima.Select(x => x.Value).ToArray();
				return WeightedAverage(values, totalWeights);
	        }
	
	        public static List<Reversal> Maxima(List<decimal> values)
	        {
	            List<Reversal> reversals = new List<Reversal>();
	            for (int i = 1; i < values.Count - 1; i++)
	            {
	                if (values[i -1] < values[i])
	                {
	                    if (values[i + 1] < values[i])
	                    {
	                        Reversal reversal = new Reversal();
	                        reversal.Index = i;
	                        reversal.Value = values[i];
	                        reversals.Add(reversal);
	                    }
	                }
	            }
	            if (values[0] > values[1])
	            {
	                Reversal reversal = new Reversal();
	                reversal.Index = 0;
	                reversal.Value = values[0];
	                reversals.Add(reversal);
	            }
	            if (values.Last() > values[values.Count - 2])
	            {
	                Reversal reversal = new Reversal();
	                reversal.Index = values.Count;
	                reversal.Value = values.Last();
	                reversals.Add(reversal);
	            }
	            return reversals;
	        }
	
	        public static List<Reversal> Minima(List<decimal> values)
	        {
	            List<Reversal> reversals = new List<Reversal>();
	            for (int i = 1; i < values.Count - 1; i++)
	            {
	                if (values[i - 1] > values[i])
	                {
	                    if (values[i + 1] > values[i])
	                    {
	                        Reversal reversal = new Reversal();
	                        reversal.Index = i;
	                        reversal.Value = values[i];
	                        reversals.Add(reversal);
	                    }
	                }
	            }
	            if (values[0] < values[1])
	            {
	                Reversal reversal = new Reversal();
	                reversal.Index = 0;
	                reversal.Value = values[0];
	                reversals.Add(reversal);
	            }
	            if (values.Last() < values[values.Count - 2])
	            {
	                Reversal reversal = new Reversal();
	                reversal.Index = values.Count;
	                reversal.Value = values.Last();
	                reversals.Add(reversal);
	            }
	            return reversals;
	        }
	        
	    	public static decimal WeightedAverage(decimal[] values, decimal[] weights)
	        {
	        	decimal d = weights.Sum();
	        	if (d != 0)
	        	{
	            	return values.Zip(weights, (x, y) => x * y).Sum() / d;
	        	}
	        	else
	        	{
	        		return 0;
	        	}
	        }

	        public static decimal[] TriangularWeightsDecimal(int length)
	        {
	            int[] intWeights = Enumerable.Range(1, length).ToArray();
	            return intWeights.Select(x => Convert.ToDecimal(x)).ToArray();
	        }
	
	        public static decimal TriangularMovingAverage(decimal[] values)
	        {
	            return WeightedAverage(values, TriangularWeightsDecimal(values.Length));
	        }
	        
	        public static decimal[] ExponentialWeights(int length, decimal exponent)
	        {
	            List<decimal> weights = new List<decimal>();
	            double exponentDouble = (double)exponent;
	            for (int i=0;i<length;i++)
	            {
	                double w = (double)(i + 1);
	                weights.Add((decimal)Math.Pow(w, exponentDouble));
	            }
	            return weights.ToArray();
	        }
	    }
    }
}