Overall Statistics
Total Trades
2357
Average Win
0.04%
Average Loss
-0.01%
Compounding Annual Return
1.831%
Drawdown
6.700%
Expectancy
0.351
Net Profit
9.504%
Sharpe Ratio
0.592
Loss Rate
63%
Win Rate
37%
Profit-Loss Ratio
2.68
Alpha
-0.001
Beta
0.159
Annual Standard Deviation
0.026
Annual Variance
0.001
Information Ratio
-0.875
Tracking Error
0.1
Treynor Ratio
0.095
Total Fees
$2357.00
//Copyright Warren Harding 2017, granted to the public domain.
//Use entirely at your own risk.
//Custom algorithm development: warrencharding@yahoo.com.
//Do not remove this copyright notice.

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
namespace QuantConnect
{
    public class Algo : QCAlgorithm
    {
    	//You can adjust this first set to optimize.
    	decimal lowerBandRatio = 0.9m;
    	decimal upperBandRatio = 1.1m;
    	decimal maximumTrade=1000000;
    	decimal minimumTrade = 500m;
    	int maPeriod=60;
    	int rsiPeriod = 2;
    	decimal rsiBuyCutoff=50;
    	decimal rsiSellCutoff=50;
    	decimal stopLossRatio=0.9m;
    	//Your broker is going to hate you if you set these too low with minute data as it will result in large amounts
    	//of unfilled order cancellations. It bogs down LEAN as well.
    	int barsToHoldBuyOrdersFor=0;
    	int barsToHoldSellOrdersFor=0;
    	decimal leverage=2m;
    	Resolution resolution = Resolution.Daily;
    	decimal ratioOfDollarVolumeForMaxTrade;

		List<StockData> stockDatas = new List<StockData>();
		List<OrderTicketWrapper> buyOrders=new List<OrderTicketWrapper>();
		List<OrderTicketWrapper> sellOrders=new List<OrderTicketWrapper>();
        
        public override void Initialize()
        {
            SetStartDate(2012, 1, 1);
			SetEndDate(2017, 1, 1);
            SetCash(100000000);
           
			//s&p 500.
        	string tickersString ="CHK,DO,ENDP,MUR,SWN,FCX,RIG,NRG,MRO,NEM,RRC,CF,MU,APA,VRTX,FOSL,APC,WDC,DVN,MOS,FTR,MNK,VIAB,WYNN,UAL,FSLR,TSO,NFX,HP,HES,NAVI,AAL,SIG,ALXN,STX,SWKS,MPC,NOV,URI,NBL,QRVO,WMB,HOG,JWN,DAL,OI,PXD,OKE,MYL,NVDA,RCL,CXO,TGNA,COG,XEC,HPE,ETFC,EOG,KSS,GPS,REGN,HRB,HAL,AVGO,PRGO,BHI,UA,M,ADS,CSC,URBN,KMI,AA,COP,PYPL,KORS,LUV,EQT,VLO,FTI,FMC,DISCA,JCI,LNC,SE,ALK,NUE,VMC,AMG,AES,LEN,CNC,SPLS,WRK,BWA,HAR,TDC,CBG,GT,R,KMX,HST,ZION,ALB,AN,TIF,RF,CBS,CMG,NFLX,MLM,DISCK,SCHW,LM,EXPE,BBBY,DHI,COH,VRSN,ILMN,FLS,DLPH,JNPR,PHM,NTAP,MAR,FTV,CFG,CHTR,TRIP,NWSA,BAC,BEN,AMAT,AGN,MET,YHOO,HCP,MS,ADSK,WHR,HPQ,WY,BIIB,KEY,VTR,LYB,CMA,VFC,LUK,LRCX,DLR,DG,LB,CELG,AIV,RL,PVH,AYI,MCK,ULTA,IVZ,ATVI,MJN,CCL,IR,SNI,IPG,FITB,FFIV,UNM,HCA,BBY,HBAN,SYF,SLG,ESS,KR,DLTR,DUK,FE,BMY,TAP,KSU,O,EQIX,LVLT,C,GRMN,MCHP,GWW,PWR,PDCO,EXR,MAS,CNP,SYMC,FLR,CRM,BF-B,STI,SCG,HCN,CSX,FOXA,IP,HOLX,VNO,PLD,EQR,KLAC,GGP,WFC,AKAM,UHS,UDR,CTL,PKI,NI,JEC,GPN,NWL,BSX,MDLZ,SEE,CTSH,SRCL,ADI,SRE,IRM,COF,PBI,AAPL,DOV,PRU,CMI,PNR,GM,GS,WFM,MAT,FBHS,HBI,EA,CTXS,PEG,JBHT,PCAR,RHT,AVB,OXY,STT,PSA,EMN,AMP,PFG,ANTM,WLTW,CCE,MAC,LNT,NSC,KIM,F,MON,XYL,RAI,TSCO,MNST,HRL,PPL,EW,LKQ,MTD,ETR,AZO,EBAY,KHC,FAST,DTE,HIG,AWK,NKE,SPGI,SYK,SLB,TSS,NEE,GILD,XRX,BLK,PNC,INTC,EXC,CCI,CERN,AEE,ETN,XRAY,ACN,FL,AAP,MCO,ESRX,ABT,CPB,ALLE,GPC,BK,CAH,GIS,LEG,XEL,WEC,DD,DGX,TXN,CMS,WAT,BXP,UNP,QCOM,ADM,WYN,FRT,ABC,AXP,AMT,HAS,NTRS,TDG,SPG,CVX,FDX,TWX,CHRW,XLNX,CA,RHI,PBCT,BAX,PNW,ES,T,EMR,CI,AMGN,DNB,TMO,DRI,BBT,CAT,ROK,MHK,WBA,TXT,ED,PSX,CME,ROST,PH,EIX,TSN,A,TGT,ZBH,HOT,PCG,AEP,LOW,TROW,PPG,PCLN,D,SHW,STZ,XL,CAG,DVA,XOM,MTB,HSIC,ABBV,BA,ADBE,ROP,ITW,HRS,PX,VZ,MRK,LH,PG,VAR,PM,BCR,L,SYY,LLY,HSY,SJM,TEL,DFS,MSFT,BDX,WU,FB,CSCO,UTX,AIZ,APD,NDAQ,DE,SO,JPM,NLSN,AET,OMC,FLIR,ORCL,UNH,FISV,IBM,EXPD,HD,AMZN,DHR,CMCSA,DOW,EFX,ISRG,LLL,FIS,MO,HON,HUM,COST,INTU,PAYX,SBUX,MKC,COL,CHD,CTAS,ECL,ZTS,AIG,PFE,AVY,PGR,USB,AME,BLL,APH,K,CVS,SNA,CINF,YUM,GD,TJX,EL,MDT,ORLY,WMT,DPS,ADP,MA,GE,GLW,ICE,GOOGL,CLX,SWK,KMB,GOOG,AFL,TRV,AON,RSG,VRSK,BRK-B,IFF,MSI,RTN,DIS,MMC,LMT,KO,STJ,TMK,V,CL,NOC,PEP,MCD,MMM,WM,ALL,UPS,JNJ,LLTC";

			string[] tickers = tickersString.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string ticker in tickers)
			{
				AddSecurity(SecurityType.Equity,ticker,resolution);
				StockData stockData=new StockData(ticker);
				stockData.EMA = EMA(ticker, maPeriod, resolution);
				stockData.RSI = RSI(ticker, rsiPeriod, MovingAverageType.Exponential, resolution);
				stockDatas.Add(stockData);
			}
            foreach (Security s in Securities.Values)
            {
            	s.FeeModel=new CustomFeeModel();
            	s.SetLeverage(leverage);
            }
        
           	//Be careful adjusting this next one, too high of a setting will result in unrealistically large
    		//purchases being made with no regards for slippage.    
        	if (resolution == Resolution.Daily)
	    	{
	    		ratioOfDollarVolumeForMaxTrade = .25m / 6.5m / 60m;
	    	}
	    	else if (resolution==Resolution.Minute)
	    	{
	    		ratioOfDollarVolumeForMaxTrade = .25m;
	    	}
	    	else if (resolution==Resolution.Hour)
	    	{
	    		ratioOfDollarVolumeForMaxTrade = .25m / 60m;
	    	}
        }
		
        public void OnData(TradeBars data)
        {
        	UpdateStockDatas(data);
			Buy(data);
			Sell(data);
        }
        
        public void UpdateStockDatas(TradeBars data)
        {
        	foreach (StockData stockData in stockDatas)	
        	{
        		if (data.ContainsKey(stockData.Ticker))
        		{
        			stockData.Update(data[stockData.Ticker].Close);
        		}
        	}
        }
        
        public void Buy(TradeBars data)
        {
            CancelExpiredOrders(buyOrders,barsToHoldBuyOrdersFor);
        	int quantity = 0;
            decimal buyPrice;
            TradeBar bar;
            string ticker;
            
            var filteredRankedStockDatas = from stockData in stockDatas
            				  where stockData.Change > 0.0m
            				  where stockData.RSI > rsiBuyCutoff
            				  where (1 - stockData.AverageMinusCloseRatio) < lowerBandRatio
            				  orderby stockData.AverageMinusCloseRatio * stockData.RSI descending
            				  select stockData;
            
    	    foreach (StockData stockData in filteredRankedStockDatas)
            {
            	ticker=stockData.Ticker;
            	if (!Portfolio[ticker].HoldStock && data.ContainsKey(ticker) && stockData.EMA.IsReady)
            	{
            		bar=data[ticker];
	            	quantity=SizePosition(bar);
	            	if (quantity > 0)
	            	{
	            		buyPrice=bar.Close;
	            		OrderTicketWrapper orderTicketWrapper=new OrderTicketWrapper();
	                    orderTicketWrapper.orderTicket = LimitOrder(bar.Symbol, quantity,buyPrice);
	                    orderTicketWrapper.price=buyPrice;
	                    buyOrders.Add(orderTicketWrapper);
	            	}
            	}
            }
            foreach (OrderTicketWrapper orderTickerWrapper in buyOrders)
            {
                orderTickerWrapper.count++;
            }
        }
        
        public void Sell(TradeBars data)
        {
        	CancelExpiredOrders(sellOrders,barsToHoldSellOrdersFor);
        	decimal sellPrice;
    	    TradeBar bar;
            string ticker;
            foreach (StockData stockData in stockDatas)
            {
            	ticker=stockData.Ticker;
                if (Portfolio[ticker].Quantity > 0 && data.ContainsKey(ticker) && stockData.EMA.IsReady)
                {
                    bar = data[ticker];
                	sellPrice = stockData.EMA * upperBandRatio;
                	if (bar.Close > sellPrice & stockData.RSI < rsiSellCutoff)
                	{
                    	sellPrice = bar.Close;
                    	OrderTicketWrapper orderTicketWrapper = new OrderTicketWrapper();
                        orderTicketWrapper.orderTicket = LimitOrder(ticker, -Portfolio[ticker].Quantity, sellPrice);
                        orderTicketWrapper.price=sellPrice;
                        sellOrders.Add(orderTicketWrapper);
                	}
		            else if (Portfolio[stockData.Ticker].AbsoluteHoldingsValue < stopLossRatio * Portfolio[stockData.Ticker].AbsoluteHoldingsCost)
		            {
		            	sellPrice = bar.Close;
                    	OrderTicketWrapper orderTicketWrapper = new OrderTicketWrapper();
                        orderTicketWrapper.orderTicket = LimitOrder(ticker, -Portfolio[ticker].Quantity, sellPrice);
                        orderTicketWrapper.price=sellPrice;
                        sellOrders.Add(orderTicketWrapper);
		            }
                }
            }
            foreach (OrderTicketWrapper orderTickerWrapper in sellOrders)
            {
                orderTickerWrapper.count++;
            }
        }
        
        static void CancelExpiredOrders(List<OrderTicketWrapper> orderTickerWrappers,int barsToHoldOrdersFor)
        {
        	foreach (OrderTicketWrapper orderTickerWrapper in orderTickerWrappers)
        	{
			    if (orderTickerWrapper.count>barsToHoldOrdersFor)
	        	{
	        		orderTickerWrapper.orderTicket.Cancel();
	        	}
        	}
        	orderTickerWrappers.RemoveAll(x=>x.orderTicket.Status==OrderStatus.Filled | x.orderTicket.Status==OrderStatus.Canceled);
        }
        
        static decimal SumBuyOrders(List<OrderTicketWrapper> buyOrders)
        {
        	decimal sum=0;
            foreach (OrderTicketWrapper orderTickerWrapper in buyOrders)
            {
                sum += orderTickerWrapper.orderTicket.Quantity * orderTickerWrapper.price;
            }
            return sum;
        }
        
        int SizePosition(TradeBar bar)
        {
            decimal maxTrade=bar.Close*bar.Volume*ratioOfDollarVolumeForMaxTrade;
            if (maxTrade>maximumTrade)
            {
            	maxTrade=maximumTrade;
            }
            int quantity =(int)Math.Floor(Math.Min(leverage*Portfolio.Cash-SumBuyOrders(buyOrders), maxTrade) / bar.Close);
            quantity = RoundLot(quantity);
            if (quantity * bar.Close < minimumTrade)
        	{
        		return 0;
        	}
        	return quantity;
        }
        
        static int RoundLot(int inOddLotQuantity)
        {
            decimal inQuantity = (decimal)inOddLotQuantity;
            if (inQuantity > 2000000)
            {
                decimal small = inQuantity / 1000000;
                small = Math.Floor(small);
                return (int)(small * 1000000);
            }
            if (inQuantity > 200000)
            {
                decimal small = inQuantity / 100000;
                small = Math.Floor(small);
                return (int)(small * 100000);
            }
            if (inQuantity > 20000)
            {
                decimal small = inQuantity / 10000;
                small = Math.Floor(small);
                return (int)(small * 10000);
            }
            if (inQuantity > 2000)
            {
                decimal small = inQuantity / 1000;
                small = Math.Floor(small);
                return (int)(small * 1000);
            }
            if (inQuantity > 200)
            {
                decimal small = inQuantity / 100;
                small = Math.Floor(small);
                return (int)(small * 100);
            }
            if (inQuantity > 20)
            {
                decimal small = inQuantity / 10;
                small = Math.Floor(small);
                return (int)(small * 10);
            }
            return inOddLotQuantity;
        }
       
       	class OrderTicketWrapper
		{
			public OrderTicket orderTicket;
			public int count=0;
			public decimal price;
		}
       
        class StockData
        {
        	public StockData(string ticker)
        	{
	      		Ticker=ticker;
        	}
        	
        	public string Ticker;
            public ExponentialMovingAverage EMA;
            public RelativeStrengthIndex RSI;
			public decimal Price;
			public decimal AverageMinusCloseRatio;
			public decimal Change;
			public decimal PreviousValue=0;
			
            public void Update(decimal value)
            {
        		AverageMinusCloseRatio=(EMA - value) / value;
       			Price=value;
       			if (PreviousValue!=0)
       			{
       				Change=(value-PreviousValue)/PreviousValue;
       			}
       			PreviousValue=value;
                return;
            }
        }
        
        public class CustomFeeModel : IFeeModel
	    {
	        public decimal GetOrderFee(Security security, Order order)
	        {
	        	return 1;
	        }
	    }
    }
}