Overall Statistics
Total Trades
40
Average Win
0.52%
Average Loss
-6.86%
Compounding Annual Return
-28.570%
Drawdown
42.900%
Expectancy
-0.260
Net Profit
-21.961%
Sharpe Ratio
-0.694
Loss Rate
31%
Win Rate
69%
Profit-Loss Ratio
0.08
Alpha
-0.354
Beta
1.485
Annual Standard Deviation
0.336
Annual Variance
0.113
Information Ratio
-1.191
Tracking Error
0.264
Treynor Ratio
-0.157
Total Fees
$40.00
namespace QuantConnect 
{   
	

    /*
    *   QuantConnect University: Full Basic Template:
    *
    *   The underlying QCAlgorithm class is full of helper methods which enable you to use QuantConnect.
    *   We have explained some of these here, but the full algorithm can be found at:
    *   https://github.com/QuantConnect/QCAlgorithm/blob/master/QuantConnect.Algorithm/QCAlgorithm.cs
    */
    public class BasicTemplateAlgorithm : QCAlgorithm
    {
        // initialize our changes to nothing
        private SecurityChanges _changes = SecurityChanges.None;

    	// Minimum price per stock for it to be of interest.
	    decimal _minPrice = 0.2m;
    	// Maximum price per stock for it to be of interest.
	    decimal _maxPrice = 20m;
	    
	    // Pull the top stocks that meet our min/max price, sorted by total Volume
	    int _chunk = 50;
	    
	    // 3 mo peek should be no less than the current price*_highModifier.
	    decimal _highModifier = 1.08m;
	    // Target $ amount when making a purchase. Should not be below $400, which makes the $2 to buy/sell .5% of the purchase price.
	    decimal _buyLimit = 400m;

	    Dictionary<double, decimal> _sellModifiers = new Dictionary<double, decimal>();
	    
	    
	    
        //Initialize the data and resolution you require for your strategy:
        public override void Initialize() 
        {
			
            //Start and End Date range for the backtest:
            //SetStartDate(DateTime.Now.Date.AddYears(-2));
            SetStartDate(DateTime.Now.Date.AddDays(-90*3));
            SetEndDate(DateTime.Now.Date.AddDays(-90));
            
            //Cash allocation
            SetCash(2500);   
            
            
            // TODO: Take a percentage of the cash monthly?
            
			SetWarmup(TimeSpan.FromDays(90));
			
			// Set up the days on which the sale price will be modified.
		    _sellModifiers.Add(1, 1.03m);
		    //_sellModifiers.Add(30, 1.01m);
		    //_sellModifiers.Add(120, 0.60m);

			
            UniverseSettings.Resolution = Resolution.Daily;
            
            AddUniverse(CoarseSelectionFunction);
        }

        //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
        public void OnData(TradeBars data) 
        {   
			try {
	            if (Portfolio.Cash < _buyLimit) return;
            	foreach(var security in _changes.AddedSecurities) {
            		var symbol = security.Symbol;
            		if(!Securities.ContainsKey(security.Symbol)) { 
            			Debug("Did not contain security " + symbol.Value);
            		}
            		if(!security.HoldStock) {
            			// If the stock doesn't pass our criteria then drop it.
            			var remove = false;
            			if(!security.HasData || !AddHolding(security)) remove = true; //Securities.Remove(symbol);
            		}
            		else {
            			Debug(string.Format("Already invested in {0}", symbol));
            		}
            	}
			} catch (Exception ex) {
				Error(string.Format("OnData: {0}", ex.Message));
				Error(ex);
			}
        }

        private bool AddHolding(Security security) {
			var symbol = security.Symbol;
			try {
				//Debug(string.Format("Testing {0}", symbol.Value));
				var price = security.Price;
				
				var quantity = BuyQuantity(security);
				if(quantity == 0) return false;
				
				var ticket = LimitOrder(symbol, quantity, security.Price);
				Debug(string.Format("Order {4:000} to buy {0} {1} at ${2:#.00} for ${3:#.00}.", quantity, symbol.Value, security.Price, security.Price*quantity, ticket.OrderId));
				return true;
			} catch (Exception ex) {
				Error(string.Format("AddHolding for {0}: {1}", symbol, ex.Message));
				Error(ex);
				return false;
			}
        }


        // Built-in handler for order completion.
        // When buying an order, set up our initial high and low stops.
        // When selling an order for profit, set up the next sell point.
        public override void OnOrderEvent(OrderEvent orderEvent) {
        	var symbol = orderEvent.Symbol;
        	try {
	        	if(!Securities.ContainsKey(symbol)) {
	        		Error(string.Format("Security for symbol '{0}' not found in Securities collection when processing OrderId {1}.", symbol, orderEvent.OrderId));
	        		return;
	        	}
	        	var security = Securities[symbol];
	        	var order = Transactions.GetOrderById(orderEvent.OrderId);
				var quantity = orderEvent.FillQuantity;
				var price = orderEvent.FillPrice;
	        	switch (orderEvent.Direction) {
	        		case OrderDirection.Buy: 
	        			if(security.Holdings.HoldingsCost != price*quantity) {
	        				Error(string.Format("Expected the security's HoldingsCost ({0}) and order event's total cost ({1}) to be the same for OrderId {2} for symbol '{3}'.", 
	        					security.Holdings.HoldingsCost, orderEvent.FillPrice*orderEvent.FillQuantity, orderEvent.OrderId, symbol));
	        			}
						if(quantity != 0) {
							Debug(string.Format("Order {4:000} bought {0} {1} at ${2:#.00} for ${3:#.00}.", quantity, symbol.Value, price, price*quantity, orderEvent.OrderId));
		        			// Put in order to sell once we've met our buy price. Start at one day to ensure we aren't day trading
		        			ScheduleSale(symbol, orderEvent.UtcTime, quantity, price);
						}
	    			break;
	    			case OrderDirection.Sell:
	    				// For a sale, the quantity is in the negative. Reverse it for logging.
	    				quantity = -quantity;
						if(quantity != 0) Debug(string.Format("Order {5:000} sold {0} {1} at ${2:#.00} for ${3:#.00}. Net: ${4}", quantity, symbol.Value, price, price*quantity, security.Holdings != null ? security.Holdings.Profit.ToString("#.00") : "?", orderEvent.OrderId));
    				break;
	        	}
        	} catch (Exception ex) {
				Error(string.Format("OnOrderEvent for {0}: {1}", symbol.Value, ex.Message));
				Error(ex);
			}
        	
        }
        
        private void ScheduleSale(Symbol symbol, DateTime buyDate, int quantity, decimal price) {
        	foreach(var sellModifier in _sellModifiers) {
        		var sellPrice = price*sellModifier.Value;
        		// Start at one day to ensure we aren't day trading.
        		Schedule.On(DateRules.On(buyDate.AddDays(sellModifier.Key)), TimeRules.AfterMarketOpen(symbol, 1), () => {
					if(Securities.ContainsKey(symbol) && Securities[symbol].Invested) {
						var ticket = LimitOrder(symbol, -quantity, sellPrice);
						Debug(string.Format("Order {3:000} to sell {5} {4} at ${1:0.00} after {2} days.", symbol.Value, sellPrice, sellModifier.Key, ticket.OrderId, symbol.Value, quantity));

					}
        		});
        	}
		}
        
        private int BuyQuantity(Security security) {
        	if(Portfolio.Cash < _buyLimit) return 0;
        	var spendRemainingCash = Math.Floor(Portfolio.Cash / security.Price);
        	if(spendRemainingCash < 1) return 0;
        	return (int)Math.Min(Math.Ceiling(_buyLimit / security.Price), spendRemainingCash);
        }

        
        private IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) {
        	try {
				return (from c in coarse
					    where c.Price >= _minPrice 
					    && c.Price <= _maxPrice
					    orderby c.DollarVolume descending 
			            select c.Symbol).Take(_chunk);
			} catch (Exception ex) {
				Error(string.Format("CoarseSelectionFunction: {0}", ex.Message));
				Error(ex);
				return null;
			}
        }
        
        
        // this event fires whenever we have changes to our universe
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            _changes = changes;

            /*if (changes.AddedSecurities.Count > 0)
            {
                Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value)));
            }
            if (changes.RemovedSecurities.Count > 0)
            {
                Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value)));
            }*/
        }
        
        /*private enum OrderTypes {
        	BuyInitial,
        	Sell,
        	
        }*/
    }
}