
    ;jF                       d Z ddlmZ ddlmZmZ ddlmZ ddlm	Z
 ddlmZ  ed       G d	 d
             Z ed       G d d             Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZh dZdd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZd dZ ed       G d d             Z ed       G d d             Ze
j.                  d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d!dZy)"u  
APEX V16 — Position sizing and budget pre-check.

Three layers, low to high:

1. PRIMITIVES (internal — `budget_pre_check`, `compute_contracts`):
   pure ticks-based math, ported from V15. Kept intact since Day 1.

2. CONVERSION HELPERS (public — `points_to_ticks`, `points_to_dollars`):
   wrap ASSETS_MAP lookups so the rest of the codebase never deals
   with `tick_size`/`tick_value` directly. Fail-loud on unknown
   symbols (no silent default-tick-size bugs).

3. PUBLIC ENTRY POINT (`size_for_entry`):
   the orchestrator-facing API. Takes a brain-produced EntryDecision +
   account_balance + risk config, returns a SizingDecision (act-on-it
   payload) with a nested SizingAudit (look-at-it diagnostics).

V15 sizing formula (verified at APEX_PREDATOR_V15.py:775-826):
    risk_usd          = balance * risk_per_trade * risk_multiplier
    sl_usd_per_contract = sl_ticks * tick_value
    target_contracts  = risk_usd / sl_usd_per_contract
    contracts         = clamp(round(target), 1, MAX_CONTRACTS_PER_TRADE[sym])
    real_risk         = contracts * sl_usd_per_contract

`inverted_quote` (6J/6C) is intentionally NOT read by sizing math: V15
uses it only as an info log post-sizing (APEX_PREDATOR_V15.py:1665) for
direction-flip awareness. The dollar formula is invariant. V16 propagates
the flag in SizingAudit for audit, never reads it in math.

Pure functions — no broker, no AI, no I/O.
    )annotations)asdict	dataclass)Optional)config_futures)EntryDecisionT)frozenc                  D    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)BudgetCheckResultz%Result of the pre-Brain budget check.boolskipstrreasonfloatmin_risk_usdtypical_risk_usdmax_allowed_usdN)__name__
__module____qualname____doc____annotations__     %/home/work/apex_v16/trading/sizing.pyr   r   /   s!    /
JKr   r   c                  `    e Zd ZU dZded<   ded<   ded<   ded<   d	ed
<   dZded<   dZded<   y)SizingResultzResult of compute_contracts.int	contractsr   real_risk_usdtarget_floatr   r   r   r   r   sl_ticks_originalFsl_ticks_flooredN)r   r   r   r   r   r"   r#   r   r   r   r   r   9   s6    &N
JKs"d"r   r   c           
     0   |dk  rt        |  d|       |dk  rt        |  d|       ||k  rt        |  d| d| d      ||z   dz  }d|z  |z  }d|z  |z  }	t        ||z
        }
|
|z  }||kD  r7t        d	d
|dd|ddt        |d      t        |	d      t        |d            S |	|dz  kD  r7t        d	d|	dd|ddt        |d      t        |	d      t        |d            S t        ddt        |d      t        |	d      t        |d            S )u	  
    Skip an asset BEFORE calling Brain if the daily budget can't
    accommodate even the smallest plausible trade.

    Logic:
      daily_remaining = abs(daily_loss_hard_stop - daily_pnl)
      max_allowed     = daily_remaining * max_risk_vs_daily_budget
      min_risk        = 1ct × min_sl_ticks × tick_value
      typical_risk    = 1ct × ((min+max)/2) × tick_value

      - If min_risk > max_allowed         -> skip HARD (impossible)
      - If typical_risk > max_allowed*1.2 -> skip SOFT (probable waste)

    Args:
        symbol: just for logging in reason string
        tick_value: $ per tick per contract (from config_futures)
        min_sl_ticks: smallest SL we'd ever accept for this asset
        max_sl_ticks: largest SL we'd ever accept for this asset
        daily_pnl: current daily P&L (negative for losses)
        daily_loss_hard_stop: e.g. -1500.0 (negative)
        max_risk_vs_daily_budget: fraction (0.33 = 33%)

    Raises:
        ValueError if tick_value <= 0 or min_sl_ticks <= 0 or max < min.
    r   : tick_value must be > 0, got z : min_sl_ticks must be > 0, got z: max_sl_ticks (z) < min_sl_ticks ()      Tzbudget_hard (min $.0fz > allowed $)r   r   r   r   r   g333333?zbudget_soft (typical $z x 1.2)F	budget_ok)
ValueErrorabsr   round)symbol
tick_valuemin_sl_ticksmax_sl_ticks	daily_pnldaily_loss_hard_stopmax_risk_vs_daily_budgettypical_sl_ticksr   r   daily_remainingr   s               r   budget_pre_checkr7   I   s   H QF8#A*NOOqF8#CL>RSSl"h&|n4F|nTUV
 	
 %|39|#j0L++j8.:;O%(@@O o% 'S'9oVYEZZ[\|Q/"#3Q7!/15
 	
 /C// +,<S+Ao^aMbbij|Q/"#3Q7!/15
 	
 <+/3oq1 r   >   MESMGCMNQMYM      ?)risk_multiplierc                   |dk  rt        |  d|       |dk  rt        |  d|       |dk  rt        |  d|       |dk  rt        |  d|       |
dk  rt        |  d|
       |}| t        v r!t        j                  j	                  | |      }n*t        |t        j                  j	                  | |            }||k7  }||z  }||z  |
z  }||z  }||k  r$t        ddt        |d	      d
d|dd| d||      S t        dt        |            }t        ||      }t        t        j                  j	                  | d            }|dkD  rt        |t        ||            }||z  }t        ||z
        }||	z  }||kD  r/t        dt        |d	      t        |d	      d
d|dd|dd||      S t        |t        |d	      t        |d	      dd||      S )a  
    Compute integer contracts to trade based on risk targeting.

    Algorithm:
      sl_usd_per_contract = sl_ticks * tick_value
      risk_usd            = account_balance * risk_per_trade * risk_multiplier
      target_float        = risk_usd / sl_usd_per_contract

      if target_float < min_contract_fraction -> skip (size too small)
      else contracts = clamp(round(target_float), 1, max_contracts)
           real_risk = contracts * sl_usd_per_contract
           if real_risk > daily_budget_cap -> skip (exceeds budget)

    Args:
        symbol: for logging
        sl_ticks: SL distance in ticks (Brain-proposed, must be > 0)
        account_balance: account equity in USD
        tick_value: $ per tick per contract
        risk_per_trade: e.g. 0.003 (0.30%)
        max_contracts: per-asset cap (from MAX_CONTRACTS_PER_TRADE)
        min_contract_fraction: e.g. 0.70 (skip if target_float < this)
        daily_pnl: current daily P&L
        daily_loss_hard_stop: e.g. -1500.0
        max_risk_vs_daily_budget: e.g. 0.33
        risk_multiplier: confidence-based scaling (1.0 default; can be
                         e.g. 0.5 for low confidence, 1.5 for high)
    r   r%   z: sl_ticks must be > 0, got z#: account_balance must be > 0, got r(   z": max_contracts must be >= 1, got z#: risk_multiplier must be > 0, got g        r'   Tzsize_too_small (z.2fz < r&   )r   r    r!   r   r   r"   r#   zrisk_exceeds_daily_budget ($r)   z > $FOK)r+   WORST_CASE_SIZING_ASSETScfg_futMAX_SL_TICKSgetmaxMIN_SL_TICKSr   r-   minr   MIN_CONTRACTS_PER_TRADEr,   )r.   sl_ticksaccount_balancer/   risk_per_trademax_contractsmin_contract_fractionr2   r3   r4   r=   r"   r#   sl_usd_per_contractrisk_usdr!   r   min_contractsr    r6   r   s                        r   compute_contractsrP      sb   T QF8#A*NOO1}F8#?zJKK!F8#FFWXYYqF8#Em_UVV!F8#FFWXYY ))''++FH=x!5!5!9!9&(!KL#44"Z///AH11L ++|Q/%l3%7s;P:QQRS/-
 	
 Au\*+II}-I 77;;FAFGMq	3}m#DE	 33M .:;O%(@@O&q1|Q/1-1DDY\H]]^_/-
 	
 M1-<++) r   c                f    t         j                  j                  |       }|t        d| d      |S )zELookup ASSETS_MAP[symbol] or fail loud (no silent default tick math).zunknown symbol z": not in config_futures.ASSETS_MAP)rA   
ASSETS_MAPrC   r+   )r.   specs     r   _specrT   (  s>    !!&)D|fZ'IJ
 	
 Kr   c                v    |dk  rt        |  d|       t        |       }t        t        ||d   z              S )z
    Convert a price-points distance into ticks for `symbol`.

    Raises:
        ValueError on unknown symbol or non-positive distance.
    r   z#: distance_points must be > 0, got 	tick_size)r+   rT   r   r-   )r.   distance_pointsrS   s      r   points_to_ticksrX   2  sN     !h9/9JK
 	
 =Du_tK'889::r   c                R    t        |       }t        | |      }t        ||d   z        S )z
    Convert a price-points distance into USD risk per contract.

    Equivalent to: ticks(symbol, distance) * tick_value(symbol).
    r/   )rT   rX   r   )r.   rW   rS   rH   s       r   points_to_dollarsrZ   A  s.     =Dv7HD..//r   c                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   dZded<   dZded<   y)SizingAuditu   
    Diagnostics for sizing_log / brain_log JSONL streams.

    Never read by orchestrator decision logic — only logged. Lets the
    calibration round (5-9 mag) histogram clamp_active rate, real vs
    target risk, etc. across days.
    r   r.   r   r=   r!   sl_distance_pointsrV   r/   rM   risk_usd_targetdaily_budget_cap_usdr   max_contracts_usedr   clamp_activeinverted_quoteFr#   r   r"   N)r   r   r   r   r   r#   r"   r   r   r   r\   r\   P  sf     K"d"sr   r\   c                  V    e Zd ZU dZded<   ded<   ded<   ded<   d	ed
<   ded<   ddZy)SizingDecisionz
    Core sizing output the orchestrator acts on. Audit nested for logging.

    Orchestrator reads contracts/skip/reason/sl_ticks/real_risk_usd to
    place the order. `audit` is forwarded to the JSONL logger.
    r   r   r   r   r   r   rH   r   r    r\   auditc                ,    t        | j                        S )z:Convenience for JSONL emission: flat dict of audit fields.)r   re   )selfs    r   
audit_dictzSizingDecision.audit_dictx  s    djj!!r   N)returndict)r   r   r   r   r   rh   r   r   r   rd   rd   i  s.     N
JKM"r   rd   )rL   c                   t        |      }t        |d         }	t        |d         }
t        |j                  dd            }t	        | j
                  | j                  z
        }|dk  r(t        | d| j
                   d| j                   d      t        ||      }t        | j                  j                  d	d
            }||z  |z  }t        t        j                  j                  |t        j                  j                  dd                  }t	        ||z
        }||z  }t        ||||
|||||||      }t        dt        |j                               }||kD  xr |j"                  |k(  }|t$        v r!t        j&                  j                  ||      }n*t        |t        j(                  j                  ||            }||
z  }t+        |||j                   t        |d      |	|
t        |d      t        |d      t        |d      ||||j,                  |j.                        }t1        |j"                  |j2                  |j4                  ||j6                  |      S )a  
    Convert a brain-produced EntryDecision into an executable contract count.

    Flow:
      1. sl_distance_points = abs(entry.entry_price - entry.sl_price).
      2. Lookup ASSETS_MAP[symbol] (raise on missing).
      3. Convert points -> ticks -> $/contract via the helpers.
      4. Read risk_multiplier from entry.metadata (Brain may set 0.5..1.2).
      5. Read MAX_CONTRACTS_PER_TRADE[symbol] (default 1).
      6. Delegate the integer-clamp math to compute_contracts (existing primitive).
      7. Wrap result + diagnostics in SizingDecision/SizingAudit.

    Raises:
        ValueError on unknown symbol, sl_distance == 0, or invalid inputs
        bubbled from compute_contracts.
    rV   r/   rb   Fr   z(: sl_distance_points must be > 0 (entry=z, sl=r&   r=   r<   defaultr(   )r.   rH   rI   r/   rJ   rK   rL   r2   r3   r4   r=      r'   )r.   r=   r!   r]   rV   r/   rM   r^   r_   r`   ra   rb   r#   r"   )r   r   r   rH   r    re   )rT   r   r   rC   r,   entry_pricesl_pricer+   rX   metadatar   rA   MAX_CONTRACTS_PER_TRADErP   rD   r-   r!   r   r@   rB   rE   r\   r#   r"   rd   r   r   r    )entryr.   rI   rJ   r2   r3   r4   rL   rS   rV   r/   rb   r]   rH   r=   r^   rK   r6   r_   	primitiverounded_targetra   sl_ticks_for_sizingrM   re   s                            r   size_for_entryrv     sv   6 =Dd;'(ItL)*J$((#3U;<NU..?@Qh ''(enn-=Q@
 	

 v'9:HENN../@#FGO%6HO''++G3377	1E	
M .:;O*-EE!'%#31!9'I E)"8"89:N"]2 },  ))%2266vxH!(G,@,@,D,DVX,VW-
:'++ !3Q7!"5q9oq1"#7;(!%"33#55E" %%^^-- r   N)r.   r   r/   r   r0   r   r1   r   r2   r   r3   r   r4   r   ri   r   )r.   r   rH   r   rI   r   r/   r   rJ   r   rK   r   rL   r   r2   r   r3   r   r4   r   r=   r   ri   r   )r.   r   ri   rj   )r.   r   rW   r   ri   r   )r.   r   rW   r   ri   r   )rr   r   r.   r   rI   r   rJ   r   r2   r   r3   r   r4   r   rL   r   ri   rd   )r   
__future__r   dataclassesr   r   typingr   corer   rA   core.contractsr   r   r   r7   r@   rP   rT   rX   rZ   r\   rd   MIN_CONTRACT_FRACTIONrv   r   r   r   <module>r}      s	  B # )  * ( $   $# # #PP P 	P
 P P  P $P P~ 8  !qq q 	q
 q q q !q q  q $q q qz;0 $  0 $" " "@ $+#@#@kk k 	k
 k k  k $k !k kr   