
    إj$5                    &   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 G d d             Ze G d	 d
             Ze G d d             Z ed       G d d             Z ed       G d d             Z G d de      Z G d de      Zy)u  
APEX V16 — Broker abstract interface.

Defines the contract that any broker adapter must implement.
TopstepX adapter (existing, reused from V15) will be wrapped/extended
to implement this interface in a follow-up step.

Why an interface here even though we only have one broker today:
  1. Enables MockBroker for unit/integration tests without API keys
  2. Future-proofs if we add a second broker
  3. Forces explicit thinking about what the orchestrator actually
     needs from "the broker" (read-only ops vs order ops)
    )annotations)ABCabstractmethod)	dataclass)datetime)Optionalc                      e Zd ZU dZded<   dZded<   dZded<   dZded<   dZd	ed
<   dZ	d	ed<   dZ
d	ed<   dZd	ed<   dZded<   y)OrderResultz6Result of placing a market order with bracket (SL+TP).boolsuccessNOptional[float]entry_pricesl_pricetp_priceOptional[str]entry_idstop_id	target_iderrorOptional[dict]raw)__name__
__module____qualname____doc____annotations__r   r   r   r   r   r   r   r        )/home/work/apex_v16/broker/broker_base.pyr
   r
      sZ    @M#'K' $Ho$ $Ho$"Hm"!G]!#I}#E=Cr   r
   c                  H    e Zd ZU dZded<   ded<   ded<   ded<   d	Zd
ed<   y	)Positionz%Broker-side position (live snapshot).strsymbol	directionint	contractsfloat	avg_priceNr   r   )r   r   r   r   r   r   r   r   r   r!   r!   )   s$    /KNNCr   r!   c                  8    e Zd ZU dZded<   dZded<   dZded<   y)CancelResultzResult of canceling an order.r   r   Nr   order_idr   )r   r   r   r   r   r+   r   r   r   r   r*   r*   3   s    'M"Hm"E=r   r*   T)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
)Ordera  
    Pending order on the broker side (read-only snapshot).

    Used by Reconciler to detect naked SL/TP orphans (V15-BUG-9).
    `kind` is normalized across brokers: TopstepX type=1 -> "LIMIT",
    type=4 -> "STOP", type=2 -> "MARKET", anything else -> "OTHER".
    r"   r+   r#   kindr'   pricer%   r&   Nr   r   r   r   r   r   r   r   r.   r.   ;   s"     MK
ILNr   r.   c                  X    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<   y)ClosedTradeuv  
    Trade that closed broker-side (read-only snapshot).

    Used by Reconciler case (ii) — when state thinks a trade is open
    but broker has no position, fetch recent_trades and recover P&L
    so RiskManager counters (daily_pnl, consecutive_sl_count) remain
    consistent across crashes.

    `closed_at` is ISO-8601 string from the broker (no parsing in V16).
    r"   trade_idr#   r%   r&   sider'   
exit_pricepnl_usd	closed_atNr1   r   r   r   r3   r3   K   s-    	 MKN
INNr   r3   c                      e Zd ZdZy)BrokerReadErrorua  
    Raised when a broker READ operation cannot determine the true state
    of the broker side (positions, orders, balance) due to transport,
    auth, or account-resolution failure.

    Critical distinction from "empty result": a [] return means the broker
    confirmed there are no positions; raising BrokerReadError means we
    DON'T KNOW. Callers that gate destructive actions (e.g.,
    trade_opener._open_live's pre-check before place_market_bracket)
    MUST treat read failure as 'unknown', NOT as 'flat' — silent []
    on transport failure caused the 28 apr 6J duplicate-bracket incident.
    N)r   r   r   r   r   r   r   r:   r:   d   s     	r   r:   c                  "   e Zd ZU dZdZded<   edd       Zedd       Zedd       Z	edd       Z
edd d
       Zedd!d       Ze	 	 	 d"	 	 	 	 	 	 	 d#d       Zd	dd	 	 	 	 	 d$dZed	d	d	dd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d%d       Ze	 	 	 	 	 	 	 	 	 	 d&d       Ze	 	 	 	 	 	 	 	 	 	 d'd       Zed(d       Zed)d       Ze	 d	 	 	 	 	 d*d       Ze	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d+d       Ze	 	 	 	 	 	 	 	 d,d       Zed-d       Zed.d       Zy	)/
BrokerBaseaX  
    Abstract broker. All methods are async (orchestrator runs async loop).

    Implementations should:
      - Wrap any SDK exceptions into structured results (no raw exceptions
        leaking to orchestrator)
      - Use rate limiting / retries internally where appropriate
      - Be idempotent where possible (e.g., position queries)
    BASEr"   namec                   K   t         w)z<Establish session/auth with broker. Returns True on success.NotImplementedErrorselfs    r   connectzBrokerBase.connect         "!   	c                   K   t         w)z!Clean shutdown of broker session.r@   rB   s    r   
disconnectzBrokerBase.disconnect   rE   rF   c                   K   t         w)zHealth check.r@   rB   s    r   is_connectedzBrokerBase.is_connected   rE   rF   c                   K   t         w)z2Last traded price for symbol. None if unavailable.r@   rC   r#   s     r   get_last_pricezBrokerBase.get_last_price   rE   rF   Nc                   K   t         w)z}
        Get current positions. If symbol is None, return all positions.
        Returns empty list if no positions.
        r@   rL   s     r   positions_getzBrokerBase.positions_get         "!rF   c                   K   t         w)a0  
        Get pending orders (open SL/TP/limit/etc.) on the broker.
        If symbol is None, return all pending orders.
        Returns empty list if no pending orders.

        Used by Reconciler to detect naked SL/TP orders left behind
        after a position closed broker-side (V15-BUG-9).
        r@   rL   s     r   pending_orderszBrokerBase.pending_orders         "!rF   c                   K   t         w)at  
        Get recent closed trades from broker-side history.
        If symbol is None, return trades for all symbols.
        If since is None, broker chooses a sensible default (e.g. 24h).

        Used by Reconciler case (ii) to recover P&L of a trade that
        closed broker-side while bot was down, so daily counters stay
        consistent across crashes.
        r@   )rC   r#   sincelimits       r   recent_tradeszBrokerBase.recent_trades   s       "!rF   i  )rU   rV   c                  K   	 | j                  d||       d{   }	 t        t        d |D                    S 7  # t        $ r Y yw xY w# t        t
        f$ r Y yw xY ww)uO  
        Realized P&L since `since`, summed across every symbol — the
        broker-side aggregate that mirrors what TopstepX displays as
        "RP&L" in the platform UI.

        Default implementation reuses `recent_trades`: ClosedTrade is
        already filtered to closed legs only (open fills have pnl=0
        and are dropped by the impl), so a plain sum is correct.

        Returns None on failure (caller should fall back to its
        in-memory counter rather than report a zero). Subclasses can
        override if the broker exposes a cheaper aggregate endpoint.
        N)r#   rU   rV   c              3  N   K   | ]  }t        |j                  xs d         yw)g        N)r'   r7   ).0ts     r   	<genexpr>z,BrokerBase.get_daily_rpnl.<locals>.<genexpr>   s     EU199#34Es   #%)rW   	Exceptionr'   sum	TypeError
ValueError)rC   rU   rV   tradess       r   get_daily_rpnlzBrokerBase.get_daily_rpnl   so     &	--Te-TTF	EfEEFF	 U 		 :& 		sN   A"> <> A A"> 	A
A"	A

A"AA"AA"ATR)sl_absolute_pricesl_tickstp_ticks	sl_sourcec                  K   t         w)a  
        Place a market order with attached SL and TP (bracket order).

        Implementations should ensure SL/TP are attached to the broker
        after the entry fills (V15 used a 4-step REST flow for this:
        market order -> poll fill -> attach SL -> attach TP).

        Returns:
            OrderResult.success=True with all IDs populated on success,
            OrderResult.success=False with error message on failure.
        r@   )
rC   r#   r$   r&   r   r   rd   re   rf   rg   s
             r   place_market_bracketzBrokerBase.place_market_bracket   s     2 "!rF   c                   K   t         w)a&  
        Place a standalone STOP order (used to re-attach SL after a
        bracket-place failure recovers an orphan position).

        `side` is the protective side (opposite of the entry: BUY entry -> SELL stop).
        OrderResult.entry_id holds the new stop order id on success.
        r@   )rC   r#   r5   r&   
stop_prices        r   place_stop_orderzBrokerBase.place_stop_order        "!rF   c                   K   t         w)z
        Place a standalone LIMIT order (used to re-attach TP after a
        bracket-place failure recovers an orphan position).

        `side` is the protective side. OrderResult.entry_id holds the
        new target order id on success.
        r@   )rC   r#   r5   r&   limit_prices        r   place_limit_orderzBrokerBase.place_limit_order  rm   rF   c                   K   t         w)z@Cancel a pending order. `symbol` is required for broker routing.r@   )rC   r#   r+   s      r   cancel_orderzBrokerBase.cancel_order(  rE   rF   c                   K   t         w)z
        Cancel all pending orders for symbol (used in cleanup of
        orphan SL/TP after position closes). Returns count canceled.
        r@   rL   s     r   cancel_all_for_symbolz BrokerBase.cancel_all_for_symbol-  rP   rF   c                   K   t         w)zk
        Market-close a position (full or partial).
        contracts=None means close everything.
        r@   )rC   r#   r&   s      r   close_positionzBrokerBase.close_position5  rS   rF   c	                   K   t         w)uR  V18 12-mag — chiusura parziale via ordine contrario + ricostruzione bracket.

        Sostituisce `close_position(size=N)` per gli asset (es. 6A) su cui
        ProjectX `/Position/partialCloseContract` ritorna 400. Flow:

          1. Market order direzione opposta per `contracts_to_close` (chiude metà)
          2. Cancella `old_stop_order_id` e `old_target_order_id` (bracket originale)
          3. Piazza nuovo STOP @ `new_sl_price` su `residual_contracts`
          4. Piazza nuovo LIMIT (TP) @ `new_tp_price` su `residual_contracts`

        `direction` = direzione del trade originale (BUY/SELL); l'ordine
        di chiusura usa il side opposto, e gli stop/limit del nuovo
        bracket riusano lo stesso side opposto (com'è già la convenzione
        in place_market_bracket).

        OrderResult.success indica il successo dello STEP 1 (closing market).
        Se step 1 ok ma 3/4 falliscono, il broker emergency-closes per
        evitare posizione senza SL. stop_id / target_id valorizzati con i
        nuovi ID (caller aggiorna `entry.stop_order_id` / `target_order_id`).
        r@   )	rC   r#   r$   contracts_to_closeresidual_contractsnew_sl_pricenew_tp_priceold_stop_order_idold_target_order_ids	            r    partial_close_via_opposite_orderz+BrokerBase.partial_close_via_opposite_orderA  s     @ "!rF   c                   K   t         w)zp
        Modify the stop price of an existing SL order (used for breakeven
        and trailing stops).
        r@   )rC   r#   r+   rz   s       r   modify_stopzBrokerBase.modify_stopc  s      "!rF   c                   K   t         w)u#  
        Current account balance/equity in USD. Used by sizing for
        position calculation. Implementations should rely on broker-side
        caching (SDK socket-pushed account_info); V16 adds no own cache.
        Raises on transport/auth failure — caller decides fallback.
        r@   rB   s    r   get_account_balancezBrokerBase.get_account_balancet  s      "!rF   c                   K   t         w)a  
        Fetch the most recent N bars for `symbol` at `timeframe`.

        timeframe values: "5min" / "1hour" / "4hour" (V16 standard).
        Returns a pandas DataFrame with columns
        [open, high, low, close, volume]; empty DataFrame if no data.

        Used by analysis.market_data.BrokerMarketDataProvider to
        feed build_tech_snapshot. Promoted to abstract in Phase C1
        (was duck-typed before).
        r@   )rC   r#   	timeframens       r   
fetch_barszBrokerBase.fetch_bars  s      "!rF   )returnr   )r   None)r#   r"   r   r   )N)r#   r   r   zlist[Position])r#   r   r   zlist[Order])NN2   )r#   r   rU   Optional[datetime]rV   r%   r   zlist[ClosedTrade])rU   r   rV   r%   r   r   )r#   r"   r$   r"   r&   r%   r   r'   r   r'   rd   r   re   Optional[int]rf   r   rg   r"   r   r
   )
r#   r"   r5   r"   r&   r%   rk   r'   r   r
   )
r#   r"   r5   r"   r&   r%   ro   r'   r   r
   )r#   r"   r+   r"   r   r*   )r#   r"   r   r%   )r#   r"   r&   r   r   r
   )r#   r"   r$   r"   rx   r%   ry   r%   rz   r'   r{   r'   r|   r   r}   r   r   r
   )r#   r"   r+   r"   rz   r'   r   r
   )r   r'   )r#   r"   r   r"   r   r%   )r   r   r   r   r>   r   r   rD   rH   rJ   rM   rO   rR   rW   rb   ri   rl   rp   rr   rt   rv   r~   r   r   r   r   r   r   r<   r<   x   s4    D# " " " " " " " " " " 	" 	"  !%$(	"" "" 	"
 
" "( %)	 " 	
 
@  .2"&"&"" " 	"
 " " +"  "  " " 
" "4 "" " 	"
 " 
" "  "" " 	"
 " 
" "  " " " "  $(	"	" !	" 
		" 	" "" "  	"
  " " " )" +" 
" "B 
"
" 
" 	
"
 

" 
"  " " " "r   r<   N)r   
__future__r   abcr   r   dataclassesr   r   typingr   r
   r!   r*   r.   r3   r]   r:   r<   r   r   r   <module>r      s    # # !   
 
 
          $   $  0	i 	(W" W"r   