Overall Statistics
Total Trades
77
Average Win
0.15%
Average Loss
-0.51%
Compounding Annual Return
-12.735%
Drawdown
24.800%
Expectancy
-0.486
Net Profit
-3.116%
Sharpe Ratio
0.216
Probabilistic Sharpe Ratio
32.308%
Loss Rate
60%
Win Rate
40%
Profit-Loss Ratio
0.29
Alpha
0
Beta
0
Annual Standard Deviation
0.683
Annual Variance
0.466
Information Ratio
0.216
Tracking Error
0.683
Treynor Ratio
0
Total Fees
BUSD510.37
Estimated Strategy Capacity
BUSD5200000.00
Lowest Capacity Asset
ETHBUSD 18N
#region imports
from AlgorithmImports import *
from QuantConnect.Indicators import *
#endregion

class MyAlphaModelDown(AlphaModel):
    def __init__(self, mainAlgo):
        self.algo = mainAlgo
 
    def Update(self, algorithm, data):
        insights = []
        # always emit Down Insight
        insights.append((Insight.Price(self.algo.eth, Expiry.EndOfDay, InsightDirection.Down)))
        self.rebalanceTime = Expiry.EndOfDay(algorithm.Time)
        
        return insights

    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'''
        pass
        
                


        
                
#region imports
from AlgorithmImports import *
from QuantConnect.Indicators import *
#endregion

class MyAlphaModelUp(AlphaModel):
    def __init__(self, mainAlgo):
        self.algo = mainAlgo

    def Update(self, algorithm, data):
        insights = []
        # always emit Up Insight
        insights.append((Insight.Price(self.algo.eth, Expiry.EndOfDay, InsightDirection.Up)))
        self.rebalanceTime = Expiry.EndOfDay(algorithm.Time)
        
        return insights

    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'''
        pass
        
                


        
                
#region imports
from AlgorithmImports import *
import datetime
#endregion

class MyExecutionModel(ExecutionModel):

    def __init__(self, mainAlgo):
        '''Initializes a new instance of the MyExecutionModel class'''
        self.targetsCollection = PortfolioTargetCollection()
        self.minOrderSizeInUSD = 10
        self.algo = mainAlgo
    
    # Fill the supplied portfolio targets efficiently
    def Execute(self, algorithm, targets: List[PortfolioTarget]) -> None:
        '''Immediately submits orders for the specified portfolio targets.
        Args:
            algorithm: The algorithm instance
            targets: The portfolio targets to be ordered'''

        # for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
        self.targetsCollection.AddRange(targets)
        if not self.targetsCollection.IsEmpty:
            
            for target in self.targetsCollection.OrderByMarginImpact(algorithm):
                security = algorithm.Securities[target.Symbol]
                # calculate remaining quantity to be ordered
                quantity = OrderSizing.GetUnorderedQuantity(algorithm, target, security)
                if quantity != 0:
                    # Helper method to determine if the requested quantity is above the algorithm minimum order margin portfolio percentage
                    aboveMinimumPortfolio = BuyingPowerModelExtensions.AboveMinimumOrderMarginPortfolioPercentage(security.BuyingPowerModel, security, quantity, algorithm.Portfolio, algorithm.Settings.MinimumOrderMarginPortfolioPercentage)
                    if aboveMinimumPortfolio:
                        # expiryDatetime = self.algo.Time + timedelta(hours=6)
                        # expiryStringTag = "expires: " + expiryDatetime.strftime("%m/%d/%Y, %H:%M:%S") + "CB: " + str(self.algo.Portfolio.CashBook["BUSD"].ValueInAccountCurrency)
                        # order_properties = OrderProperties()
                        # order_properties.TimeInForce = TimeInForce.GoodTilDate(expiryDatetime)
                        # algorithm.MarketOrder(security, quantity, tag=expiryStringTag, orderProperties=order_properties)
                        algorithm.MarketOrder(security, quantity)

            # leading to lingering small orders - issue?
            # self.targetsCollection.ClearFulfilled(algorithm)
            self.targetsCollection.Clear()

            # print all CashBook Positions after trading
            # for cash in self.algo.Portfolio.CashBook.Values:
            #    self.algo.Log(f"Holding {cash.Amount} of {cash.Symbol} ({cash.ValueInAccountCurrency} BUSD value)")

    # Optional: Securities changes event for handling new securities.
    def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Security additions and removals are pushed here.
        # This can be used for setting up algorithm state.
        # changes.AddedSecurities
        # changes.RemovedSecurities
        pass
#region imports
from AlgorithmImports import *
import datetime
#endregion

# https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts
# Portfolio construction scaffolding class; basic method args.

class MyPortfolioConstructionModel(PortfolioConstructionModel):
    def __init__(self, rebalance, mainAlgo):
        '''Initialize a new instance of MyPortfolioConstructionModel
        Args:
            rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
                              If None will be ignored.
                              The function returns the next expected rebalance time for a given algorithm UTC DateTime.
                              The function returns null if unknown, in which case the function will be called again in the
                              next loop. Returning current time will trigger rebalance.
            portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)'''
        super().__init__()
        self.algo = mainAlgo
        self.portfolioBias = PortfolioBias.LongShort

        # If the argument is an instance of Resolution or Timedelta
        # Redefine rebalancingFunc
        rebalancingFunc = rebalance
        if isinstance(rebalance, int):
            rebalance = Extensions.ToTimeSpan(rebalance)
        if isinstance(rebalance, timedelta):
            rebalancingFunc = lambda dt: dt + rebalance
        if rebalancingFunc:
            self.SetRebalancingFunc(rebalancingFunc)

    # REQUIRED: Will determine the target percent for each insight
    def DetermineTargetPercent(self, activeInsights):
        '''Will determine the target percent for each insight
        Args:
            activeInsights: The active insights to generate a target for'''
        result = {}
        # give equal weighting to each security
        count = sum(x.Direction != InsightDirection.Flat and self.RespectPortfolioBias(x) for x in activeInsights)
        percent = 0 if count == 0 else 1.0 / count
        for insight in activeInsights:
            # return insight only when all active insights have the same direction            
            if all(insight.Direction == other_insights.Direction for other_insights in activeInsights):
                result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * percent
            else:
                result[insight] = (InsightDirection.Flat) * percent
        return result

    # Determines if the portfolio should be rebalanced base on the provided rebalancing func
    def IsRebalanceDue(self, insights: List[Insight], algorithmUtc: datetime) -> bool:
        return True
    
    def RespectPortfolioBias(self, insight):
        '''Method that will determine if a given insight respects the portfolio bias
        Args:
            insight: The insight to create a target for
        '''
        return self.portfolioBias == PortfolioBias.LongShort or insight.Direction == self.portfolioBias

    # OPTIONAL: Security change details
    def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        pass
# region imports
from AlgorithmImports import *
from AlphaModelUp import *
from AlphaModelDown import *
from ExecutionModel import *
from PortfolioConstructionModel import *
# endregion

class FocusedBrownJaguar(QCAlgorithm):

    def Initialize(self):
        # *** initial configurations and backtest ***
        self.SetStartDate(2022, 9, 30)  # Set Start Date
        self.SetEndDate(2022, 12, 22)  # Set End Date
        self.SetSecurityInitializer(self.CustomSecurityInitializer)
        self.SetAccountCurrency("BUSD") # Set Account Currency
        self.SetCash("BUSD", 100000, 1)  # Set Strategy Cash

        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(CryptoCoarseFundamentalUniverse(Market.Binance, self.UniverseSettings, self.universe_filter))
        self.SetBenchmark(Symbol.Create("BTCUSDC", SecurityType.Crypto, Market.Binance))
        self.ethSecurity = self.AddCrypto("ETHBUSD", Resolution.Daily, market=Market.Binance)
        self.eth = self.ethSecurity.Symbol

        # *** Framework initialization ***
        self.AddAlpha(MyAlphaModelUp(self))
        self.AddAlpha(MyAlphaModelDown(self))
        self.SetPortfolioConstruction(MyPortfolioConstructionModel(self.DateRules.EveryDay(), self))                
        self.AddRiskManagement(NullRiskManagementModel())
        self.SetExecution(ImmediateExecutionModel())

        # *** Brokerage settings
        self.SetBrokerageModel(BrokerageName.Binance, AccountType.Margin)
        self.order_properties = BinanceOrderProperties()
    
    def OnData(self, data):
        pass
    
    def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
        pass
    
    def CustomSecurityInitializer(self, security):
        security.SetFeeModel(BinanceFeeModel())
        security.SetFillModel(ImmediateFillModel())
        security.SetBuyingPowerModel(SecurityMarginModel(1))
       
    def universe_filter(self, crypto_coarse: List[CryptoCoarseFundamental]) -> List[Symbol]:
        # for demonstration purposes, return only Ethereum
        static_universe_list = [self.eth]
        return static_universe_list