Low Float Runners Strategy

Concepto Base

La estrategia Low Float Runners se enfoca en stocks con float extremadamente bajo (típicamente <15M shares) que experimentan momentum buying significativo. Estos stocks pueden tener movimientos explosivos debido a la limitada supply de shares disponibles para trading.

Fundamentos de Low Float

¿Por Qué Low Float Causa Explosive Moves?

class LowFloatMechanics:
    def __init__(self):
        self.float_categories = {
            'micro_float': {'range': (1_000_000, 5_000_000), 'volatility': 'extreme'},
            'low_float': {'range': (5_000_000, 15_000_000), 'volatility': 'high'},
            'small_float': {'range': (15_000_000, 30_000_000), 'volatility': 'medium'}
        }
        
    def calculate_supply_demand_dynamics(self, float_shares, daily_volume):
        """Calcular dynamics de supply/demand"""
        
        # Institutional lockup estimate (typically 60-80% of float)
        locked_shares = float_shares * 0.70
        tradeable_float = float_shares - locked_shares
        
        # Daily turnover rate
        turnover_rate = daily_volume / tradeable_float if tradeable_float > 0 else 0
        
        # Scarcity factor
        scarcity_score = min(100, (1 / (tradeable_float / 1_000_000)) * 20)
        
        return {
            'tradeable_float': tradeable_float,
            'daily_turnover_rate': turnover_rate,
            'scarcity_score': scarcity_score,
            'explosive_potential': turnover_rate > 0.5 and scarcity_score > 60,
            'risk_level': 'extreme' if scarcity_score > 80 else 'high'
        }
        
    def estimate_price_impact(self, order_size, tradeable_float, current_price):
        """Estimar impacto en precio de orden grande"""
        
        # Simplified market impact model
        float_ratio = order_size / tradeable_float
        
        # Price impact increases exponentially with order size
        base_impact = float_ratio * 0.1  # 10% impact per 1% of float
        explosive_multiplier = 1 + (float_ratio * 2)  # Exponential component
        
        estimated_impact = base_impact * explosive_multiplier
        
        return {
            'estimated_price_impact_pct': estimated_impact,
            'new_price_estimate': current_price * (1 + estimated_impact),
            'market_impact_category': self.categorize_impact(estimated_impact)
        }
    
    def categorize_impact(self, impact_pct):
        """Categorizar impacto en mercado"""
        if impact_pct > 0.20:
            return 'explosive'
        elif impact_pct > 0.10:
            return 'high'
        elif impact_pct > 0.05:
            return 'medium'
        else:
            return 'low'

Screening System

Multi-Tier Filtering

class LowFloatScreener:
    def __init__(self):
        self.screening_tiers = {
            'tier_1_universe': self.tier_1_screening,    # 8000 → 200 stocks
            'tier_2_activity': self.tier_2_screening,    # 200 → 50 stocks  
            'tier_3_momentum': self.tier_3_screening,    # 50 → 10 stocks
            'tier_4_execution': self.tier_4_screening    # 10 → 3-5 stocks
        }
    
    def tier_1_screening(self, stock_universe):
        """Filtro inicial - características básicas"""
        
        filtered = []
        
        for stock in stock_universe:
            # Basic float requirements
            if not (1_000_000 <= stock.float_shares <= 15_000_000):
                continue
                
            # Price range for momentum potential
            if not (3.00 <= stock.price <= 50.00):
                continue
                
            # Minimum liquidity
            if stock.avg_volume_20d < 500_000:
                continue
                
            # Market cap range
            if not (10_000_000 <= stock.market_cap <= 500_000_000):
                continue
                
            # Exchange quality
            if stock.exchange not in ['NASDAQ', 'NYSE']:
                continue
                
            # Basic financial health
            if stock.cash_per_share < 0.10:  # Some cash cushion
                continue
                
            filtered.append(stock)
        
        return filtered
    
    def tier_2_screening(self, stocks):
        """Filtro de actividad - anomalías de volumen/precio"""
        
        filtered = []
        
        for stock in stocks:
            activity_metrics = self.calculate_activity_metrics(stock)
            
            # Volume anomaly
            if activity_metrics['volume_ratio'] < 2.0:  # At least 2x normal volume
                continue
                
            # Price movement
            if activity_metrics['price_change_abs'] < 0.15:  # At least 15% move
                continue
                
            # Intraday range
            if activity_metrics['daily_range'] < 0.20:  # At least 20% range
                continue
                
            # Relative strength
            if activity_metrics['relative_strength'] < 60:  # vs SPY
                continue
            
            stock.activity_score = self.calculate_activity_score(activity_metrics)
            
            if stock.activity_score >= 70:
                filtered.append(stock)
        
        return sorted(filtered, key=lambda x: x.activity_score, reverse=True)
    
    def tier_3_screening(self, stocks):
        """Filtro de momentum - calidad del setup"""
        
        filtered = []
        
        for stock in stocks:
            momentum_analysis = self.analyze_momentum_quality(stock)
            
            # Momentum persistence
            if not momentum_analysis['has_persistent_momentum']:
                continue
                
            # Technical setup
            if momentum_analysis['technical_score'] < 65:
                continue
                
            # Catalyst verification
            if not momentum_analysis['has_legitimate_catalyst']:
                continue
                
            # Risk/reward profile
            if momentum_analysis['risk_reward_ratio'] < 2.0:
                continue
            
            stock.momentum_score = momentum_analysis['total_score']
            stock.risk_reward = momentum_analysis['risk_reward_ratio']
            
            filtered.append(stock)
        
        return filtered
    
    def calculate_activity_metrics(self, stock):
        """Calcular métricas de actividad"""
        
        volume_ratio = stock.volume_today / stock.avg_volume_20d
        price_change = (stock.price - stock.prev_close) / stock.prev_close
        daily_range = (stock.high - stock.low) / stock.low
        
        # Relative strength vs market
        spy_change = self.get_spy_performance()
        relative_strength = ((price_change - spy_change) + 1) * 50  # Normalized 0-100
        
        return {
            'volume_ratio': volume_ratio,
            'price_change': price_change,
            'price_change_abs': abs(price_change),
            'daily_range': daily_range,
            'relative_strength': relative_strength
        }

Momentum Quality Analysis

class MomentumQualityAnalyzer:
    def __init__(self):
        self.quality_factors = [
            'volume_profile',
            'price_structure', 
            'catalyst_strength',
            'technical_setup',
            'risk_reward'
        ]
    
    def analyze_momentum_quality(self, stock_data):
        """Análisis completo de calidad del momentum"""
        
        # 1. Volume Profile Analysis
        volume_analysis = self.analyze_volume_profile(stock_data)
        
        # 2. Price Structure
        price_structure = self.analyze_price_structure(stock_data)
        
        # 3. Catalyst Assessment
        catalyst_analysis = self.assess_catalyst_strength(stock_data)
        
        # 4. Technical Setup
        technical_analysis = self.analyze_technical_setup(stock_data)
        
        # 5. Risk/Reward Calculation
        risk_reward = self.calculate_risk_reward(stock_data)
        
        # Composite scoring
        total_score = self.calculate_composite_score({
            'volume': volume_analysis['score'],
            'price_structure': price_structure['score'],
            'catalyst': catalyst_analysis['score'],
            'technical': technical_analysis['score'],
            'risk_reward': min(100, risk_reward * 25)  # Cap at 100
        })
        
        return {
            'has_persistent_momentum': volume_analysis['persistent'] and price_structure['strong'],
            'has_legitimate_catalyst': catalyst_analysis['legitimate'],
            'technical_score': technical_analysis['score'],
            'risk_reward_ratio': risk_reward,
            'total_score': total_score,
            'components': {
                'volume': volume_analysis,
                'price_structure': price_structure,
                'catalyst': catalyst_analysis,
                'technical': technical_analysis
            }
        }
    
    def analyze_volume_profile(self, stock_data):
        """Analizar perfil de volumen"""
        
        score = 0
        
        # Volume surge magnitude
        volume_ratio = stock_data.volume_today / stock_data.avg_volume_20d
        if volume_ratio > 10:
            score += 30
        elif volume_ratio > 5:
            score += 20
        elif volume_ratio > 3:
            score += 10
        
        # Volume trend
        if stock_data.volume_increasing_trend:
            score += 20
        
        # Volume at key levels
        if stock_data.volume_at_breakout > stock_data.avg_volume * 2:
            score += 25
        
        # Sustained volume
        if stock_data.avg_volume_last_hour > stock_data.avg_hourly_volume * 1.5:
            score += 15
        
        # Distribution signs
        if stock_data.has_distribution_volume:
            score -= 20
        
        persistent = score >= 60 and volume_ratio >= 3
        
        return {
            'score': min(100, score),
            'persistent': persistent,
            'volume_ratio': volume_ratio,
            'trend': 'increasing' if stock_data.volume_increasing_trend else 'decreasing'
        }
    
    def analyze_price_structure(self, stock_data):
        """Analizar estructura de precio"""
        
        score = 0
        
        # Higher highs and higher lows
        if stock_data.has_higher_highs:
            score += 25
        if stock_data.has_higher_lows:
            score += 25
        
        # Breakout quality
        if stock_data.clean_breakout:
            score += 20
        
        # Support/resistance clarity
        if stock_data.clear_support_resistance:
            score += 15
        
        # Momentum acceleration
        if stock_data.accelerating_momentum:
            score += 15
        
        strong = score >= 70
        
        return {
            'score': min(100, score),
            'strong': strong,
            'pattern': self.identify_price_pattern(stock_data),
            'breakout_quality': 'clean' if stock_data.clean_breakout else 'messy'
        }

Entry Strategies

Progressive Entry System

class LowFloatEntryManager:
    def __init__(self, risk_tolerance='medium'):
        self.risk_tolerance = risk_tolerance
        self.entry_approaches = {
            'breakout': self.breakout_entry,
            'pullback': self.pullback_entry,
            'momentum': self.momentum_entry,
            'scalp': self.scalp_entry
        }
    
    def determine_entry_approach(self, stock_data, momentum_analysis):
        """Determinar mejor approach de entrada"""
        
        # Factor analysis
        volume_ratio = stock_data.volume_today / stock_data.avg_volume_20d
        price_momentum = stock_data.price_momentum_score
        volatility = stock_data.atr_14 / stock_data.price
        
        # Decision matrix
        if (volume_ratio > 10 and price_momentum > 80 and 
            stock_data.at_resistance):
            return 'breakout'
        elif (stock_data.pullback_to_support and 
              momentum_analysis['total_score'] > 75):
            return 'pullback'
        elif (volume_ratio > 5 and price_momentum > 70):
            return 'momentum'
        else:
            return 'scalp'
    
    def breakout_entry(self, stock_data, momentum_analysis):
        """Entrada en breakout de resistencia"""
        
        resistance_level = stock_data.resistance_level
        current_price = stock_data.price
        
        return {
            'strategy_type': 'breakout',
            'entry_levels': {
                'aggressive': {
                    'price': resistance_level * 1.005,  # Just above resistance
                    'size_pct': 0.40,
                    'trigger': 'resistance_break',
                    'confirmation': 'volume_surge'
                },
                'conservative': {
                    'price': resistance_level * 1.02,   # Clear break
                    'size_pct': 0.60,
                    'trigger': 'confirmed_breakout',
                    'confirmation': 'sustained_volume'
                }
            },
            'stop_loss': resistance_level * 0.97,  # 3% below resistance
            'targets': {
                'target_1': resistance_level * 1.15,  # 15% above breakout
                'target_2': resistance_level * 1.30,  # 30% extended move
                'measured_move': self.calculate_measured_move(stock_data)
            },
            'risk_management': {
                'max_hold_time': '4_hours',
                'profit_take_schedule': [0.25, 0.35, 0.40],  # % of position
                'profit_levels': [0.08, 0.15, 0.25]           # % profit levels
            }
        }
    
    def pullback_entry(self, stock_data, momentum_analysis):
        """Entrada en pullback a soporte"""
        
        support_level = stock_data.support_level
        vwap = stock_data.vwap
        
        return {
            'strategy_type': 'pullback',
            'entry_levels': {
                'support_test': {
                    'price': support_level * 1.01,
                    'size_pct': 0.50,
                    'trigger': 'support_hold',
                    'confirmation': 'buying_volume'
                },
                'vwap_reclaim': {
                    'price': vwap * 1.005,
                    'size_pct': 0.50,
                    'trigger': 'vwap_reclaim',
                    'confirmation': 'momentum_resumption'
                }
            },
            'stop_loss': support_level * 0.95,  # 5% below support
            'targets': {
                'previous_high': stock_data.recent_high,
                'extension_target': stock_data.recent_high * 1.10
            },
            'time_limits': {
                'entry_window': '2_hours',
                'max_consolidation': '1_hour'
            }
        }
    
    def momentum_entry(self, stock_data, momentum_analysis):
        """Entrada en momentum continuation"""
        
        current_price = stock_data.price
        
        return {
            'strategy_type': 'momentum',
            'entry_approach': 'chase_with_discipline',
            'entry_levels': {
                'immediate': {
                    'price': current_price * 1.01,  # 1% premium
                    'size_pct': 0.30,
                    'trigger': 'market_order',
                    'max_slippage': 0.02
                },
                'strength_add': {
                    'price': current_price * 1.05,  # Add on strength
                    'size_pct': 0.40,
                    'trigger': 'momentum_acceleration',
                    'volume_requirement': '3x_spike'
                },
                'final_add': {
                    'price': current_price * 1.08,  # Final add
                    'size_pct': 0.30,
                    'trigger': 'explosive_move',
                    'max_chase_level': current_price * 1.10
                }
            },
            'stop_loss': current_price * 0.92,  # 8% stop
            'profit_strategy': 'quick_scalp',
            'targets': {
                'quick_target': current_price * 1.12,  # 12% quick
                'extended_target': current_price * 1.25 # 25% if momentum continues
            }
        }

Risk Management Extremo

Position Sizing para High Volatility

class LowFloatRiskManager:
    def __init__(self, account_size, max_portfolio_exposure=0.15):
        self.account_size = account_size
        self.max_portfolio_exposure = max_portfolio_exposure
        self.low_float_max_exposure = 0.08  # Never more than 8% in one low float
        
    def calculate_low_float_position_size(self, stock_data, entry_plan, momentum_score):
        """Cálculo especializado para low float"""
        
        # Base calculations
        entry_price = entry_plan['entry_price']
        stop_price = entry_plan['stop_loss']
        risk_per_share = abs(entry_price - stop_price)
        
        # Volatility adjustment
        volatility_factor = self.calculate_volatility_factor(stock_data)
        
        # Float size adjustment
        float_factor = self.calculate_float_factor(stock_data.float_shares)
        
        # Momentum quality adjustment
        momentum_factor = self.calculate_momentum_factor(momentum_score)
        
        # Base risk amount
        base_risk = self.account_size * 0.02  # 2% base risk
        
        # Adjusted risk
        adjusted_risk = (base_risk * volatility_factor * 
                        float_factor * momentum_factor)
        
        # Cap at low float maximum
        max_position_value = self.account_size * self.low_float_max_exposure
        max_shares_by_exposure = int(max_position_value / entry_price)
        
        # Calculate shares
        shares_by_risk = int(adjusted_risk / risk_per_share) if risk_per_share > 0 else 0
        final_shares = min(shares_by_risk, max_shares_by_exposure)
        
        return {
            'shares': final_shares,
            'position_value': final_shares * entry_price,
            'risk_amount': final_shares * risk_per_share,
            'portfolio_exposure_pct': (final_shares * entry_price) / self.account_size,
            'adjustments': {
                'volatility_factor': volatility_factor,
                'float_factor': float_factor,
                'momentum_factor': momentum_factor
            },
            'risk_level': self.assess_risk_level(stock_data, final_shares * entry_price)
        }
    
    def calculate_volatility_factor(self, stock_data):
        """Factor de ajuste por volatilidad"""
        
        atr_pct = stock_data.atr_14 / stock_data.price
        
        if atr_pct > 0.20:      # >20% daily ATR
            return 0.4          # Reduce size dramatically
        elif atr_pct > 0.15:    # >15% daily ATR
            return 0.6
        elif atr_pct > 0.10:    # >10% daily ATR
            return 0.8
        else:
            return 1.0
    
    def calculate_float_factor(self, float_shares):
        """Factor de ajuste por float size"""
        
        if float_shares < 3_000_000:      # Micro float
            return 0.5                    # Very risky
        elif float_shares < 8_000_000:    # Low float
            return 0.75
        elif float_shares < 15_000_000:   # Small float
            return 1.0
        else:
            return 1.2                    # Larger float = less risk

Dynamic Stop Management

class LowFloatStopManager:
    def __init__(self):
        self.stop_strategies = {
            'breakout': self.breakout_stops,
            'pullback': self.pullback_stops,
            'momentum': self.momentum_stops
        }
    
    def manage_stops_dynamically(self, position, current_data, strategy_type):
        """Gestión dinámica de stops para low float"""
        
        stop_strategy = self.stop_strategies.get(strategy_type, self.default_stops)
        return stop_strategy(position, current_data)
    
    def breakout_stops(self, position, current_data):
        """Stops específicos para breakout trades"""
        
        entry_price = position['entry_price']
        current_price = current_data['price']
        breakout_level = position['breakout_level']
        
        # Initial stop below breakout level
        initial_stop = breakout_level * 0.97
        
        # Trailing stop logic
        current_profit = (current_price - entry_price) / entry_price
        
        if current_profit > 0.15:  # 15% profit
            # Trail at 50% of max profit
            max_profit = (position['highest_price'] - entry_price) / entry_price
            trailing_profit = max_profit * 0.5
            trailing_stop = entry_price * (1 + trailing_profit)
            
            return {
                'stop_price': max(initial_stop, trailing_stop),
                'stop_type': 'trailing',
                'trigger_reason': 'trailing_profit'
            }
        
        elif current_profit > 0.08:  # 8% profit
            # Move stop to breakeven
            return {
                'stop_price': entry_price * 1.01,  # Slight profit
                'stop_type': 'breakeven_plus',
                'trigger_reason': 'secure_profit'
            }
        
        else:
            return {
                'stop_price': initial_stop,
                'stop_type': 'initial',
                'trigger_reason': 'risk_management'
            }
    
    def momentum_stops(self, position, current_data):
        """Stops para momentum trades - más agresivos"""
        
        entry_price = position['entry_price']
        current_price = current_data['price']
        
        # Tighter stops for momentum chasing
        initial_stop = entry_price * 0.92  # 8% stop
        
        # Quick profit protection
        current_profit = (current_price - entry_price) / entry_price
        
        if current_profit > 0.10:  # 10% profit
            # Lock in 5% profit
            return {
                'stop_price': entry_price * 1.05,
                'stop_type': 'profit_protection',
                'trigger_reason': 'momentum_profit_protection'
            }
        
        elif current_profit > 0.05:  # 5% profit
            # Move to breakeven
            return {
                'stop_price': entry_price * 1.01,
                'stop_type': 'breakeven',
                'trigger_reason': 'quick_momentum_profit'
            }
        
        else:
            return {
                'stop_price': initial_stop,
                'stop_type': 'initial_tight',
                'trigger_reason': 'momentum_risk_control'
            }
    
    def check_emergency_exit_conditions(self, position, current_data):
        """Condiciones de exit de emergencia"""
        
        emergency_conditions = []
        
        # Volume collapse
        if current_data['volume_ratio'] < 0.5:  # Volume fell below 50% of average
            emergency_conditions.append({
                'condition': 'volume_collapse',
                'severity': 'high',
                'action': 'immediate_exit'
            })
        
        # Extreme volatility spike
        current_volatility = current_data['current_atr'] / current_data['price']
        if current_volatility > 0.30:  # 30% intraday ATR
            emergency_conditions.append({
                'condition': 'extreme_volatility',
                'severity': 'high',
                'action': 'reduce_position_50pct'
            })
        
        # Multiple failed attempts at key level
        if current_data.get('failed_attempts_count', 0) >= 3:
            emergency_conditions.append({
                'condition': 'multiple_rejections',
                'severity': 'medium',
                'action': 'consider_exit'
            })
        
        # Time-based exit (low float trades should be quick)
        time_in_trade = current_data['current_time'] - position['entry_time']
        if time_in_trade.seconds > 14400:  # 4 hours
            emergency_conditions.append({
                'condition': 'time_limit_exceeded',
                'severity': 'medium',
                'action': 'exit_before_close'
            })
        
        return emergency_conditions

Profit Taking Strategy

Staged Profit Taking

class LowFloatProfitManager:
    def __init__(self):
        self.profit_stages = {
            'quick_scalp': [0.05, 0.08, 0.12],      # 5%, 8%, 12%
            'momentum_ride': [0.08, 0.15, 0.25],    # 8%, 15%, 25%
            'breakout_play': [0.10, 0.20, 0.35]     # 10%, 20%, 35%
        }
        
        self.position_reduction = {
            'quick_scalp': [0.50, 0.30, 0.20],      # Reduce position %
            'momentum_ride': [0.30, 0.40, 0.30],
            'breakout_play': [0.25, 0.35, 0.40]
        }
    
    def create_profit_plan(self, entry_price, position_size, strategy_type):
        """Crear plan de toma de ganancias"""
        
        profit_levels = self.profit_stages.get(strategy_type, self.profit_stages['momentum_ride'])
        reduction_schedule = self.position_reduction.get(strategy_type, self.position_reduction['momentum_ride'])
        
        plan = []
        remaining_shares = position_size
        
        for i, (profit_pct, reduction_pct) in enumerate(zip(profit_levels, reduction_schedule)):
            target_price = entry_price * (1 + profit_pct)
            shares_to_sell = int(position_size * reduction_pct)
            
            plan.append({
                'stage': i + 1,
                'target_price': round(target_price, 2),
                'profit_percentage': profit_pct,
                'shares_to_sell': shares_to_sell,
                'remaining_shares': remaining_shares - shares_to_sell,
                'profit_amount': shares_to_sell * entry_price * profit_pct,
                'execution_style': 'limit_order',
                'urgency': 'high' if i == 0 else 'medium'
            })
            
            remaining_shares -= shares_to_sell
        
        return {
            'profit_plan': plan,
            'strategy_type': strategy_type,
            'total_stages': len(plan),
            'final_remaining_shares': remaining_shares
        }
    
    def dynamic_profit_adjustment(self, current_position, market_conditions):
        """Ajustar profit taking basado en condiciones dinámicas"""
        
        adjustments = []
        
        # Market close approaching
        if market_conditions['minutes_to_close'] < 60:
            adjustments.append({
                'action': 'accelerate_exit',
                'reason': 'market_close',
                'adjustment': 'exit_75pct_remaining'
            })
        
        # Volume declining
        if market_conditions['volume_trend'] == 'declining':
            adjustments.append({
                'action': 'take_profits_faster',
                'reason': 'volume_declining',
                'adjustment': 'reduce_profit_targets_10pct'
            })
        
        # Extreme volatility
        if market_conditions['volatility_spike']:
            adjustments.append({
                'action': 'lock_profits',
                'reason': 'volatility_spike',
                'adjustment': 'immediate_partial_exit_50pct'
            })
        
        # Strong momentum continuation
        if (market_conditions['momentum_score'] > 85 and 
            market_conditions['volume_increasing']):
            adjustments.append({
                'action': 'extend_targets',
                'reason': 'strong_momentum',
                'adjustment': 'increase_targets_20pct'
            })
        
        return adjustments

La estrategia Low Float Runners requiere extrema disciplina en risk management debido a la naturaleza explosiva y volátil de estos stocks. La clave está en entries precisos, stops ajustados, y profit taking agresivo.