Overall Statistics
using QuantConnect.Indicators;
using QuantConnect.PythonPredictions;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// Basic algorithm showing how to implement Python code in a C# algorithm
    /// </summary>
    public class PythonInCSharpeExampleAlgorithm : QCAlgorithm
    {
    	PythonMethods python = new PythonMethods();
    	public Resolution resolution = Resolution.Hour;
    	
        /// <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()
        {
        	// Initialize the Algorithm
			SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage);
            SetStartDate(2015, 01, 01);
            
            SetCash(1000000);
            SetWarmUp(180);
            SetBenchmark("SPY");
            var spy = AddEquity("SPY", resolution).Symbol;
            AddEquity("AAPL", resolution);
            AddEquity("GM", resolution);
            AddEquity("TSLA", resolution);
			
			//Schedule the HMM model event to trigger. We will update the market state for each symbol every morning
			//and we will refit our model at the beginning of each month
        	Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(5,0), () => 
	        {
	        	var bars = History<TradeBar>(Securities.Keys, 100, Resolution.Daily);
	        	foreach (Symbol sym in Securities.Keys)
	        	{
	        		// Pass the history and the symbol we are looking to update
	        		python.UpdateMarketState(sym, bars);
	        	}
	        	
	        	// Print the current overall market state
	        	if (python.GetSymbolState(spy) == 1)
	        	{
	        		Debug("Current State of the market = " + python.GetSymbolState(spy));
	        	}
	        	else if (python.GetSymbolState(spy) == 0)
	        	{
	        		Debug("Current State of the market = " + python.GetSymbolState(spy));
	        	}
	        });
	        Schedule.On(DateRules.MonthStart("SPY"), TimeRules.At(19,0), () => 
	        {
        		var bars = History<TradeBar>(Securities.Keys, 900, Resolution.Daily);
	        	foreach (Symbol sym in Securities.Keys)
	        	{
	        		// Pass the extended history to refit the selected symbol models
	        		python.RefitModels(sym, bars);
	        	}
        	});
        }

        /// <summary>
        /// OnData event.
        /// </summary>
        /// <param name="data">Slice object keyed by symbol containing the stock data</param>
        public void OnData(Slice data)
        {
        	// Check each security to see if the HMM state of the symbol matches the market. If it does, enter a position.
        	// Exit the position if the market state changes.
            foreach(var i in Securities.Keys)
        	{
    			if (data.ContainsKey(i))
    			{
					if (Portfolio[i].Quantity > 0 && python.GetSymbolState(Portfolio["SPY"].Symbol) == 1)
					{
						Liquidate(i);
					}
					else if (Portfolio[i].Quantity < 0 && python.GetSymbolState(Portfolio["SPY"].Symbol) == 0)
					{
						Liquidate(i);
					}
					else if (Portfolio[i].Quantity == 0)
					{
	    				// Check to make sure we have a state for the given symbol or market, otherwise do nothing.
	    				if (python.GetSymbolState(i) == -1 || python.GetSymbolState(Portfolio["SPY"].Symbol) == -1)
	    					return;
						if (python.GetSymbolState(i) == python.GetSymbolState(Portfolio["SPY"].Symbol))
						{
							if (python.GetSymbolState(i) == 1)
								MarketOrder(i, -1);
							else if (python.GetSymbolState(i) == 0)
								MarketOrder(i, 1);
						}
					}
				}
			}
    	}
    }
}
using Python.Runtime;

namespace QuantConnect.PythonPredictions
{
	// Python Methods class that allows for calls of the HMM code. The original code can be found here:
	// https://www.quantconnect.com/forum/discussion/6878/from-research-to-production-hidden-markov-models/p1
	class PythonMethods
	{
		// Dictionary containing the current state of the symbol. Will result in a 1 or 0 value
		private Dictionary<Symbol, double> _symbolState = new Dictionary<Symbol, double>();
		
		// A Dictionary of the models the HMM function creates. Used to create a prediction and will be
		// periodically updated
		private Dictionary<Symbol, PyObject> _models = new Dictionary<Symbol, PyObject>();
		
		// Update the market state of a selected symbol
		public void UpdateMarketState(Symbol symbol, IEnumerable<DataDictionary<TradeBar>> bars)
		{ 
			// Convert the history TradeBars to a list that allows for easy conversion into a DataFrame
			// object in python
        	var history = new List<TradeBar>();
        	foreach (var bar in bars)
        	{
        		if (!bar.ContainsKey(symbol))
        			return;
        		history.Add(bar[symbol]);
        	}
        	// Check to see if we already have a model for the given symbol. If not we will have to create one
	    	if (_models.ContainsKey(symbol))
	    	{
	    		var model = _models[symbol];
	    		// This is where we implement our python code. This creates the script that we can then call
	    		using (Py.GIL())
	            {
	                dynamic GetState = PythonEngine.ModuleFromString("GetStateModule",
	                    @"
from clr import AddReference
AddReference(""QuantConnect.Common"")
from QuantConnect import *
from QuantConnect.Data.Custom import *
import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import adfuller
from hmmlearn.hmm import GaussianHMM

def to_dict(TradeBar):
	return{
		'close': TradeBar.Close,
		'EndTime': TradeBar.EndTime,
		'Symbol': TradeBar.Symbol,
	}

def TestStationartiy(returns):
    return adfuller(returns)[1] < 0.05

def PredictState(df, model):
	price = np.array(df.iloc[0].close).reshape((1,1))
	return model.predict(price)[0]

def GetState(history, model):
	state = -1
	df = pd.DataFrame([to_dict(s) for s in history], columns = ['close', 'Symbol', 'EndTime'])
	df.set_index('EndTime')
	#returns = np.array(df.close.pct_change().dropna())
	#returns = np.array(returns).reshape((len(returns),1))
	returns = df.unstack(level = 1).close.transpose().pct_change().dropna()
	stationarity = TestStationartiy(returns)
	if stationarity:
		state = PredictState(df, model)
	        
	return state
").GetAttr("GetState");
	
	                // Run the GetState method we created above
	                double state = GetState(history, model);
	                
	                // Add or update the state of the selected symbol
	                if (!_symbolState.ContainsKey(symbol))
	                	_symbolState.Add(symbol, state);
	                else if (_symbolState[symbol] != state && state != -1)
	                	_symbolState[symbol] = state;
	            }
	    	}
	    	// Create a new model
	    	else
	    	{
	    		// Implementing python code to create the HMM model
	    		using (Py.GIL())
	            {
	                dynamic CreateHMM = PythonEngine.ModuleFromString("CreateHMMModule",
	                    @"
from clr import AddReference
AddReference(""QuantConnect.Common"")
from QuantConnect import *
from QuantConnect.Data.Custom import *
import numpy as np
import pandas as pd
from hmmlearn.hmm import GaussianHMM

def to_dict(TradeBar):
	return{
		'close': TradeBar.Close,
		'EndTime': TradeBar.EndTime,
		'Symbol': TradeBar.Symbol,
	}
	
def CreateHMM(history, symbol):
	df = pd.DataFrame([to_dict(s) for s in history], columns = ['close', 'Symbol', 'EndTime'])
	df.set_index('EndTime')
	returns = np.array(df.close.pct_change().dropna())
	returns = np.array(returns).reshape((len(returns),1))
	_model = GaussianHMM(n_components=2, covariance_type=""full"", n_iter=10000).fit(returns)
	return _model

").GetAttr("CreateHMM");
	                
	                // Call the Create HMM model script we created above and add to the dictionary
	                var _model = CreateHMM(history, symbol);
	                _models[symbol] = _model;
	            }
	    	}
		}
		
		
		// Refit the HMM of a selected symbol
		public void RefitModels(Symbol symbol, IEnumerable<DataDictionary<TradeBar>> bars)
		{
			// Convert the history TradeBars to a list that allows for easy conversion into a DataFrame
			// object in python
        	var history = new List<TradeBar>();
        	foreach (var bar in bars)
        	{
        		if (!bar.ContainsKey(symbol))
        			return;
        		history.Add(bar[symbol]);
        	}
        	// Check to see if we already have a model for the given symbol. If not we will have to create one
	    	if (_models.ContainsKey(symbol))
	    	{
	    		// This is where we implement our python code. This creates the script that we can then call
	    		using (Py.GIL())
	            {
	                dynamic RefitModel = PythonEngine.ModuleFromString("RefitModelModule",
	                    @"
from clr import AddReference
AddReference(""QuantConnect.Common"")
from QuantConnect import *
from QuantConnect.Data.Custom import *
import numpy as np
import pandas as pd
from hmmlearn.hmm import GaussianHMM

def to_dict(TradeBar):
	return{
		'close': TradeBar.Close,
		'EndTime': TradeBar.EndTime,
		'Symbol': TradeBar.Symbol,
	}


def RefitModel(history, symbol, model):
	df = pd.DataFrame([to_dict(s) for s in history], columns = ['close', 'Symbol', 'EndTime'])
	df.set_index('EndTime')
	returns = np.array(df.close.pct_change().dropna())
	returns = np.array(returns).reshape((len(returns),1))
	return model.fit(returns)
").GetAttr("RefitModel");
	
	                // Call the Refit HMM model script we created above and update the dictionary
	                var model = _models[symbol];
	                var _model = RefitModel(history, symbol, model);
	                _models[symbol] = _model;
	            }		
	    	}
	    	// Create a new model
	    	else
	    	{
	    		// Implementing python code to create the HMM model
	    		using (Py.GIL())
	            {
	                dynamic CreateHMM = PythonEngine.ModuleFromString("CreateHMMModule",
	                    @"
from clr import AddReference
AddReference(""QuantConnect.Common"")
from QuantConnect import *
from QuantConnect.Data.Custom import *
import numpy as np
import pandas as pd
from hmmlearn.hmm import GaussianHMM

def to_dict(TradeBar):
	return{
		'close': TradeBar.Close,
		'EndTime': TradeBar.EndTime,
		'Symbol': TradeBar.Symbol,
	}
	
def CreateHMM(history, symbol):
	df = pd.DataFrame([to_dict(s) for s in history], columns = ['close', 'Symbol', 'EndTime'])
	df.set_index('EndTime')
	returns = np.array(df.close.pct_change().dropna())
	returns = np.array(returns).reshape((len(returns),1))
	_model = GaussianHMM(n_components=2, covariance_type=""full"", n_iter=10000).fit(returns)
	return _model

").GetAttr("CreateHMM");
	                
	                // Call the Create HMM model script we created above and add to the dictionary
	                var _model = CreateHMM(history, symbol);
	                _models[symbol] = _model;
	            }
	    	}
		}
		
		// Retreive the symbol state of a selected symbol
		public double GetSymbolState(Symbol symbol)
		{
			// Check to see if we have a state for the symbol and return that value. Otherwise return -1.
			if (_symbolState.ContainsKey(symbol))
				return (_symbolState[symbol]);
			else
				return -1;
		}
	}
}