Overall Statistics
Total Trades
3067
Average Win
0.56%
Average Loss
-0.36%
Compounding Annual Return
18.832%
Drawdown
35.300%
Expectancy
0.470
Net Profit
1256.789%
Sharpe Ratio
1.014
Probabilistic Sharpe Ratio
52.448%
Loss Rate
43%
Win Rate
57%
Profit-Loss Ratio
1.58
Alpha
0.157
Beta
-0.042
Annual Standard Deviation
0.151
Annual Variance
0.023
Information Ratio
0.302
Tracking Error
0.228
Treynor Ratio
-3.617
Total Fees
$19149.02
/*
 * 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 QuantConnect.Data;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Data.Custom;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Data.Consolidators;


namespace QuantConnect.Algorithm.CSharp
{

	public class StockRankAlgorithm : QCAlgorithm
	{
		// algorythm settings
		const Resolution _algo_resolution = Resolution.Daily;
		private ExponentialMovingAverage _spyMovingAverage;
		private const string Spy = "SPY";
		private int _trendfilter = 200;
		public const decimal _startingcash = 100000m;
		public const decimal _maxLeverage = 1.0m;
		public const decimal _maxNumHoldings = 25m;
		public const decimal _positionweight = _maxLeverage/_maxNumHoldings;
		private bool flag1 = true;
		private bool flag2 = false;
		private int flag3 = 0;
		private int _securitiesnum = 0;
		private SecurityChanges _changes = SecurityChanges.None;
		public DateTime _startdate = new DateTime(2005, 1, 1); 
		// public DateTime _enddate = new DateTime(2014, 11, 23);
		//public DateTime _startdate = new DateTime(2015, 1, 1);
		//public DateTime _startdate = DateTime.Now.Date.AddDays(-120);
		public DateTime _enddate = DateTime.Now.Date.AddDays(-1);     
		//public DateTime _enddate = new DateTime(2011, 1, 1);
        // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse'
        public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
        	if (flag1) {
        	var hasmorningstar = from x in coarse
        							where (x.HasFundamentalData)
        							orderby x.DollarVolume descending
        							select x.Symbol;
        							
            // we need to return only the symbol objects
            return hasmorningstar.Take(1000);
        	}
        	else {
        		return Enumerable.Empty<Symbol>();
        	}
        }

 
        // sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
        public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
        	if (flag1) {
        		flag1 = false;
        		flag2 = true;
        		ConcurrentDictionary<Symbol, IndicatorBase<IndicatorDataPoint>> averages = new ConcurrentDictionary<Symbol, IndicatorBase<IndicatorDataPoint>>();
	            // find piatroski score
	            var piatroskiscore = from x in fine
	            		   where x.SecurityReference.SecurityType == "ST00000001" && x.SecurityReference.IsPrimaryShare 
	            		   where !x.SecurityReference.IsDepositaryReceipt
                           where x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.FinancialServices
                           where x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.Utilities
                          // where (x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths*x.ValuationRatios.PERatio) > 300000000) //&& x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths*x.ValuationRatios.PERatio) < 2000000000)

							let fs = FScore(
	            				x.FinancialStatements.IncomeStatement.NetIncome.TwelveMonths,
	            				x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.TwelveMonths,
	            				x.OperationRatios.ROA.ThreeMonths, x.OperationRatios.ROA.OneYear,
	            				x.FinancialStatements.BalanceSheet.ShareIssued.ThreeMonths, x.FinancialStatements.BalanceSheet.ShareIssued.TwelveMonths,  
								x.OperationRatios.GrossMargin.ThreeMonths, x.OperationRatios.GrossMargin.OneYear,
								x.OperationRatios.LongTermDebtEquityRatio.ThreeMonths, x.OperationRatios.LongTermDebtEquityRatio.OneYear, // fix need last year
								x.OperationRatios.CurrentRatio.ThreeMonths, x.OperationRatios.CurrentRatio.OneYear, // fix need last year 
								x.OperationRatios.AssetsTurnover.ThreeMonths, x.OperationRatios.AssetsTurnover.OneYear // fix need last year
	            			)
	            			where (fs >= 6) 
							//orderby fs descending
							let avg = averages.GetOrAdd(x.Symbol, sym => WarmUpIndicator(x.Symbol, new MomentumPercent(90), Resolution.Daily))
	                        // Update returns true when the indicators are ready, so don't accept until they are
	                        where avg.Update(x.EndTime, x.Price)
	                        // only pick symbols who have positive slopes [Absolute Momentum]
	                        where avg > 0m
	                        // prefer symbols with a larger slope
                        	orderby (avg) descending
							select x;
	            
	            // take the top entries from our sorted collection
	            var topFine = piatroskiscore.Take((int)_maxNumHoldings);
	            _securitiesnum = topFine.Count();
	            flag3++;
	            //Debug("Found " + topFine.Count() + " possibilities");
	            // foreach (var s in topFine)
	            // {
	            // 	Debug(s.ToString());
	            // }
	            // we need to return only the symbol objects
	            return topFine.Select(x => x.Symbol);
        	} else {
        		return Enumerable.Empty<Symbol>();
        	}
        }


		// Initialize the data and resolution you require for your strategy:
		public override void Initialize()
		{
			SetStartDate(_startdate);
			SetEndDate(_enddate);

			// don't do any margin stuff
			SetBrokerageModel(Brokerages.BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
			var spy = AddEquity(Spy);
			AddEquity("TLT");
			SetBenchmark(spy.Symbol);
			_spyMovingAverage = EMA(spy.Symbol, _trendfilter, Resolution.Daily);
			//Warm up indicator
            //IEnumerable<TradeBar> history = History(spy.Symbol, _trendfilter, Resolution.Daily);
            //foreach (TradeBar tradeBar in history)
            //{
            //    _spyMovingAverage.Update(tradeBar.EndTime, tradeBar.Close);
            //}
            //set warm up algorithm to avoid premature trading
            SetWarmUp(_trendfilter);
            
			EnableAutomaticIndicatorWarmUp = true;
			//Cash allocation
			SetCash(_startingcash);

			UniverseSettings.Resolution = _algo_resolution;
			
			// this add universe method accepts two parameters:
            // - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
            // - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol>
            AddUniverse(CoarseSelectionFunction, FineSelectionFunction);
            
            Schedule.On(DateRules.MonthStart(spy.Symbol) , TimeRules.AfterMarketOpen(spy.Symbol), Rebalancing);

		}


		public override void OnData(Slice slice)
		{
			if (flag3 > 0)
			{
	            if (flag2) {
	                flag2 = false;

					//
					//# if we have no changes, do nothing
	                if (_changes == SecurityChanges.None) return;
	                //# liquidate removed securities
	                foreach (Security security in _changes.RemovedSecurities)
	                {
	                    if (security.Invested)
	                        Liquidate(security.Symbol);
	                        Debug("Liquidated Stock: " + security.Symbol.Value);
	                }
	                
	                decimal diff = _maxNumHoldings - _securitiesnum;
	        		SetHoldings("TLT", _positionweight*diff);
	        		
	        		if (Securities[Spy].Price > _spyMovingAverage)
                    {          
		                foreach (Security security in _changes.AddedSecurities)
		                {
		                	if (security.Symbol.Value.Equals("SPY")) continue;
							if (security.Symbol.Value.Equals("TLT")) continue;

		                    SetHoldings(security.Symbol, _positionweight);  
		                    Debug("Purchased Stock: " + security.Symbol.Value);
		                }
                    }	
	                _changes = SecurityChanges.None;
                	
                	if (Portfolio.TotalPortfolioValue > 0)
		            {
		                decimal accountLeverage = Portfolio.TotalAbsoluteHoldingsCost / Portfolio.TotalPortfolioValue;
		                decimal bondLeverage = (Portfolio["TLT"].Quantity*Portfolio["TLT"].Price) / Portfolio.TotalPortfolioValue;
		                Plot("Leverage", "Leverage", accountLeverage);
		            	Plot("Leverage", "bondLeverage", bondLeverage);
		            	Plot("Memory", "memusage", (float)GC.GetTotalMemory(false));
		            }
				}
			}
		}
		
		
		//# 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)));
            }
            
    	}
    	public void Rebalancing()
    	{
    		//can rebalance every month
    		if (Time.Month % 1 == 0)
    		//can rebalance once a year in a particular month
    		//if (Time.Month == 5)
    		//can rebalance every quarter
    		//if (Time.Month % 3 == 0)
    		//can rebalance every two months
    		//if (Time.Month % 2 == 0)
    		{
        		flag1 = true;
    		}
        	
    	}
    	
    	// calculate the f-score
        private int FScore(
        	decimal netincome, 
        	decimal operating_cashflow, 
        	decimal roa_current, decimal roa_past, 
        	decimal issued_current, decimal issued_past, 
        	decimal grossm_current, decimal grossm_past,
        	decimal longterm_current, decimal longterm_past,
        	decimal curratio_current, decimal curratio_past,
        	decimal assetturn_current, decimal assetturn_past
        	)
        {
        	int fscore = 0;
        	
        	fscore += (roa_current > 0m ? 1 : 0 );  // return on assets is positive
        	fscore += ( operating_cashflow > 0m ? 1 : 0 );  // operating cashflow in current year is positive
        	fscore += ( roa_current >= roa_past ? 1 : 0 );  // roa has increased since last Time
        	fscore += ( operating_cashflow > roa_current ? 1 : 0 );  // cashflow from current operations are greater than roa
        	fscore += ( longterm_current <= longterm_past ? 1 : 0 );  // a decrease in the long term debt ratio
        	fscore += ( curratio_current >= curratio_past ? 1 : 0 );  // an increase in the current ratio
        	fscore += ( issued_current <= issued_past ? 1 : 0 );  // no new shares have been issued
        	fscore += ( grossm_current >= grossm_past ? 1 : 0 );  // an increase in gross margin
        	fscore += ( assetturn_current >= assetturn_past ? 1 : 0 ); // a higher asset turnover ratio
        	
        	// Debug("<<<<<<");
        	//Debug("f-score :"  +fscore);
        	// Debug("roa current : " + roa_current);
        	// Debug("operating cashflow : " + operating_cashflow);
        	// Debug("roa current : " + roa_current + " past : " + roa_past);
        	// Debug("operating cashflow : " + operating_cashflow );
        	// Debug("longterm_current : " + longterm_current + " past : " + longterm_past);
        	// Debug("curratio_current : " + curratio_current + " past : " + curratio_past);
        	// Debug("issued current : " + issued_current + " : past " + issued_past);
        	// Debug("grossm current : " + grossm_current + " : past " + grossm_past);
        	// Debug("assetturn_current : " + assetturn_current + " : " + assetturn_past);
        	
        	return fscore;
        }
        
	}
}