Overall Statistics
import pandas as pd
from datetime import datetime

from QuantConnect import Expiry

def normalise(series, equal_ls=True):
    if equal_ls:
        series -= series.mean()
    sum = series.abs().sum()
    return series.apply(lambda x: x/sum)


class ValueAlphaModel(AlphaModel):

    def __init__(self):
        self.securities = []

    def Update(self, algorithm, data):
        algorithm.Log("Generating alphas...")
        algorithm.Log(f"{len(algorithm.ActiveSecurities)} securities in ActiveSecurities")
        algorithm.Log(f"{len(self.securities)} securities in self.securities")
        
        insights = []
        
        fcf_y = pd.DataFrame.from_records(
            [
                {
                    'symbol': security.Symbol,
                    'fcf_y': security.Fundamentals.ValuationRatios.CashReturn
                    
                } for security in self.securities
            ])
        
        fcf_y['fcf_y'] = normalise(fcf_y['fcf_y'], True)
        
        for i, security in fcf_y.iterrows():
            direction = InsightDirection.Up if security['fcf_y'] > 0 else InsightDirection.Down
            symbol = security['symbol']
            weight = abs(security['fcf_y'])
            insight = Insight.Price(security['symbol'], timedelta(days=1), InsightDirection.Up, None, None, "ValueAlphaModel", weight)
            insights.append(insight)
        
        securities = set([i.Symbol for i in insights])
        algorithm.Debug(f"Generated {len(insights)} insights for {len(securities)} securities")
        return insights

    def OnSecuritiesChanged(self, algorithm, changes):
        algorithm.Log(f"{len(changes.AddedSecurities)} securities to add")
        algorithm.Log(f"{len(changes.RemovedSecurities)} securities to remove")
        
        for security in changes.AddedSecurities:
            self.securities.append(security)
            
        for security in changes.RemovedSecurities:
            if security in self.securities:
                self.securities.remove(security)
import pandas as pd
from datetime import timedelta

from universe_selection import FactorUniverseSelectionModel
from alpha_model import ValueAlphaModel
from portfolio_construction import OptimisationPortfolioConstructionModel

class TradingBot(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2003, 1, 1)
        self.SetEndDate(2003, 1, 10)
        self.SetCash(100000)
        self.UniverseSettings.Leverage = 1
        
        # Data resolution
        self.UniverseSettings.Resolution = Resolution.Minute
        self.Settings.RebalancePortfolioOnInsightChanges = False
        self.Settings.RebalancePortfolioOnSecurityChanges = False
        
        # Universe selection model
        self.AddUniverseSelection(FactorUniverseSelectionModel())
        self.securities = []
        
        # Alpha model
        self.AddAlpha(ValueAlphaModel())
        
        # Portfolio construction model
        #self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        self.SetPortfolioConstruction(OptimisationPortfolioConstructionModel())
        
        # Execution model
        self.SetExecution(ImmediateExecutionModel())
from clr import AddReference
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")

import pandas as pd

from datetime import timedelta

from QuantConnect import Resolution, Extensions
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *

class OptimisationPortfolioConstructionModel(PortfolioConstructionModel):

    def __init__(self, rebalancingParam = Resolution.Daily):
        self.SetRebalancing(rebalancingParam)

    def SetRebalancing(self, rebalancingParam):
        if isinstance(rebalancingParam, int):
            rebalancingParam = Extensions.ToTimeSpan(rebalancingParam)
        if isinstance(rebalancingParam, timedelta):
            rebalancingFunc = lambda dt: dt + rebalancingParam
        if rebalancingFunc:
            self.SetRebalancingFunc(rebalancingFunc)
        

    def CreateTargets(self, algorithm, insights):
        algorithm.Log("Generating targets...")
        
        targets = []
        
        active_insights = self.GetActiveInsights(algorithm, insights)
        
        if len(active_insights) == 0:
            algorithm.Log("No active insights")
            return targets
        
        alpha_portfolio = self.CalcAlphaPortfolio(algorithm, active_insights)
        
        for symbol, weight in alpha_portfolio.iteritems():
            target = PortfolioTarget.Percent(algorithm, symbol, weight)
            algorithm.Log(f"Target - {symbol} {weight} {target}")
            if target is not None:
                targets.append(target)
        
        algorithm.Log(f"Created a portfolio of {len(targets)} targets")
        
        return targets

    def CalcAlphaPortfolio(self, algorithm, active_insights):
        portfolio = pd.Series([i.Weight if i.Direction == InsightDirection.Up else -i.Weight for i in active_insights], index=[i.Symbol for i in active_insights])
        port_sum = portfolio.abs().sum()
        if port_sum != 1:
            algorithm.Log(f"Alpha insight weights don't add up to 1: {port_sum}")
            portfolio /= port_sum
        return portfolio

    def GetActiveInsights(self, algorithm, insights):
        active_insights = [i for i in insights if i.IsActive(algorithm.UtcTime)]
        return active_insights
from QuantConnect.Data.UniverseSelection import * 
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel

class FactorUniverseSelectionModel(FundamentalUniverseSelectionModel):
    
    def __init__(self):
        super().__init__(True, None, None)

    def SelectCoarse(self, algorithm, coarse):
        algorithm.Log("Generating universe...")
        universe = self.FilterDollarVolume(coarse)
        return [c.Symbol for c in universe]

    def SelectFine(self, algorithm, fine):
        universe = self.FilterFactor(self.FilterFinancials(fine))
        algorithm.Log(f"Universe consists of {len(universe)} securities")
        return [f.Symbol for f in universe]
    
    def FilterDollarVolume(self, coarse):
        sorted_dollar_volume = sorted([c for c in coarse if c.HasFundamentalData], key=lambda c: c.DollarVolume, reverse=True)
        return sorted_dollar_volume[:1000]

    def FilterFinancials(self, fine):
        filter_financials = [f for f in fine if f.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices]
        return filter_financials
    
    def FilterFactor(self, fine):
        filter_factor = sorted(fine, key=lambda f: f.ValuationRatios.CashReturn, reverse=True)
        return filter_factor[:2] + filter_factor[-2:]