Riesgo Asimétrico

Apostar Más Cuando las Probabilidades Están a Tu Favor

El riesgo asimétrico es el secreto de los traders exitosos: arriesgar menos en setups mediocres y más en setups de alta probabilidad. No todos los trades son iguales, y tu position sizing debería reflejarlo.

El Concepto de Riesgo Asimétrico

¿Qué es?

# ❌ SIMÉTRICO: Mismo riesgo en todos los trades
def symmetric_risk_sizing(account_value):
    return account_value * 0.02  # Siempre 2%

# ✅ ASIMÉTRICO: Riesgo variable según calidad del setup
def asymmetric_risk_sizing(account_value, setup_quality):
    base_risk = 0.02
    
    if setup_quality == 'A+':
        return account_value * (base_risk * 2.0)    # 4% en mejores setups
    elif setup_quality == 'A':
        return account_value * (base_risk * 1.5)    # 3% en buenos setups
    elif setup_quality == 'B':
        return account_value * (base_risk * 1.0)    # 2% en setups normales
    elif setup_quality == 'C':
        return account_value * (base_risk * 0.5)    # 1% en setups débiles
    else:
        return 0  # No tradear setups malos

Por Qué Funciona

# Ejemplo: 100 trades con diferentes calidades
trade_scenarios = {
    'symmetric': {
        'risk_per_trade': 0.02,
        'a_plus_setups': {'count': 10, 'win_rate': 0.8, 'avg_rr': 3.0},
        'a_setups': {'count': 20, 'win_rate': 0.7, 'avg_rr': 2.5},
        'b_setups': {'count': 40, 'win_rate': 0.6, 'avg_rr': 2.0},
        'c_setups': {'count': 30, 'win_rate': 0.4, 'avg_rr': 1.5}
    },
    'asymmetric': {
        'a_plus_setups': {'count': 10, 'risk': 0.04, 'win_rate': 0.8, 'avg_rr': 3.0},
        'a_setups': {'count': 20, 'risk': 0.03, 'win_rate': 0.7, 'avg_rr': 2.5},
        'b_setups': {'count': 40, 'risk': 0.02, 'win_rate': 0.6, 'avg_rr': 2.0},
        'c_setups': {'count': 0, 'risk': 0.0, 'win_rate': 0.0, 'avg_rr': 0.0}  # Skip C setups
    }
}

def calculate_expected_return(scenario):
    """Calcular retorno esperado de cada escenario"""
    total_return = 0
    
    for setup_type, stats in scenario.items():
        if setup_type == 'symmetric':
            continue
            
        if 'risk' in stats:
            risk = stats['risk']
        else:
            risk = scenario.get('risk_per_trade', 0.02)
        
        # Expected return por trade de este tipo
        win_amount = risk * stats['avg_rr']
        loss_amount = risk
        expected_per_trade = (stats['win_rate'] * win_amount) - ((1 - stats['win_rate']) * loss_amount)
        
        total_return += expected_per_trade * stats['count']
    
    return total_return

symmetric_return = calculate_expected_return(trade_scenarios['symmetric'])
asymmetric_return = calculate_expected_return(trade_scenarios['asymmetric'])

print(f"Symmetric approach: {symmetric_return:.2%} expected return")
print(f"Asymmetric approach: {asymmetric_return:.2%} expected return")
print(f"Improvement: {(asymmetric_return - symmetric_return):.2%}")

Setup Quality Scoring

1. Multi-Factor Scoring System

class SetupQualityScorer:
    def __init__(self):
        self.scoring_factors = {
            'technical': {
                'vwap_reclaim': {'weight': 0.2, 'max_score': 10},
                'volume_spike': {'weight': 0.25, 'max_score': 10},
                'price_action': {'weight': 0.15, 'max_score': 10},
                'support_resistance': {'weight': 0.15, 'max_score': 10},
                'trend_alignment': {'weight': 0.25, 'max_score': 10}
            },
            'fundamental': {
                'catalyst_strength': {'weight': 0.4, 'max_score': 10},
                'float_quality': {'weight': 0.3, 'max_score': 10},
                'sector_momentum': {'weight': 0.3, 'max_score': 10}
            },
            'market_context': {
                'market_direction': {'weight': 0.5, 'max_score': 10},
                'volatility_regime': {'weight': 0.3, 'max_score': 10},
                'time_of_day': {'weight': 0.2, 'max_score': 10}
            }
        }
    
    def score_technical_factors(self, data):
        """Score factores técnicos"""
        scores = {}
        
        # VWAP Reclaim
        if 'vwap_reclaim' in data and data['vwap_reclaim']:
            if data['close'] > data['vwap'] * 1.02:  # 2% above VWAP
                scores['vwap_reclaim'] = 10
            elif data['close'] > data['vwap'] * 1.01:  # 1% above VWAP
                scores['vwap_reclaim'] = 7
            else:
                scores['vwap_reclaim'] = 5
        else:
            scores['vwap_reclaim'] = 0
        
        # Volume Spike
        rvol = data.get('rvol', 1)
        if rvol >= 5:
            scores['volume_spike'] = 10
        elif rvol >= 3:
            scores['volume_spike'] = 8
        elif rvol >= 2:
            scores['volume_spike'] = 6
        elif rvol >= 1.5:
            scores['volume_spike'] = 4
        else:
            scores['volume_spike'] = 0
        
        # Price Action
        gap_pct = abs(data.get('gap_pct', 0))
        if 10 <= gap_pct <= 25:  # Sweet spot for gaps
            scores['price_action'] = 10
        elif 5 <= gap_pct < 10:
            scores['price_action'] = 7
        elif gap_pct < 5:
            scores['price_action'] = 5
        else:  # >25% gap is risky
            scores['price_action'] = 3
        
        # Support/Resistance
        distance_from_key_level = data.get('distance_from_key_level', float('inf'))
        if distance_from_key_level < 0.005:  # Within 0.5% of key level
            scores['support_resistance'] = 10
        elif distance_from_key_level < 0.01:  # Within 1%
            scores['support_resistance'] = 7
        else:
            scores['support_resistance'] = 3
        
        # Trend Alignment
        if data.get('trend_alignment', 0) >= 3:  # Multiple timeframes aligned
            scores['trend_alignment'] = 10
        elif data.get('trend_alignment', 0) >= 2:
            scores['trend_alignment'] = 7
        else:
            scores['trend_alignment'] = 3
        
        return scores
    
    def score_fundamental_factors(self, data):
        """Score factores fundamentales"""
        scores = {}
        
        # Catalyst Strength
        catalyst_type = data.get('catalyst_type', 'none')
        if catalyst_type in ['earnings_beat', 'fda_approval', 'major_contract']:
            scores['catalyst_strength'] = 10
        elif catalyst_type in ['analyst_upgrade', 'insider_buying']:
            scores['catalyst_strength'] = 7
        elif catalyst_type in ['technical_breakout']:
            scores['catalyst_strength'] = 5
        else:
            scores['catalyst_strength'] = 0
        
        # Float Quality
        float_shares = data.get('float_shares', float('inf'))
        if float_shares < 5_000_000:
            scores['float_quality'] = 10
        elif float_shares < 15_000_000:
            scores['float_quality'] = 8
        elif float_shares < 30_000_000:
            scores['float_quality'] = 6
        else:
            scores['float_quality'] = 3
        
        # Sector Momentum
        sector_performance = data.get('sector_performance_5d', 0)
        if sector_performance > 0.05:  # Sector up 5%+ in 5 days
            scores['sector_momentum'] = 10
        elif sector_performance > 0.02:
            scores['sector_momentum'] = 7
        elif sector_performance > -0.02:
            scores['sector_momentum'] = 5
        else:
            scores['sector_momentum'] = 2
        
        return scores
    
    def score_market_context(self, data):
        """Score contexto de mercado"""
        scores = {}
        
        # Market Direction
        spy_trend = data.get('spy_trend', 'neutral')
        if spy_trend == 'strong_uptrend':
            scores['market_direction'] = 10
        elif spy_trend == 'uptrend':
            scores['market_direction'] = 8
        elif spy_trend == 'neutral':
            scores['market_direction'] = 5
        elif spy_trend == 'downtrend':
            scores['market_direction'] = 3
        else:  # strong_downtrend
            scores['market_direction'] = 0
        
        # Volatility Regime
        vix_level = data.get('vix', 20)
        if 12 <= vix_level <= 20:  # Sweet spot
            scores['volatility_regime'] = 10
        elif 20 < vix_level <= 25:
            scores['volatility_regime'] = 7
        elif vix_level < 12:  # Too complacent
            scores['volatility_regime'] = 5
        else:  # VIX > 25
            scores['volatility_regime'] = 2
        
        # Time of Day
        current_time = pd.Timestamp.now().time()
        if pd.Timestamp('09:30').time() <= current_time <= pd.Timestamp('11:00').time():
            scores['time_of_day'] = 10  # Best time for momentum
        elif pd.Timestamp('14:00').time() <= current_time <= pd.Timestamp('15:30').time():
            scores['time_of_day'] = 8   # Good afternoon volume
        else:
            scores['time_of_day'] = 5
        
        return scores
    
    def calculate_overall_score(self, data):
        """Calcular score total del setup"""
        technical_scores = self.score_technical_factors(data)
        fundamental_scores = self.score_fundamental_factors(data)
        market_scores = self.score_market_context(data)
        
        # Calculate weighted scores
        technical_weighted = sum(
            score * self.scoring_factors['technical'][factor]['weight'] 
            for factor, score in technical_scores.items()
        )
        
        fundamental_weighted = sum(
            score * self.scoring_factors['fundamental'][factor]['weight'] 
            for factor, score in fundamental_scores.items()
        )
        
        market_weighted = sum(
            score * self.scoring_factors['market_context'][factor]['weight'] 
            for factor, score in market_scores.items()
        )
        
        # Combine scores (equal weight for now)
        total_score = (technical_weighted + fundamental_weighted + market_weighted) / 3
        
        # Convert to letter grade
        if total_score >= 8.5:
            grade = 'A+'
        elif total_score >= 7.5:
            grade = 'A'
        elif total_score >= 6.5:
            grade = 'B+'
        elif total_score >= 5.5:
            grade = 'B'
        elif total_score >= 4.5:
            grade = 'C+'
        elif total_score >= 3.5:
            grade = 'C'
        else:
            grade = 'D'
        
        return {
            'total_score': total_score,
            'grade': grade,
            'technical_score': technical_weighted,
            'fundamental_score': fundamental_weighted,
            'market_score': market_weighted,
            'detailed_scores': {
                'technical': technical_scores,
                'fundamental': fundamental_scores,
                'market': market_scores
            }
        }

2. Historical Performance by Setup Quality

class SetupPerformanceTracker:
    def __init__(self):
        self.trade_history = []
        
    def record_trade(self, setup_data, trade_result):
        """Registrar trade con setup quality y resultado"""
        scorer = SetupQualityScorer()
        quality_score = scorer.calculate_overall_score(setup_data)
        
        trade_record = {
            'timestamp': pd.Timestamp.now(),
            'ticker': setup_data['ticker'],
            'setup_grade': quality_score['grade'],
            'setup_score': quality_score['total_score'],
            'entry_price': trade_result['entry_price'],
            'exit_price': trade_result['exit_price'],
            'pnl': trade_result['pnl'],
            'pnl_pct': trade_result['pnl_pct'],
            'hold_time': trade_result['hold_time'],
            'was_winner': trade_result['pnl'] > 0,
            'setup_details': quality_score['detailed_scores']
        }
        
        self.trade_history.append(trade_record)
    
    def analyze_performance_by_grade(self):
        """Analizar performance por grado de setup"""
        if not self.trade_history:
            return {}
        
        df = pd.DataFrame(self.trade_history)
        performance_by_grade = {}
        
        for grade in df['setup_grade'].unique():
            grade_trades = df[df['setup_grade'] == grade]
            
            performance_by_grade[grade] = {
                'total_trades': len(grade_trades),
                'win_rate': grade_trades['was_winner'].mean(),
                'avg_pnl_pct': grade_trades['pnl_pct'].mean(),
                'avg_winner_pct': grade_trades[grade_trades['was_winner']]['pnl_pct'].mean() if grade_trades['was_winner'].any() else 0,
                'avg_loser_pct': grade_trades[~grade_trades['was_winner']]['pnl_pct'].mean() if (~grade_trades['was_winner']).any() else 0,
                'profit_factor': abs(grade_trades[grade_trades['was_winner']]['pnl'].sum() / 
                                   grade_trades[~grade_trades['was_winner']]['pnl'].sum()) if (~grade_trades['was_winner']).any() else float('inf'),
                'avg_hold_time': grade_trades['hold_time'].mean(),
                'expectancy': (
                    grade_trades['win_rate'].iloc[0] * grade_trades[grade_trades['was_winner']]['pnl_pct'].mean() +
                    (1 - grade_trades['win_rate'].iloc[0]) * grade_trades[~grade_trades['was_winner']]['pnl_pct'].mean()
                ) if len(grade_trades) > 0 else 0
            }
        
        return performance_by_grade
    
    def get_optimal_risk_allocation(self):
        """Calcular allocation óptimo basado en historical performance"""
        performance = self.analyze_performance_by_grade()
        
        # Calculate Kelly-inspired allocation
        allocations = {}
        
        for grade, stats in performance.items():
            if stats['total_trades'] < 5:  # Not enough data
                allocations[grade] = 0.01  # Minimal allocation
                continue
            
            win_rate = stats['win_rate']
            avg_win = stats['avg_winner_pct'] / 100 if stats['avg_winner_pct'] else 0
            avg_loss = abs(stats['avg_loser_pct'] / 100) if stats['avg_loser_pct'] else 0.02
            
            if avg_loss == 0:
                avg_loss = 0.02  # Default
            
            # Modified Kelly
            kelly_fraction = (win_rate * avg_win - (1 - win_rate) * avg_loss) / avg_win if avg_win > 0 else 0
            
            # Conservative fraction of Kelly
            conservative_allocation = max(0.005, min(0.05, kelly_fraction * 0.25))
            allocations[grade] = conservative_allocation
        
        return allocations

Dynamic Risk Allocation

1. Confidence-Based Sizing

class ConfidenceBasedSizer:
    def __init__(self, base_risk=0.02):
        self.base_risk = base_risk
        self.performance_tracker = SetupPerformanceTracker()
        
    def calculate_confidence_multiplier(self, setup_data):
        """Calcular multiplicador de confianza"""
        scorer = SetupQualityScorer()
        quality_score = scorer.calculate_overall_score(setup_data)
        
        # Base multiplier from score
        score_multiplier = quality_score['total_score'] / 5.0  # Normalize to 2.0 max
        
        # Historical performance adjustment
        performance = self.performance_tracker.analyze_performance_by_grade()
        grade = quality_score['grade']
        
        if grade in performance and performance[grade]['total_trades'] >= 10:
            # Use historical expectancy
            expectancy = performance[grade]['expectancy']
            performance_multiplier = max(0.25, min(2.0, 1 + expectancy * 5))
        else:
            performance_multiplier = 1.0
        
        # Market regime adjustment
        market_multiplier = self.get_market_regime_multiplier(setup_data)
        
        # Combined confidence
        final_multiplier = score_multiplier * performance_multiplier * market_multiplier
        
        # Cap between 0.25x and 3.0x
        return max(0.25, min(3.0, final_multiplier))
    
    def get_market_regime_multiplier(self, setup_data):
        """Adjust for market regime"""
        vix = setup_data.get('vix', 20)
        spy_trend = setup_data.get('spy_trend', 'neutral')
        
        # Base market multiplier
        if spy_trend == 'strong_uptrend':
            market_mult = 1.2
        elif spy_trend == 'uptrend':
            market_mult = 1.1
        elif spy_trend == 'neutral':
            market_mult = 1.0
        elif spy_trend == 'downtrend':
            market_mult = 0.8
        else:  # strong_downtrend
            market_mult = 0.5
        
        # VIX adjustment
        if vix > 30:  # High fear
            market_mult *= 0.7
        elif vix > 25:
            market_mult *= 0.85
        elif vix < 12:  # Complacency
            market_mult *= 0.9
        
        return market_mult
    
    def calculate_position_size(self, account_value, setup_data, entry_price, stop_price):
        """Calculate final position size"""
        confidence_multiplier = self.calculate_confidence_multiplier(setup_data)
        adjusted_risk = self.base_risk * confidence_multiplier
        
        # Standard position sizing
        risk_per_share = abs(entry_price - stop_price)
        risk_amount = account_value * adjusted_risk
        shares = int(risk_amount / risk_per_share) if risk_per_share > 0 else 0
        
        # Position limits
        max_position_value = account_value * 0.25  # 25% max position
        max_shares = int(max_position_value / entry_price)
        
        final_shares = min(shares, max_shares)
        
        return {
            'shares': final_shares,
            'risk_amount': final_shares * risk_per_share,
            'risk_pct': (final_shares * risk_per_share) / account_value,
            'confidence_multiplier': confidence_multiplier,
            'base_risk': self.base_risk,
            'adjusted_risk': adjusted_risk
        }

2. Streak-Based Adjustments

class StreakAdjustedRisk:
    def __init__(self, base_risk=0.02):
        self.base_risk = base_risk
        self.recent_trades = []
        self.max_lookback = 20
        
    def add_trade_result(self, was_winner, pnl_pct):
        """Add trade result"""
        self.recent_trades.append({
            'timestamp': pd.Timestamp.now(),
            'was_winner': was_winner,
            'pnl_pct': pnl_pct
        })
        
        # Keep only recent trades
        if len(self.recent_trades) > self.max_lookback:
            self.recent_trades = self.recent_trades[-self.max_lookback:]
    
    def calculate_streak_multiplier(self):
        """Calculate risk multiplier based on recent performance"""
        if len(self.recent_trades) < 5:
            return 1.0
        
        recent_10 = self.recent_trades[-10:] if len(self.recent_trades) >= 10 else self.recent_trades
        
        # Calculate recent win rate
        recent_win_rate = sum(1 for trade in recent_10 if trade['was_winner']) / len(recent_10)
        
        # Calculate recent avg return
        recent_avg_return = sum(trade['pnl_pct'] for trade in recent_10) / len(recent_10)
        
        # Current streak
        current_streak = 0
        streak_type = None
        
        for trade in reversed(self.recent_trades):
            if streak_type is None:
                streak_type = 'win' if trade['was_winner'] else 'loss'
                current_streak = 1
            elif (streak_type == 'win' and trade['was_winner']) or (streak_type == 'loss' and not trade['was_winner']):
                current_streak += 1
            else:
                break
        
        # Adjustment logic
        multiplier = 1.0
        
        # Winning streak: gradually increase confidence
        if streak_type == 'win' and current_streak >= 3:
            multiplier = min(1.5, 1.0 + (current_streak - 2) * 0.1)
        
        # Losing streak: reduce risk
        elif streak_type == 'loss' and current_streak >= 2:
            multiplier = max(0.25, 1.0 - (current_streak - 1) * 0.15)
        
        # Poor recent performance: reduce risk
        if recent_win_rate < 0.3:  # <30% win rate
            multiplier *= 0.5
        elif recent_avg_return < -0.02:  # Losing money on average
            multiplier *= 0.75
        
        # Great recent performance: slight increase
        elif recent_win_rate > 0.7 and recent_avg_return > 0.03:
            multiplier *= 1.25
        
        return max(0.25, min(2.0, multiplier))
    
    def get_adjusted_risk(self):
        """Get current risk adjustment"""
        streak_multiplier = self.calculate_streak_multiplier()
        return self.base_risk * streak_multiplier

Implementation Framework

1. Integrated Asymmetric Risk Manager

class AsymmetricRiskManager:
    def __init__(self, account_value, base_risk=0.02):
        self.account_value = account_value
        self.base_risk = base_risk
        
        # Components
        self.scorer = SetupQualityScorer()
        self.performance_tracker = SetupPerformanceTracker()
        self.confidence_sizer = ConfidenceBasedSizer(base_risk)
        self.streak_adjuster = StreakAdjustedRisk(base_risk)
        
    def calculate_optimal_position_size(self, setup_data, entry_price, stop_price):
        """Master function para calcular position size óptimo"""
        
        # 1. Score the setup
        quality_assessment = self.scorer.calculate_overall_score(setup_data)
        
        # 2. Confidence-based sizing
        confidence_sizing = self.confidence_sizer.calculate_position_size(
            self.account_value, setup_data, entry_price, stop_price
        )
        
        # 3. Streak adjustment
        streak_multiplier = self.streak_adjuster.calculate_streak_multiplier()
        
        # 4. Combine adjustments
        final_shares = int(confidence_sizing['shares'] * streak_multiplier)
        
        # 5. Final validation
        final_risk_amount = final_shares * abs(entry_price - stop_price)
        final_risk_pct = final_risk_amount / self.account_value
        
        # Safety caps
        max_risk_pct = 0.06  # Never risk more than 6%
        if final_risk_pct > max_risk_pct:
            final_shares = int((self.account_value * max_risk_pct) / abs(entry_price - stop_price))
            final_risk_amount = final_shares * abs(entry_price - stop_price)
            final_risk_pct = final_risk_amount / self.account_value
        
        return {
            'final_shares': final_shares,
            'final_risk_amount': final_risk_amount,
            'final_risk_pct': final_risk_pct,
            'setup_quality': quality_assessment,
            'confidence_multiplier': confidence_sizing['confidence_multiplier'],
            'streak_multiplier': streak_multiplier,
            'base_risk': self.base_risk,
            'decision_breakdown': {
                'setup_grade': quality_assessment['grade'],
                'setup_score': quality_assessment['total_score'],
                'base_shares': confidence_sizing['shares'],
                'streak_adjusted_shares': final_shares
            }
        }
    
    def should_take_trade(self, setup_data, min_grade='C'):
        """¿Debería tomar este trade?"""
        quality_assessment = self.scorer.calculate_overall_score(setup_data)
        grade = quality_assessment['grade']
        
        # Grade hierarchy
        grade_values = {'A+': 10, 'A': 9, 'B+': 8, 'B': 7, 'C+': 6, 'C': 5, 'D': 4}
        min_value = grade_values.get(min_grade, 5)
        current_value = grade_values.get(grade, 0)
        
        should_trade = current_value >= min_value
        
        return {
            'should_trade': should_trade,
            'grade': grade,
            'score': quality_assessment['total_score'],
            'reason': f"Grade {grade} {'meets' if should_trade else 'below'} minimum {min_grade}"
        }

2. Monitoring and Reporting

def generate_asymmetric_risk_report(risk_manager):
    """Generate comprehensive risk report"""
    
    # Historical performance by grade
    performance = risk_manager.performance_tracker.analyze_performance_by_grade()
    
    # Current allocations
    optimal_allocations = risk_manager.performance_tracker.get_optimal_risk_allocation()
    
    # Recent streak performance
    streak_multiplier = risk_manager.streak_adjuster.calculate_streak_multiplier()
    
    report = f"""
📊 **Asymmetric Risk Management Report**
{'='*50}

🎯 **Setup Performance by Grade**
"""
    
    for grade in ['A+', 'A', 'B+', 'B', 'C+', 'C']:
        if grade in performance:
            stats = performance[grade]
            report += f"""
{grade} Grade Setups:
  • Total Trades: {stats['total_trades']}
  • Win Rate: {stats['win_rate']:.1%}
  • Avg Return: {stats['avg_pnl_pct']:.2%}
  • Profit Factor: {stats['profit_factor']:.2f}
  • Expectancy: {stats['expectancy']:.2%}
"""
    
    report += f"""

💰 **Current Risk Allocations**
"""
    for grade, allocation in optimal_allocations.items():
        report += f"  • {grade} Grade: {allocation:.1%} risk per trade\n"
    
    report += f"""

📈 **Current Adjustments**
  • Streak Multiplier: {streak_multiplier:.2f}x
  • Base Risk: {risk_manager.base_risk:.1%}
  • Effective Risk Range: {risk_manager.base_risk * streak_multiplier * 0.25:.1%} - {risk_manager.base_risk * streak_multiplier * 3.0:.1%}
"""
    
    return report

Mi Setup Personal

# asymmetric_config.py
ASYMMETRIC_CONFIG = {
    'base_risk': 0.015,  # 1.5% base risk
    'grade_multipliers': {
        'A+': 2.5,  # 3.75% risk for A+ setups
        'A': 2.0,   # 3.0% risk for A setups
        'B+': 1.5,  # 2.25% risk for B+ setups
        'B': 1.0,   # 1.5% risk for B setups
        'C+': 0.5,  # 0.75% risk for C+ setups
        'C': 0.25,  # 0.375% risk for C setups
        'D': 0.0    # No trading D setups
    },
    'min_tradeable_grade': 'B',  # Don't trade below B grade
    'max_single_risk': 0.05,     # Never risk more than 5%
    'streak_adjustment': True,    # Enable streak-based adjustments
    'market_regime_adjustment': True  # Enable market-based adjustments
}

# Usage
def my_position_sizing_workflow(ticker, setup_data, entry_price, stop_price):
    """Mi workflow completo de position sizing"""
    
    # Initialize manager
    risk_manager = AsymmetricRiskManager(
        account_value=50000, 
        base_risk=ASYMMETRIC_CONFIG['base_risk']
    )
    
    # Should I trade this?
    trade_decision = risk_manager.should_take_trade(
        setup_data, 
        min_grade=ASYMMETRIC_CONFIG['min_tradeable_grade']
    )
    
    if not trade_decision['should_trade']:
        return {
            'approved': False,
            'reason': trade_decision['reason'],
            'grade': trade_decision['grade']
        }
    
    # Calculate position size
    position_info = risk_manager.calculate_optimal_position_size(
        setup_data, entry_price, stop_price
    )
    
    # Final approval
    if position_info['final_risk_pct'] > ASYMMETRIC_CONFIG['max_single_risk']:
        return {
            'approved': False,
            'reason': f"Risk too high: {position_info['final_risk_pct']:.2%}",
            'max_allowed': ASYMMETRIC_CONFIG['max_single_risk']
        }
    
    return {
        'approved': True,
        'shares': position_info['final_shares'],
        'risk_amount': position_info['final_risk_amount'],
        'risk_pct': position_info['final_risk_pct'],
        'setup_grade': position_info['setup_quality']['grade'],
        'confidence_multiplier': position_info['confidence_multiplier'],
        'decision_detail': position_info['decision_breakdown']
    }

Completada la sección de gestión de riesgo. Continuemos con las estrategias aplicadas a small caps.