""" Super Quality strategy implementation. """ from datetime import date from decimal import Decimal from app.services.strategy.base import BaseStrategy from app.schemas.strategy import StockFactor, StrategyResult, UniverseFilter class QualityStrategy(BaseStrategy): """Super Quality strategy using F-Score and quality factors.""" strategy_name = "quality" def run( self, universe_filter: UniverseFilter, top_n: int, base_date: date = None, min_fscore: int = 7, ) -> StrategyResult: if base_date is None: base_date = date.today() # Get universe stocks = self.get_universe(universe_filter) tickers = [s.ticker for s in stocks] stock_map = {s.ticker: s for s in stocks} if not tickers: return StrategyResult( strategy_name=self.strategy_name, base_date=base_date, universe_count=0, result_count=0, stocks=[], ) # Get data valuations = self.factor_calc.get_valuations(tickers, base_date) sectors = self.factor_calc.get_sectors(tickers) fscores = self.factor_calc.calculate_fscore(tickers) quality_scores = self.factor_calc.calculate_quality_scores(tickers, base_date) # Filter by F-Score qualified_tickers = [t for t in tickers if fscores.get(t, 0) >= min_fscore] # Build results results = [] for ticker in qualified_tickers: stock = stock_map[ticker] val = valuations.get(ticker) q_score = quality_scores.get(ticker, Decimal("0")) fscore = fscores.get(ticker, 0) results.append(StockFactor( ticker=ticker, name=stock.name, market=stock.market, sector_name=sectors.get(ticker), market_cap=int(stock.market_cap / 100_000_000) if stock.market_cap else None, close_price=Decimal(str(stock.close_price)) if stock.close_price else None, per=Decimal(str(val.per)) if val and val.per else None, pbr=Decimal(str(val.pbr)) if val and val.pbr else None, dividend_yield=Decimal(str(val.dividend_yield)) if val and val.dividend_yield else None, quality_score=q_score, total_score=q_score, fscore=fscore, )) # Sort by quality score results.sort(key=lambda x: x.total_score or Decimal("0"), reverse=True) for i, r in enumerate(results[:top_n], 1): r.rank = i return StrategyResult( strategy_name=self.strategy_name, base_date=base_date, universe_count=len(stocks), result_count=min(top_n, len(results)), stocks=results[:top_n], )