Overall Statistics |
Total Orders
4883
Average Win
0.12%
Average Loss
-0.12%
Compounding Annual Return
10.281%
Drawdown
34.200%
Expectancy
0.436
Start Equity
100000
End Equity
382931.20
Net Profit
282.931%
Sharpe Ratio
0.501
Sortino Ratio
0.433
Probabilistic Sharpe Ratio
4.903%
Loss Rate
27%
Win Rate
73%
Profit-Loss Ratio
0.96
Alpha
0.001
Beta
0.69
Annual Standard Deviation
0.121
Annual Variance
0.015
Information Ratio
-0.3
Tracking Error
0.084
Treynor Ratio
0.088
Total Fees
$294.78
Estimated Strategy Capacity
$390000000.00
Lowest Capacity Asset
LINTA TIIB7Z82AFS5
Portfolio Turnover
0.65%
|
# https://quantpedia.com/strategies/alpha-cloning-following-13f-fillings/ # # Create a universe of active mutual fund managers. # Use 13F filings to identify the “best idea” stocks for each manager. # Invest in the stocks, which are the “best ideas” for most of the managers. # # QC Implementation changes: # - Investor preferences was downloaded from https://www.insidermonkey.com/hedge-fund/browse/A/ # - Investors list consists of first 10 investors in each browse letter and from lists in basic and premium cards on https://www.gurufocus.com/guru/list # - Investor preferences are modeled to be known 2 months after announcement. #region imports from AlgorithmImports import * import numpy as np from dateutil.relativedelta import relativedelta #endregion class AlphaCloningFollowing13FFillings(QCAlgorithm): def Initialize(self) -> None: self.SetStartDate(2011, 1, 1) self.SetCash(100_000) self.UniverseSettings.Leverage = 5 self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.FundamentalSelectionFunction) self.Settings.MinimumOrderMarginPortfolioPercentage = 0.0 self.settings.daily_precise_end_time = False self.months_lag: int = 2 # Lag for getting investors preferences report self.last_update_date: datetime.date self.weights: dict[Symbol, float] = {} self.investors_preferences: dict[datetime, dict[str, int]] = self.GetPreferences() self.selection_flag: bool = False market: Symbol = self.AddEquity('SPY', Resolution.Daily).Symbol self.Schedule.On(self.DateRules.MonthStart(market), self.TimeRules.AfterMarketOpen(market), self.Selection) def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.AddedSecurities: security.SetFeeModel(CustomFeeModel()) def FundamentalSelectionFunction(self, fundamental: List[Fundamental]) -> List[Symbol]: if not self.selection_flag: return Universe.Unchanged selected_report: dict[str, int] = None min_date: datetime.date = self.Time.date() - relativedelta(months=self.months_lag+1) # quarterly data max_date: datetime.date = self.Time.date() for date in self.investors_preferences: # Get latest report if date >= min_date and date <= max_date: selected_report = self.investors_preferences[date] # Report might not be selected, because there are no data for that date if selected_report is None: return [] # Select universe based on report selected: list[Symbol] = [x.Symbol for x in fundamental if x.Symbol.Value in selected_report] # Calculate total preferences votes for selected report total_preferences_votes: int = sum([x[1] for x in selected_report.items()]) # Calculate weight for each stock in selected universe for symbol in selected: # weight = total stock preferences votes / total investor votes in selected report self.weights[symbol] = selected_report[symbol.Value] / total_preferences_votes return selected def OnData(self, slice: Slice) -> None: if not self.selection_flag: return self.selection_flag = False # Trade Execution portfolio: List[PortfolioTarget] = [PortfolioTarget(symbol, w) for symbol, w in self.weights.items() if symbol in slice and slice[symbol]] self.SetHoldings(portfolio, True) self.weights.clear() def Selection(self) -> None: if self.Time.month % 3 == 2: self.selection_flag = True def GetPreferences(self) -> Dict[datetime, Dict[str, int]]: preferences: dict[datetime, dict[str, int]] = {} csv_string_file = self.Download('data.quantpedia.com/backtesting_data/economic/investors_preferences.csv') lines: list[str] = csv_string_file.split('\r\n') # Skip csv header in loop for line in lines[1:]: line_split: list[str] = line.split(';') date: datetime.date = datetime.strptime(line_split[0], "%d.%m.%Y").date() preferences[date]: dict[str, int] = {} for ticker in line_split[1:]: if ticker not in preferences[date]: preferences[date][ticker] = 0 preferences[date][ticker] += 1 # Set last update date if line == lines[-1]: self.last_update_date = date return preferences # Custom fee model class CustomFeeModel(FeeModel): def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee: fee: float = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005 return OrderFee(CashAmount(fee, "USD"))