Overall Statistics
import numpy as np
from datetime import datetime, timedelta
from collections import deque
from decimal import Decimal
from itertools import groupby
from pytz import utc
UTCMIN = datetime.min.replace(tzinfo=utc)

from Execution.NullExecutionModel import NullExecutionModel
from Risk.NullRiskManagementModel import NullRiskManagementModel


class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework):

    def Initialize(self):

        # self.SetWarmUp(10)
        self.SetStartDate(2018, 8, 18)   #Set Start Date
        self.SetEndDate(2018, 10, 25)    #Set End Date
        self.SetCash(1000)           #Set Strategy Cash
        self.SetBrokerageModel(BrokerageName.OandaBrokerage,AccountType.Margin)
        self.resolution = Resolution.Daily

        self.UniverseSettings.Resolution = Resolution.Daily
        symbols = [Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda),
                   Symbol.Create("GBPUSD", SecurityType.Forex, Market.Oanda)]
        self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )
        
        self.SetAlpha(MyAlpha())
        self.SetPortfolioConstruction(MyPortfolio()) 
        self.SetExecution(MyExecution())

        self.SetRiskManagement(NullRiskManagementModel())

    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.Filled:
            self.Log("Order Executed for: {0}".format(orderEvent.Symbol)+" | "+str(orderEvent))
            order = self.Transactions.GetOrderById(orderEvent.OrderId)
            if order.Status == OrderStatus.Filled:
                self.Log("OnOrder ask: "+str(self.Securities[order.Symbol].AskPrice))
                self.Log("OnOrder bid: "+str(self.Securities[order.Symbol].BidPrice)) 
            pass
        
class MyAlpha(AlphaModel):
    def __init__(self, prediction_interval=1):
        
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), prediction_interval)
    
    def Update(self, algorithm, data):
        insights = []
        
        for kvp in algorithm.Securities:
            
            Open = data[kvp.Key].Open
            # Open = data[kvp.Key].bidopen
            Close = data[kvp.Key].Close
            algorithm.Log("{}: Open:{}  Close:{}".format(str(kvp.Key), str(Open), str(Close)) )
            
            # if Open > Close:
            if algorithm.Time.day % 5 == 0:

                insights.append(Insight.Price(kvp.Key, self.predictionInterval, InsightDirection.Up, None, None))
                # algorithm.Log("self.predictionInterval: "+str(self.predictionInterval))
        
        return insights
        
class MyPortfolio(PortfolioConstructionModel):
    def __init__(self, resolution = Resolution.Daily, percent = 0.02):
        
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        self.nextExpiryTime = UTCMIN
        self.rebalancingTime = UTCMIN
        self.rebalancingPeriod = Extensions.ToTimeSpan(resolution)

        self.percentage = percent
    
    def CreateTargets(self, algorithm, insights):

        targets = []

        self.insightCollection.AddRange(insights)
        # algorithm.Log("insightCollection "+str([str(x.Symbol)+" "+str(x.Direction) for x in self.insightCollection]) )

        # 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])

        percent = self.percentage

        errorSymbols = {}

        for insight in lastActiveInsights:
            target = PortfolioTarget.Percent(algorithm, insight.Symbol, insight.Direction * percent)
            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)
        # algorithm.Log("expiredInsights: "+str([str(x.Symbol)+" "+str(x.Direction) for x in expiredInsights]) )

        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
        # algorithm.Log("in Portfolio -targets: "+str([( str(x.Symbol) ,x.Quantity) for x in targets]))
        # algorithm.Log("nextExpiryTime: "+str(self.nextExpiryTime))
        return targets

    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''

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

class MyExecution(ExecutionModel):

    def __init__(self, entry_pips = 5, take_profit_pips = 30):
        
        self.targetsCollection = PortfolioTargetCollection()

        self.entry_pips = entry_pips
        self.take_profit_pips = self.entry_pips + take_profit_pips

    def Execute(self, algorithm, targets):

        self.targetsCollection.AddRange(targets)

        algorithm.Log("in Execution -targets: "+str([( str(x.Symbol) ,x.Quantity) for x in targets]))
        # algorithm.Log("in Execution -targetsCollection: "+str([( str(x.Symbol), x.Quantity) for x in self.targetsCollection]))
        # algorithm.Log("in Execution -OrderByMarginImpact: "+str([( str(x.Symbol), x.Quantity) for x in self.targetsCollection.OrderByMarginImpact(algorithm)]))


        for target in self.targetsCollection:
            open_quantity = sum([x.Quantity for x in algorithm.Transactions.GetOpenOrders(target.Symbol)])
            existing_quantity = algorithm.Securities[target.Symbol].Holdings.Quantity + open_quantity
            target_quantity = target.Quantity
            price = algorithm.Securities[target.Symbol].Price
            
            entry_price = 0
            take_profit_price = 0
            
            if np.sign(target_quantity) == 1:
                entry_price = price + Decimal(0.0005)
                take_profit_price = price + Decimal(0.0030)
            if np.sign(target_quantity) == -1:
                entry_price = price - Decimal(0.0005)
                take_profit_price = price - Decimal(0.0030)
            
            algorithm.StopMarketOrder(target.Symbol, target_quantity, entry_price, tag="")
            algorithm.Log("__placed StopMarketOrder at: "+str(entry_price) )
            algorithm.LimitOrder(target.Symbol, -1*target_quantity, take_profit_price, tag="")
            algorithm.Log("__placed LimitOrder at: "+str(take_profit_price) )