Overall Statistics
Total Trades
32
Average Win
56.33%
Average Loss
-22.20%
Compounding Annual Return
5.452%
Drawdown
85.600%
Expectancy
1.211
Net Profit
254.755%
Sharpe Ratio
0.202
Sortino Ratio
0.223
Probabilistic Sharpe Ratio
0.001%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
2.54
Alpha
0.008
Beta
0.88
Annual Standard Deviation
0.262
Annual Variance
0.068
Information Ratio
0.011
Tracking Error
0.172
Treynor Ratio
0.06
Total Fees
$244.56
Estimated Strategy Capacity
$130000000.00
Lowest Capacity Asset
AAPL R735QTJ8XC9X
Portfolio Turnover
0.35%
from AlgorithmImports import *
import pandas as pd
from io import StringIO
class MorningStarDataAlgorithm(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2000, 1, 1) # Cambiar fechas en las que quieres que corra
        self.SetEndDate(2023, 11, 1)
        self.SetCash(100000)
        self.UniverseSettings.Resolution = Resolution.Hour
        self.SetBenchmark("QQQ")
        self.num_symbols = 3 # Número de acciones en el portafolio
        self.AddUniverse(self.FundamentalSelection)
        self.Settings.FreePortfolioValuePercentage = 0.05
        self.Settings.MinimumOrderMarginPortfolioPercentage = 0
        self.Schedule.On(self.DateRules.On(self.EndDate),
                 self.TimeRules.At(15, 55),
                 self.Liquidate)
        
        self.changes = None
        #i = int(self.GetParameter('i'))
        
        self.percentages = self.get_text()
        self.x1 = self.percentages.loc[65,'Number1']
        self.x2 = self.percentages.loc[65,'Number2']
        self.x3 = self.percentages.loc[65,'Number3']
        self.Debug('1st stock percentage '+str(self.x1))
        self.Debug('2nd stock percentage '+str(self.x2))
        self.Debug('3rd stock percentage '+str(self.x3))
        self.e1 = None
        self.e2 = None
        self.e3 = None
        self.sorted_added_securities = []
        self.active_securities = []
    
    def FundamentalSelection(self, fundamental):
        selected = [c for c in fundamental if c.HasFundamentalData and c.SecurityReference.ExchangeId == "NAS"]
        self.sorted_by_mc = sorted(selected, key=lambda f: f.MarketCap, reverse=True)
        self.sorted_by_mc = self.sorted_by_mc[:self.num_symbols]
        self.Log(f"Symbols: {[x.Symbol.Value for x in self.sorted_by_mc]}")
        return [ f.Symbol for f in self.sorted_by_mc ]
    
    def OnData(self, data):
        # if we have no changes, do nothing
        if self.changes is None and self.previous_sorted_by_mc == self.sorted_by_mc: return
       
        try:
        # liquidate removed securities
            for security in self.changes.RemovedSecurities:
                    self.Liquidate(security.Symbol)
                    if security.Symbol.Value == self.e1:
                        self.Debug("Liquidated Stock 1: " + str(security.Symbol.Value))
                        self.e1 = None
                    if security.Symbol.Value == self.e2:
                        self.Debug("Liquidated Stock 2: " + str(security.Symbol.Value))
                        self.e2 = None
                    if security.Symbol.Value == self.e3:
                        self.Debug("Liquidated Stock 3: " + str(security.Symbol.Value))
                        self.e3 = None
        except:
            pass
        
        if len(self.active_securities)==3:
            self.Rebalance_weights()
        
        self.previous_sorted_by_mc = self.sorted_by_mc.copy()
        self.changes = None
        sorted_added_securities = []
   
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
        for security in self.changes.RemovedSecurities:
            self.Debug('Removed '+ str(security.Symbol))
            if security in self.active_securities:
                self.active_securities.remove(security)
        
        for security in self.changes.AddedSecurities:
            self.active_securities.append(security)
            self.Debug('Added stock '+str(security.Symbol))
   
    def Rebalance_weights(self):
        # Sort active securities by Market Cap
        self.active_securities = sorted(self.active_securities, key=lambda x: x.Fundamentals.MarketCap, reverse=True)
        self.Log(f"Inside Rebalance_weights: {[x.Symbol.Value for x in self.active_securities]}")
        # Set portfolio weights accordingly
        # Check if the security is already invested with that percentage
        

        if self.active_securities[0].Invested:
            self.Debug('is invested')
            percentage_invested_1 = self.Portfolio[self.active_securities[0].Symbol].Quantity * self.Securities[self.active_securities[0].Symbol].Price / self.Portfolio.TotalPortfolioValue
            if not percentage_invested_1 >= (self.x1-0.09):
                self.Debug('Port per 1 '+str(percentage_invested_1))
                self.SetHoldings(self.active_securities[0].Symbol, self.x1)
                self.Debug("Bought Stock 1: " + str(self.active_securities[0].Symbol))
                self.e1 = self.active_securities[0].Symbol.Value
        
        elif not self.active_securities[0].Invested:
            self.Debug('not invested')
            self.SetHoldings(self.active_securities[0].Symbol, self.x1)
            self.Debug("Bought Stock 1: " + str(self.active_securities[0].Symbol))
            self.e1 = self.active_securities[0].Symbol.Value
        
        if self.active_securities[1].Invested:
            percentage_invested_2 = self.Portfolio[self.active_securities[1].Symbol].Quantity * self.Securities[self.active_securities[1].Symbol].Price / self.Portfolio.TotalPortfolioValue
            if percentage_invested_2 >= (self.x2-0.09):
                self.Debug('Port per 2 '+str(percentage_invested_2))
                self.SetHoldings(self.active_securities[1].Symbol, self.x2)
                self.Debug("Bought Stock 2: " + str(self.active_securities[1].Symbol))
                self.e2 = self.active_securities[1].Symbol.Value
        
        elif not self.active_securities[1].Invested:
            self.SetHoldings(self.active_securities[1].Symbol, self.x2)
            self.Debug("Bought Stock 2: " + str(self.active_securities[1].Symbol))
            self.e2 = self.active_securities[1].Symbol.Value
        
        if self.active_securities[2].Invested:
            percentage_invested_3 = self.Portfolio[self.active_securities[2].Symbol].Quantity * self.Securities[self.active_securities[2].Symbol].Price / self.Portfolio.TotalPortfolioValue
            if percentage_invested_3 >= (self.x3-0.09):
                self.Debug('Port per 3 '+str(percentage_invested_3))
                self.SetHoldings(self.active_securities[2].Symbol, self.x3)
                self.Debug("Bought Stock 3: " + str(self.active_securities[2].Symbol))
                self.e3 = self.active_securities[2].Symbol.Value
        elif not self.active_securities[2].Invested:
            self.SetHoldings(self.active_securities[2].Symbol, self.x3)
            self.Debug("Bought Stock 3: " + str(self.active_securities[2].Symbol))
            self.e3 = self.active_securities[2].Symbol.Value
       
    #def Liquidate(self):
    #    self.Liquidate()
    def get_text(self):
        url = 'https://www.dropbox.com/scl/fi/cgcdnn328vi7mqwjsgoi6/portfolio_weights_1.csv?rlkey=1dam9mp6430gj4xt32qlpwjx6&dl=1'
        csv_data = self.Download(url)
        # Use pd.read_csv to directly read the CSV data into a DataFrame
        df = pd.read_csv(StringIO(csv_data), index_col=0)
        return df