Overall Statistics
Total Orders
2723
Average Win
-35.75%
Average Loss
7.39%
Compounding Annual Return
-7.907%
Drawdown
85.400%
Expectancy
-1.975
Start Equity
100000
End Equity
73371.46
Net Profit
-26.629%
Sharpe Ratio
0.211
Sortino Ratio
0.277
Probabilistic Sharpe Ratio
3.739%
Loss Rate
75%
Win Rate
25%
Profit-Loss Ratio
-4.84
Alpha
-0.02
Beta
2.05
Annual Standard Deviation
0.649
Annual Variance
0.421
Information Ratio
0.1
Tracking Error
0.602
Treynor Ratio
0.067
Total Fees
$2206.45
Estimated Strategy Capacity
$23000.00
Lowest Capacity Asset
AREB XVTYLRAYE2XX
Portfolio Turnover
1.15%
# region imports
from AlgorithmImports import *
# endregion

from datetime import datetime, timedelta
import numpy as np

class StockScreener(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2020, 11, 17)
        self.SetCash(100000)
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.Asynchronous = True
        
        # Add specific stocks from Finviz results to test data availability
        self.symbols = [
            "DDD",  # 3D Systems Corp
            "GILT", # Gilat Satellite Networks
            "HRYU", # Hanryu Holdings Inc
            "RRGB", # Red Robin Gourmet Burgers
            "SPCE", # Virgin Galactic Holdings
            "UNCY", # Unicycive Therapeutics
            "VSTE", # Vast Renewables
            "WDH"   # Waterdrop Inc ADR
        ]
        
        # Test data availability for specific symbols
        for symbol in self.symbols:
            try:
                equity = self.AddEquity(symbol, Resolution.Daily)
                history = self.History(equity.Symbol, 200, Resolution.Daily)
                # Convert to pandas DataFrame first
                history_df = history.loc[str(equity.Symbol)] if len(history) > 0 else None
                
                if history_df is not None and len(history_df) > 0:
                    self.Debug(f"Data available for {symbol}: {len(history_df)} days of history")
                else:
                    self.Debug(f"No historical data available for {symbol}")
            except Exception as e:
                self.Debug(f"Error adding {symbol}: {str(e)}")
        
        # Add universe selection
        self.AddUniverseSelection(FundamentalUniverseSelectionModel(self.FilterStocks))
        self.holdings = {}

    def FilterStocks(self, fundamental: List[Fundamental]) -> List[Symbol]:
        filtered_stocks = []
        
        for stock in fundamental:
            try:
                # Log initial check
                self.Debug(f"\nAnalyzing {stock.symbol}:")
                self.Debug(f"Price: ${stock.price:.2f}, Volume: {stock.volume}")
                
                # Basic filters with logging
                if not stock.has_fundamental_data:
                    self.Debug(f"{stock.symbol}: No fundamental data")
                    continue
                if stock.price >= 7:
                    self.Debug(f"{stock.symbol}: Price ${stock.price:.2f} >= $7")
                    continue
                if stock.volume <= 50000:
                    self.Debug(f"{stock.symbol}: Volume {stock.volume} <= 50K")
                    continue
                
                history = self.History(stock.symbol, 200, Resolution.Daily)
                history_df = history.loc[str(stock.symbol)] if len(history) > 0 else None
                
                if history_df is None or len(history_df) < 200:
                    self.Debug(f"{stock.symbol}: Insufficient history data")
                    continue
                
                closes = history_df['close']
                current_price = closes.iloc[-1]
                
                # Calculate and log technical indicators
                sma20 = closes[-20:].mean()
                sma50 = closes[-50:].mean()
                sma200 = closes[-200:].mean()
                
                self.Debug(f"{stock.symbol} Technical Analysis:" +
                        f"\n  Current Price: ${current_price:.2f}" +
                        f"\n  SMA20: ${sma20:.2f}" +
                        f"\n  SMA50: ${sma50:.2f}" +
                        f"\n  SMA200: ${sma200:.2f}")
                
                # Check each condition individually
                conditions = {
                    "Price < SMA20": current_price < sma20,
                    "Price > SMA50": current_price > sma50,
                    "Price > SMA200": current_price > sma200,
                    "Price Up": closes.iloc[-1] > closes.iloc[-2]
                }
                
                # Log which conditions passed/failed
                for condition_name, condition_met in conditions.items():
                    self.Debug(f"{stock.symbol} - {condition_name}: {'✓' if condition_met else '✗'}")
                
                if all(conditions.values()):
                    filtered_stocks.append(stock.symbol)
                    shares = int(self.Portfolio.TotalPortfolioValue * 0.1 / current_price)
                    if shares > 0:
                        self.MarketOrder(stock.symbol, shares)
                        self.Debug(f"✓ ORDER PLACED: {shares} shares of {stock.symbol} at ${current_price:.2f}")
                
            except Exception as e:
                self.Debug(f"Error processing {stock.symbol}: {str(e)}")
        
        return filtered_stocks

    def OnData(self, data):
        pass