Overall Statistics
Total Trades
90
Average Win
0.18%
Average Loss
-1.43%
Compounding Annual Return
-11.512%
Drawdown
33.300%
Expectancy
-0.127
Net Profit
-31.455%
Sharpe Ratio
-0.916
Loss Rate
22%
Win Rate
78%
Profit-Loss Ratio
0.12
Alpha
0
Beta
0
Annual Standard Deviation
0.123
Annual Variance
0.015
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$111.15
/*
 * 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;
using System.Linq;
using System.Collections.Generic;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Orders;

namespace QuantConnect.Algorithm.Examples
{
	class MeanRevertSymbolData
	{
		public string Symbol;

		//public Momentum OneMonthPerformance { get; set; }
		public Momentum ThreeMonthPerformance { get; set; }
		public SimpleMovingAverage SMA200 { get; set; }
		public CommodityChannelIndex CCI5 { get; set; }

//		public decimal ObjectiveScore
//		{
//			get
//			{
//				// we weight the one month performance higher
//				decimal weight1 = 100;
//				decimal weight2 = 75;
//
//				return (weight1 * OneMonthPerformance + weight2 * ThreeMonthPerformance) / (weight1 + weight2);
//			}
//		}
	}


	/// <summary>
    /// In this example we look at the canonical 15/30 day moving average cross. This algorithm
    /// will go long when the 15 crosses above the 30 and will liquidate when the 15 crosses
    /// back below the 30.
    /// </summary>
    public class CommodityChannelIndexAlgorithm : QCAlgorithm
    {
        //private const string Symbol = "SPY";
        private DateTime previous;
        //private CommodityChannelIndex cci;
        //private SimpleMovingAverage sma;
        private SimpleMovingAverage[] ribbon;
		private Dictionary<string,int> Quantity = new Dictionary<string,int>();
		private int LastDay;
		private const decimal _stopLimit =  1.001m;
		private const decimal _limitVal =  0.03m;
		private readonly HashSet<int> _immediateCancellations = new HashSet<int>();
		private const decimal _maxPctPerSymbol = 0.1m;
		private decimal _minCash = 0m;
		private decimal _sumOrdersYetToBeFilled = 0m;
		// these are the symbols we trade
		List<string> Symbols = new List<string>
		{
			"UPRO", 
			"UDOW", 
			"EDC", 
			"TNA", 
			"YINN",
			"BIB",
			"CURE",
			"DRN",
			"RUSL",
			"SOXL",
			"TMF"
		};



		List<MeanRevertSymbolData> SymbolData = new List<MeanRevertSymbolData>();
		private readonly List<OrderTicket> _stopTickets = new List<OrderTicket>();
		private readonly List<OrderTicket> _limitBuyTickets = new List<OrderTicket>();
        /// <summary>
        /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
        /// </summary>
        public override void Initialize()
        {
            // set up our analysis span
            SetStartDate(2009, 04, 16);
            SetEndDate(2015, 01, 01);
            SetCash(53000);             //Set Strategy Cash
			_minCash = 0.05m*Portfolio.Cash;
            // request SPY data with minute resolution
            //AddSecurity(SecurityType.Equity, Symbol, Resolution.Daily);
			foreach (var symbol in Symbols)
			{
				// ideally we would use daily data
				AddSecurity(SecurityType.Equity, symbol, Resolution.Daily);
				//var oneMonthPerformance = MOM(symbol, 30, Resolution.Daily);
				var threeMonthPerformance = MOM(symbol, 90, Resolution.Daily);
				var cci = CCI(symbol, 5,MovingAverageType.Simple,Resolution.Daily);
				var sma = SMA(symbol, 200, Resolution.Daily);
				SymbolData.Add(new MeanRevertSymbolData
					{
						Symbol = symbol,
				//		OneMonthPerformance = oneMonthPerformance,
						ThreeMonthPerformance = threeMonthPerformance,
						CCI5 = cci,
						SMA200 = sma
					});
				Quantity [symbol] = 0;
			}


            
            // create a 30 day exponential moving average
            //slow = EMA(Symbol, 30, Resolution.Daily);
            
            int ribbonCount = 8;
            int ribbonInterval = 15;
            //ribbon = Enumerable.Range(0, ribbonCount).Select(x => SMA(Symbol, (x + 1)*ribbonInterval, Resolution.Daily)).ToArray();
            
        }

        private int GetNumSymbols(decimal currentPrice)
        {
            var risk = 0.01m;
			if (Portfolio.Cash < _minCash) {
				Log ("Free margin, no money left");
				return 0;
			}

			var min_value = Math.Min(Portfolio.Cash, Portfolio.TotalPortfolioValue);
			var this_risk = risk * min_value;
            var symbol_risk = 0.14m * currentPrice;
            var quantity = (int)(Math.Round(this_risk / (symbol_risk)));
            var totalPrice = quantity * currentPrice;
			if (totalPrice > _maxPctPerSymbol*min_value)
            {
				quantity = (int)(Math.Round(_maxPctPerSymbol*min_value/currentPrice));
            }
            return quantity;
            
        }
		/// <summary>
		/// Update stop limit orders
		/// </summary>
		private void UpdateStops()
		{
			foreach(var ticket in _stopTickets)
			{
				
				Log(ticket.ToString());
				ticket.Update(new UpdateOrderFields
					{
						LimitPrice = Securities[ticket.Symbol].Low - _limitVal,
						StopPrice = Securities[ticket.Symbol].Low * _stopLimit,
						Tag = "Change prices: " + Time
					});
				Log("UPDATE2:: " + ticket.UpdateRequests.Last());
			}

		}
		private void RemoveStop(string symbol)
		{
			for (var i = 0; i < _stopTickets.Count; i++) {
				if (_stopTickets [i].Symbol == symbol)
					_stopTickets.RemoveAt (i);
			}

		}

        
        /// <summary>
        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// </summary>
        /// <param name="data">TradeBars IDictionary object with your stock data</param>
        public void OnData(TradeBars data)
        {
            // a couple things to notice in this method:
            //  1. We never need to 'update' our indicators with the data, the engine takes care of this for us
            //  2. We can use indicators directly in math expressions
            //  3. We can easily plot many indicators at the same time

            // wait for our slow ema to fully initialize
			foreach (var symboldata in SymbolData) {
				if (!symboldata.CCI5.IsReady) return;
				if (!symboldata.SMA200.IsReady) return;

			}				

			if (Time.Day != LastDay) {
//				foreach (var symboldata in SymbolData) {
//					var holdings = Portfolio[symboldata.Symbol].Quantity;
//					// we only want to go long if we're currently short or flat
//					if (holdings > 0) {
//							
//						var stopPrice = Securities [symboldata.Symbol].Low * _stopLimit;
//						var limitPrice = Securities [symboldata.Symbol].Low - _limitVal;
//
//						var newStopTicket = StopLimitOrder (symboldata.Symbol, Quantity [symboldata.Symbol], stopPrice, limitPrice);
//						_stopLimitTickets.Add (newStopTicket);
//					}
//				}
				UpdateStops ();
			}
			LastDay = Time.Day;

			foreach (var symboldata in SymbolData) {
				var diff = Securities[symboldata.Symbol].Price / symboldata.SMA200;
				if (diff < 0.01m)
				{
					Log("price under SMA 200, not buying.");
				}
				var holdings = Portfolio[symboldata.Symbol].Quantity;
				// we only want to go long if we're currently short or flat
				if (holdings <= 0)
				{
					
					if (symboldata.CCI5 <= -100)
					{
						var cash_left = Portfolio.Cash - _sumOrdersYetToBeFilled;
						int num = GetNumSymbols(Securities[symboldata.Symbol].Price);
						if (num > 0 && cash_left > _minCash)
						{
							Quantity[symboldata.Symbol] = num;
							Log("BUY, symbol = " + symboldata.Symbol + " cci =  " + symboldata.CCI5 + " num = " + num + " >> " + Securities[symboldata.Symbol].Price);
							var limitPrice = Securities [symboldata.Symbol].Price * .999m;
							var newTicket = LimitOrder(symboldata.Symbol, num, limitPrice);
							_limitBuyTickets.Add (newTicket);

							_sumOrdersYetToBeFilled += (decimal)num *  limitPrice;
							//UpdateTemporaryCashBalance ();
							//var newStopTicket = StopMarketOrder(symboldata.Symbol, -1*Quantity[symboldata.Symbol], stopPrice);

						//Log("BUY  >> " + Securities[Symbol].Price);
						//SetHoldings(Symbol, 1.0);
						}
					}
				}
				if (holdings > 0 && symboldata.CCI5 > 100)
				{
					//var num = (int)(-0.5 * Quantity);
					var num = -1 * Quantity[symboldata.Symbol];
					Log("SELL symbol = " + symboldata.Symbol + " cci =  " + symboldata.CCI5 + " num = " + num + " >> " + Securities[symboldata.Symbol].Price);
					var newTicket = LimitOrder(symboldata.Symbol,num, Securities[symboldata.Symbol].Price * .999m);
					//var stopPrice = Securities[Symbol].Low * 1.001m;
					//var limitPrice = Securities[Symbol].Low - 0.03m;

					//var newStopTicket = StopLimitOrder(Symbol, Quantity, stopPrice,limitPrice);
					//Liquidate(Symbol);    
				}

			}
		    
            // only once per day
            if (previous.Date == data.Time.Date) return;

            // define a small tolerance on our checks to avoid bouncing
            const decimal tolerance = 0.00015m;
            
            

            // we only want to liquidate if we're currently long
            // if the fast is less than the slow we'll liquidate our long
            
            //Plot(Symbol, "Price", data[Symbol].Price);
            
            // easily plot indicators, the series name will be the name of the indicator
            //Plot(Symbol, cci, sma);
            //Plot("Ribbon", ribbon);

            previous = data.Time;
        }
//		private void UpdateTemporaryCashBalance()
//		{
//			_sumOrdersYetToBeFilled = 0;
//			foreach (var ticket in _limitBuyTickets) {
//				if (ticket.Status.IsOpen () && !ticket.Status.IsFill ()) {
//					_sumOrdersYetToBeFilled += ticket.Quantity * ticket.LimitPrice;
//				}
//			}
//		}


        public override void OnOrderEvent(OrderEvent orderEvent)
        {
//			if (_immediateCancellations.Contains(orderEvent.OrderId))
//			{
//				_immediateCancellations.Remove(orderEvent.OrderId);
//				Transactions.CancelOrder(orderEvent.OrderId);
//			}
			if (orderEvent.Status == OrderStatus.Filled)
            {
                Log("FILLED:: " + Transactions.GetOrderById(orderEvent.OrderId) + " FILL PRICE:: " + orderEvent.FillPrice.SmartRounding());
                if (orderEvent.FillQuantity > 0)
                {
                    Log("BUY  >> " + orderEvent.FillPrice.SmartRounding());
                    Log("Cash left: " + Portfolio.Cash);
					var stopPrice = Securities[orderEvent.Symbol].Low * _stopLimit;
					var limitPrice = Securities[orderEvent.Symbol].Low - _limitVal;
					var newStopTicket = StopLimitOrder(orderEvent.Symbol, Quantity[orderEvent.Symbol], stopPrice,limitPrice);
					_stopTickets.Add (newStopTicket);
					_sumOrdersYetToBeFilled -= orderEvent.FillPrice * orderEvent.FillQuantity;
					//					UpdateTemporaryCashBalance ();
					if (Portfolio.Cash < _minCash) {
						foreach (var buyTicket in _limitBuyTickets) {
							if (buyTicket.Status.IsOpen () && !buyTicket.Status.IsFill ()) {
								buyTicket.Cancel ();
							}
						}
					}
                }
                else
                {
                    Log("SELL  >> " + orderEvent.FillPrice.SmartRounding());
                    Log("Cash left: " + Portfolio.Cash);
					RemoveStop(orderEvent.Symbol);
						
					
                }


            }
            else
            {
                Log(orderEvent.ToString());
                //Log("TICKET:: " + _tickets.Last());
            }
        }
    }
}