HEX
Server: Apache/2.4.18 (Ubuntu)
System: Linux ubuntu 7.0.5-x86_64-linode173 #1 SMP PREEMPT_DYNAMIC Fri May 8 10:12:05 EDT 2026 x86_64
User: root (0)
PHP: 7.2.28-1+ubuntu16.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: //proc/self/root/opt/trading-bot/allocator.py
import logging
from config import settings
from risk_engine import RiskEngine

logger = logging.getLogger(__name__)

class Allocator:
    def __init__(self, risk_engine: RiskEngine):
        self.risk_engine = risk_engine

    def calculate_target_positions(self, signals: list, current_positions: list, portfolio_state: dict) -> dict:
        targets = {}
        drawdown_pct = portfolio_state.get("drawdown_pct", 0.0)
        throttle = self.risk_engine.get_drawdown_throttle(drawdown_pct)
        total_max_alloc = settings.MAX_EXPOSURE_USD * throttle
        
        if not signals:
            return targets

        for signal in signals:
            action = signal.get("action", "NEUTRAL")
            leverage = signal.get("leverage", 1.0)
            edge = signal.get("edge_bps", 999.0)
            
            # Additional safety: Reduce allocation for lower-confidence signals
            conf_multiplier = 1.0
            if edge < (settings.FEE_BPS + settings.SLIPPAGE_BPS) * 3:
                conf_multiplier = 0.5 # Scale down if edge is thin
                
            alloc_per_signal = min(total_max_alloc / len(signals), settings.MAX_PER_ASSET_EXPOSURE_USD) * leverage * conf_multiplier

            if action == "LONG_BASIS":
                spot_symbol = signal.get("spot_symbol")
                perp_symbol = signal.get("perp_symbol")
                spot_price = signal.get("spot_price")
                perp_price = signal.get("perp_price")
                if spot_price and perp_price:
                    targets[spot_symbol] = alloc_per_signal / spot_price
                    targets[perp_symbol] = -alloc_per_signal / perp_price

            elif action == "LONG":
                symbol = signal.get("symbol") or signal.get("spot_symbol") or (f"{signal.get('asset')}/USDC" if signal.get('asset') else None)
                price = signal.get("price") or signal.get("spot_price")
                if symbol and price:
                    targets[symbol] = alloc_per_signal / price

            elif action == "SHORT":
                # For high yield, we use perps for shorting
                asset = signal.get("asset")
                symbol = f"{asset}/USDC:USDC" if asset else None
                price = signal.get("price") or signal.get("perp_price")
                if symbol and price:
                    targets[symbol] = -alloc_per_signal / price

            elif action == "CLOSE":
                # Only close for specific symbols provided in signal
                symbols_to_close = []
                if signal.get("symbol"): symbols_to_close.append(signal.get("symbol"))
                if signal.get("spot_symbol"): symbols_to_close.append(signal.get("spot_symbol"))
                if signal.get("perp_symbol"): symbols_to_close.append(signal.get("perp_symbol"))
                
                for s in symbols_to_close:
                    targets[s] = 0.0

        return targets

    def get_target_qty(self, signal: dict, current_equity: float, available_cash: float = None) -> float:
        """Calculates target quantity for a single signal based on current equity and available cash."""
        action = signal.get("action", "NEUTRAL")
        leverage = float(signal.get("leverage", 1.0))
        price = signal.get("price") or signal.get("spot_price") or signal.get("perp_price")
        
        if action == "CLOSE" or not price:
            return 0.0
        
        # Cap leverage at 2x to prevent over-allocation
        leverage = min(leverage, 2.0)
            
        # Target a specific $ amount per strategy, but cap by current equity
        target_usd = max(0.0, min(settings.MAX_PER_ASSET_EXPOSURE_USD, current_equity * leverage))
        
        # Confidence-weighted sizing (strategies can express conviction)
        confidence = signal.get("confidence", 1.0)
        target_usd *= max(0.1, min(1.0, confidence))
        
        # Cap by available cash (keep 5% reserve for fees/slippage)
        if available_cash is not None and available_cash > 0:
            cash_cap = available_cash * 0.95
            target_usd = min(target_usd, cash_cap)
        elif available_cash is not None:
            return 0.0  # No cash available
        
        qty = target_usd / float(price)
        return -qty if action == "SHORT" else qty