Overall Statistics
Total Trades
3301
Average Win
1.58%
Average Loss
-1.59%
Compounding Annual Return
2663.811%
Drawdown
51.400%
Expectancy
0.153
Net Profit
2818.801%
Sharpe Ratio
2.967
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.99
Alpha
4.898
Beta
-159.092
Annual Standard Deviation
0.913
Annual Variance
0.833
Information Ratio
2.952
Tracking Error
0.913
Treynor Ratio
-0.017
Total Fees
$0.00
//Copyright HardingSoftware.com, 2018.
//Granted to the public domain.
//Use entirely at your own risk.
namespace QuantConnect 
{   
    public class MultiCoinFramework : QCAlgorithm
    {
    	string tickersString ="BTCUSD,ETHUSD,LTCUSD";
    	
    	decimal changes1Ratio=-1.0m; //The influence of change upon fitness.
    	decimal changes2Ratio=0.0m; //The influence of change in change upon fitness.
    	int emaOfChanges1Length=24; //The length of the change indicator.
		int emaOfChanges2Length=24; //The length of the change in change indicator.
		decimal leverage=1m;
		
		int historyLength=2;
		int changes1Length=2;
		int changes2Length=2;
		Resolution resolution=Resolution.Hour;
		List<StockData> stockDatas = new List<StockData>();
		string stockHeld="";
		
        public override void Initialize() 
        {
            SetStartDate(2017, 3, 1); 
            SetEndDate(2018, 3, 6);
            SetCash(10000);
			string[] tickers = tickersString.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string ticker in tickers)
			{
				Symbol symbol = QuantConnect.Symbol.Create(ticker, SecurityType.Crypto, Market.GDAX);
				AddCrypto(symbol, resolution);
				StockData stockData=new StockData();
				stockData.Ticker=ticker;
				stockData.emaOfChanges1Indicator = new ExponentialMovingAverage(emaOfChanges1Length);
            	stockData.emaOfChanges2Indicator = new ExponentialMovingAverage(emaOfChanges2Length);
				stockDatas.Add(stockData);
			}
        }

        public  void SetHoldings_(Symbol symbol,Decimal ratio) 
        {
        	
		    decimal price = Securities[symbol].Price;
		    var quantity =Securities[symbol].Holdings.Quantity;
		
		    // Keep 3% Cash    (for the limit order, rounding errors, and safety)
		    var keep = .03;
		    var usablePortfolioValue = Portfolio.TotalPortfolioValue * Convert.ToDecimal(1 - keep);
		    
		    // +0.1% Limit Order
		   // (to make sure it executes quickly and without much loss)
		   // (if you set the limit large it will act like a market order)
		    var limit = 1.001;
		    var desiredQuantity = usablePortfolioValue * ratio / price;
		    var orderQuantity = desiredQuantity - quantity;
		    // limit needs to be inverse when selling
		    decimal limitPrice = 0;
		    if (orderQuantity >= 0) {
		    	limitPrice = price * Convert.ToDecimal(limit);
		    }else {
		    	limitPrice =price * Convert.ToDecimal(1/limit);	
		    }
		    
		    Log("Limit Order: "+ orderQuantity+ " coins @ $"+limitPrice+ " per coin");
		    LimitOrder(symbol, orderQuantity, limitPrice);
		        	
        }
        



        public override void OnData(Slice data) 
        {
        	foreach (StockData stockData in stockDatas)
        	{
	        	stockData.history.Add(data[stockData.Ticker].Close);
	        	if (stockData.history.Count>historyLength)
	        	{
	        		stockData.history.RemoveAt(0);
	        	}
				if (stockData.history.Count>=2)
				{
					if (stockData.history[stockData.history.Count-2]!=0)
					{
						decimal change=(stockData.history.Last()-stockData.history[stockData.history.Count-2])/stockData.history[stockData.history.Count-2];
						stockData.changes1History.Add(change);
						if (stockData.changes1History.Count>changes1Length)
						{
							stockData.changes1History.RemoveAt(0);
						}
					}
				}
				if (stockData.changes1History.Count>=2)
				{
					decimal change=stockData.changes1History.Last()-stockData.changes1History[stockData.changes1History.Count-2];
					stockData.changes2History.Add(change);
					if (stockData.changes2History.Count>changes2Length)
					{
						stockData.changes2History.RemoveAt(0);
					}
				}
				if (stockData.changes1History.Count>0)
				{
					stockData.emaOfChanges1Indicator.Update(Time,stockData.changes1History.Last());
				}
				if (stockData.changes2History.Count>0)
				{
					stockData.emaOfChanges2Indicator.Update(Time,stockData.changes2History.Last());
				}
				stockData.Fitness=changes1Ratio*stockData.emaOfChanges1Indicator+changes2Ratio*stockData.emaOfChanges2Indicator;
        	}

    	    var q1 = from x in stockDatas
    			orderby x.Fitness descending
    			select x;
        	
        	List<StockData> q2=q1.ToList();
        	if (q2.Count>0)		
        	{
        		StockData selectedStockData=q2.First();
        		if (selectedStockData.Ticker != stockHeld)
        		{
        			Liquidate();
        			SetHoldings_(selectedStockData.Ticker, leverage);
					stockHeld=selectedStockData.Ticker;
        		}

        	}
        }
        
        class StockData
        {
        	public string Ticker;
			public List<decimal> history=new List<decimal>();
			public List<decimal> changes1History=new List<decimal>();
			public List<decimal> changes2History=new List<decimal>();
			public ExponentialMovingAverage emaOfChanges1Indicator;
			public ExponentialMovingAverage emaOfChanges2Indicator;
			public decimal Fitness;
        }
        
    }
}