Overall Statistics
/*
 * 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.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// Demonstration of how to define a universe filtered by the combination of coarse
    /// fundamental data and fine fundamental data. This lets you do a first pass based on the asset volume; then later
    /// select based on the company fundamentals.
    /// </summary>
    /// <meta name="tag" content="using data" />
    /// <meta name="tag" content="universes" />
    /// <meta name="tag" content="coarse universes" />
    /// <meta name="tag" content="fine universes" />
    public class _FactorStrategy : QCAlgorithm
    {
        private const int NumberOfSymbolsFine = 20;

        // last selected universe
        private IEnumerable<Symbol> lastSelectedSymbols = Enumerable.Empty<Symbol>();

        // last month of universe selection
        private int lastUniverseSelectionMonth = 0;

        private Symbol spy;
        private Symbol tlt;

        private decimal mainAssetRatio = 0.6m;
        private SecurityChanges _changes = SecurityChanges.None;

        public override void Initialize()
        {
            SetTimeZone(TimeZones.NewYork);
            SetStartDate(2007, 7, 1);
            SetEndDate(2008, 1, 1);
            SetCash(50000);

            spy = AddEquity("SPY", Resolution.Daily).Symbol;
            tlt = AddEquity("TLT", Resolution.Daily).Symbol;//Symbol("TLT");

            UniverseSettings.Resolution = Resolution.Daily;

            // 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);

        }

        private void rebalance()
        {
            Debug("Rebalancing...");

            // no trade from May to October
            if (Time.Month >= 5 && Time.Month <= 10)
            {
                foreach (var security in Portfolio.Values)
                {
                    if (security.Invested && !lastSelectedSymbols.Contains(security.Symbol) && security.Symbol != tlt)
                    {
                        Liquidate(security.Symbol);
                        //Debug("Liquidated: " + security.Symbol.Value);
                    }
                }
                
				Debug("Active securities: " + string.Join(" ", ActiveSecurities.Select(x => x.Key.Value)));
				
                foreach (var symbol in lastSelectedSymbols)
                {
                    SetHoldings(symbol, (1m - mainAssetRatio) / NumberOfSymbolsFine);
                    Debug($"Purchsed: {symbol.Value} Price: {Securities[symbol].Price}");
                    //Debug("Purchsed: " + symbol.Value);
                }

                SetHoldings(tlt, mainAssetRatio);
                //Debug("Purchsed: " + tlt.Value);
                
				Debug("Invested securities: " + string.Join(" ", ActiveSecurities.Select(x => {
                    if (x.Value.Invested) return x.Key.Value;
                    return "";
                })));

                Debug("Cash in portfolio: " + Portfolio.Cash);
            }
            // November to April
            else
            {
                foreach (var security in Portfolio.Values)
                {
                    if (security.Invested && !lastSelectedSymbols.Contains(security.Symbol) && security.Symbol != tlt)
                    {
                        Liquidate(security.Symbol);
                        //Debug("Liquidated: " + security.Symbol.Value);
                    }
                }

                foreach (var symbol in lastSelectedSymbols)
                {
                    SetHoldings(symbol, mainAssetRatio / NumberOfSymbolsFine);
                    Debug($"Purchsed: {symbol.Value} Price: {Securities[symbol].Price}");
                    //Debug("Purchsed: " + symbol.Value);
                }

                SetHoldings(tlt, 1 - mainAssetRatio);
                //Debug("Purchsed: " + tlt.Value);

                Debug("Cash in portfolio: " + Portfolio.Cash);
            }
        }

        // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse'
        public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
            if (Time.Month == lastUniverseSelectionMonth)
            {
                return Universe.Unchanged;
            }

            Debug("Selecting coarse universe...");

            // select only symbols with fundamental data and sort descending by daily dollar volume
            var sortedByDollarVolume = coarse
                .Where(x => x.HasFundamentalData && x.Price > 1)
                .OrderBy(x => x.Price);

            lastUniverseSelectionMonth = Time.Month;

            // return selected symbols to FineSelectionFunction
            return sortedByDollarVolume.Take(200).Select(x => x.Symbol);// top5.Select(x => x.Symbol);
        }

        // sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
        public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
            Debug("Selecting fine universe...");

            var pe = fine.Where(x => x.ValuationRatios.PERatio > 0).OrderBy(x => x.ValuationRatios.PERatio);

            lastSelectedSymbols = pe.Take(NumberOfSymbolsFine).Select(x => x.Symbol);
            Debug("selected symbols: " + string.Join(" ", lastSelectedSymbols.Select(x => x.Value)));

            // we need to return only the symbol objects
            return lastSelectedSymbols;
        }

        //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
        public override void OnData(Slice slice)
        {
            Debug($"Security changes: {_changes}");
            if (_changes != SecurityChanges.None)
            {
                rebalance();
                _changes = SecurityChanges.None;
            }
        }

        // 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)));
            }*/

        }

    }
}