Overall Statistics
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Orders import *
from QuantConnect.Algorithm import *
from QuantConnect.Brokerages import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect import Resolution, Extensions

from itertools import groupby
from datetime import datetime, timedelta
from pytz import utc
UTCMIN = datetime.min.replace(tzinfo=utc)
import io, requests
import numpy as np
import pandas as pd

class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework):

    def Initialize(self):
        
        self.SetStartDate(2019,1,3)  #Set Start Date
        self.SetEndDate(2019,3,1)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash
        self.SetBrokerageModel(BrokerageName.OandaBrokerage)
        
        self.weights = {}

        assets = ['AUDUSD', 'EURUSD', 'GBPUSD', 'NZDUSD', 'USDCAD', 'USDCHF', 'USDJPY', 'USDNOK', 'USDSEK', 'USDSGD']
    
        self.UniverseSettings.Resolution = Resolution.Hour
        symbols = [ Symbol.Create(asset, SecurityType.Forex, Market.Oanda) for asset in assets ]
        
        self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )
        
        self.SetAlpha(CustomAlphaModel(assets=assets, No_Channels = 10, Input_Size = 256, func = self.CalculateWeights))
        
        self.SetPortfolioConstruction(CustomWeightingPortfolioConstructionModel(resolution = Resolution.Daily, func = self.CalculateWeights, Input_Size = 256))
        
        self.SetExecution(ImmediateExecutionModel())
        
        self.SetRiskManagement(NullRiskManagementModel())
        

       
    def CalculateWeights(self, assets, securities, Input_Size):
        history_df = self.History(assets, timedelta(14), Resolution.Hour)
        return_weights = {}

        for security in securities:
            symbol = security.Value.Symbol.Value
            cc = None
            if str(symbol) not in history_df.index.get_level_values(0):
                continue
            
            history = history_df.loc[str(symbol)]
            df = history['close'].resample('1H').interpolate(method='cubic')
            
            if symbol[-3:] != 'USD':
                df = 1.0 / df
            df = np.log((df/df.shift(1)).tail(Input_Size))
            if cc is None:
                cc = df
            else:
                cc = pd.concat([cc, df], axis=1)
        
        data = cc.values.tolist()
        #response = requests.post('https://api.server/model', json=data)
        #weights = np.array(response.json()['arr'])
        
        ## Right now this sets it as an arbitray weighting scheme -- I was having trouble fulling implementing the response/weights part
        ## of your code. However, if you can create the proper weights then this is dynamic and will place orders accordingly
        weights = [0.1, 0.1, 0.3, 0.1, 0.2, 0.25, 0.25, 0.5, 0.1, 0.1]
        self.Debug(str(weights))
        for i, asset in enumerate(assets):
            howmuch = weights[i] if asset[-3:] == 'USD' else (-1.0*weights[i])
            return_weights[asset] = howmuch
        
        return return_weights        
        
        
class CustomAlphaModel:
    
    def __init__(self, assets, No_Channels, Input_Size, func):
        self.assets = assets
        self.No_Channels = No_Channels
        self.Input_Size = Input_Size
        self.weights = {}
        self.CalculateWeights = func

        
    def Update(self, algorithm, data):
        insights = []
        
        ## If you need to access weights, do so as below
        ## self.weights = self.GetWeightsFromHistoryRequest(self.assets, algorithm.ActiveSecurities)

        for asset in self.assets:
            insights.append(Insight(asset, timedelta(5), InsightType.Price, InsightDirection.Down, 0.005, None))
        ## emit insights here

        return insights
    
    def OnSecuritiesChanged(self, algorithm, changes):
        pass
        
        

class CustomWeightingPortfolioConstructionModel(PortfolioConstructionModel):
    def __init__(self, resolution, func, Input_Size):
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        self.nextExpiryTime = UTCMIN
        self.rebalancingTime = UTCMIN
        self.rebalancingPeriod = Extensions.ToTimeSpan(resolution)
        self.CalculateWeights = func
        self.Input_Size = Input_Size
        
    def CreateTargets(self, algorithm, insights):
    
        targets = []

        if (algorithm.UtcTime <= self.nextExpiryTime and
            algorithm.UtcTime <= self.rebalancingTime and
            len(insights) == 0 and
            self.removedSymbols is None):
            return targets

        self.insightCollection.AddRange(insights)

        # Create flatten target for each security that was removed from the universe
        if self.removedSymbols is not None:
            universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ]
            targets.extend(universeDeselectionTargets)
            self.removedSymbols = None

        # Get insight that haven't expired of each symbol that is still in the universe
        activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime)

        # Get the last generated active insight for each symbol
        lastActiveInsights = []
        for symbol, g in groupby(activeInsights, lambda x: x.Symbol):
            lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1])

        # give weighting to each security
        count = sum(x.Direction != InsightDirection.Flat for x in lastActiveInsights)
        
        if count == 0:
            percent = 0
        else:
            insightKeys = [x.Symbol.Value for x in lastActiveInsights]
            percent = self.CalculateWeights(insightKeys, algorithm.ActiveSecurities, self.Input_Size)

        errorSymbols = {}
        for insight in lastActiveInsights:
            target = PortfolioTarget.Percent(algorithm, insight.Symbol, insight.Direction * percent[insight.Symbol.Value])
            if not target is None:
                targets.append(target)
            else:
                errorSymbols[insight.Symbol] = insight.Symbol

        # Get expired insights and create flatten targets for each symbol
        expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime)

        expiredTargets = []
        for symbol, f in groupby(expiredInsights, lambda x: x.Symbol):
            if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime) and not symbol in errorSymbols:
                expiredTargets.append(PortfolioTarget(symbol, 0))
                continue

        targets.extend(expiredTargets)

        self.nextExpiryTime = self.insightCollection.GetNextExpiryTime()
        if self.nextExpiryTime is None:
            self.nextExpiryTime = UTCMIN

        self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod

        return targets

    def OnSecuritiesChanged(self, algorithm, changes):
    
        # Get removed symbol and invalidate them in the insight collection
        self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities]
        self.insightCollection.Clear(self.removedSymbols)