
    2j`              
         U d Z ddlmZ ddlZddlZddlmZmZ ddlmZmZ ddl	m
Z
mZ ddlZddlmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZmZmZmZm Z  dd	l!m"Z" dd
l#m$Z$ i Z%de&d<   dZ'de&d<   d+dZ(d,d-dZ) G d de*      Z+d.dZ,	 	 	 	 	 	 d/dZ- ed       G d d             Z.dZ/dZ0dZ1dZ2de&d<   dZ3d Z4d!Z5d0d"Z6dd#d$d%dddd&d'd(		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d1d)Z7d2d*Z8y)3u
  
APEX V16 — TechSnapshot.

Single source of truth for the per-symbol tech state consumed by Brains.
Replaces V15's `tech: dict` (cf V15-BUG-3 "init_data with hidden fallbacks"):
typed fields with sane defaults eliminate the "key dimenticata che fa
partire un fallback nascosto" bug class.

Field inventory (mapped to V15 build_tech_dict + the keys both Brains read):

    Identity / market data:
        symbol, price, open
    Bars / timing:
        candle_time         — UTC unix timestamp of the latest M5 bar.
                              Stable for 5 min: Brain dedup key.
    RSI multi-TF:
        rsi (M5), rsi_prev (M5 t-1), rsi_h1, rsi_h4
    ATR / volatility:
        atr_m5_points       — ATR M5 in price points (NOT ticks).
        atr_ratio           — atr / 20-bar avg.
        vol_regime          — "HIGH" if ratio > 1.5 else "NORMAL".
        vol_spike           — last bar volume > 2x 20-bar avg.
    Trend / structure:
        market_structure    — Enum-string: BULLISH_EXPANSION / BEARISH_EXPANSION / RANGING
        h1_struct_bull / h1_struct_bear  — HH+HL / LH+LL on last 6 H1.
        trend_maturity      — max consecutive up/down H1 closes (last 5).
        regime              — TRENDING / TRENDING_SOFT / BREAKOUT / RANGING.
        regime_reason       — human reason from determine_regime.
        regime_near_trending — diagnostics list (calibration log fodder).
    EMA / divergence / MACD:
        deviation_pct       — (close - EMA50_H1) / EMA50_H1 * 100.
        divergence          — "BULLISH" / "BEARISH" / "NONE".
        macd_decelerating   — bool.
    Candle / pattern:
        candle_strength     — body / 10-bar avg body.
        hammer, shooting_star, bull_engulfing, bear_engulfing,
        doji, doji_type, piercing, dark_cloud, morning_star,
        evening_star, volume_weak,
        buy_absorption, sell_absorption.
    VWAP:
        vwap, vwap_deviation_pct.
    Bias H4:
        bias, allowed_direction, h1_compatibility, h1_reason.
    Swing structural (lazy — populated only when caller passes a direction):
        swing_data          — dict {swing_found, swing_type, swing_price, ...}
                              or {} when not requested.
    Anti-revenge / housekeeping:
        consecutive_sl_count — how many SL fires this trading day.
    Tick mechanics (mirrored from config_futures for downstream price math):
        tick_size, tick_value
    Experimental / non-canonical:
        extra: dict[str, Any] — orderflow features, ad-hoc experiments.

The builder `build_tech_snapshot(...)` is async (it awaits the
MarketDataProvider) but does no other I/O. Returns None when bars
are insufficient (V15 parity).
    )annotationsN)	dataclassfield)datetimetimezone)AnyOptional)BiasDatacompute_algo_bias)calc_absorptioncalc_atrcalc_candle_strengthcalc_divergence	calc_dojicalc_engulfingcalc_hammercalc_macd_decel_and_histcalc_market_structurecalc_piercing_darkcalc_rsicalc_star_patternscalc_volume_weakcalc_vwap_intradayidentify_swing_levels)MarketDataProvider)determine_regimez%'dict[tuple[str, int], TechSnapshot]'_TECH_CACHE@   int_TECH_CACHE_MAXc                 ,    t         j                          y)z%Test-only: drop all cached snapshots.N)r   clear     -/home/work/apex_v16/analysis/tech_snapshot.pyclear_tech_cacher&   l   s    r$   c                    | t         j                          yt         D cg c]  }|d   | k(  s| }}|D ]  }t         j                  |d        yc c}w )a  V17 bug #2 hook: drop cached snapshots ad-hoc.

    Called by orchestrator from:
      - _handle_exit / _check_external_close: ensure next scan rebuilds
        fresh after a trade closes (consecutive_sl_count may have changed).
      - reconcile_startup post-reconnect: broker state may have shifted.

    Args:
        symbol: drop only entries for this symbol; None drops everything.
    Nr   )r   r"   pop)symbolkkeys_to_drops      r%   invalidate_tech_cacher,   q   sW     ~*=!adfnA=L= !4 ! >s
   AAc                  4     e Zd ZdZ	 	 	 	 	 	 	 	 d fdZ xZS )StaleDataErrorzV17 bug #2: raised when broker hasn't published the expected M5
    candle after retries. Caller skips the scan WITHOUT marking the
    candle as already-evaluated, so the next tick retries naturally.c                ^    || _         || _        || _        t        |   | d| d|        y )Nu   : broker lag — last bar at u   , expected ≥ )r)   candle_time_seencandle_time_expectedsuper__init__)selfr)   r0   r1   	__class__s       r%   r3   zStaleDataError.__init__   sH      0$8!h34D3E F013	
r$   )r)   strr0   r   r1   r   returnNone)__name__
__module____qualname____doc__r3   __classcell__)r5   s   @r%   r.   r.      s3    H	
	
-0	
HK	
		
 	
r$   r.   c                   | t         v rt         j                  |        |t         | <   t        t               t        kD  rCt         j                  t	        t        t                            t        t               t        kD  rBy y N)r   r(   lenr    nextiter)keysnaps     r%   _store_with_lrurE      sR    
kK
k
_
,T+./0 k
_
,r$   c                    | j                   dk  r| S ||n"t        j                  t        j                        }|j                         | j                   z
  t        z
  }|dk\  }t        j                  | ||      S )z
    Return a copy of `snap` with `candle_age_seconds` and
    `is_candle_closed` recomputed against `now_utc`. All other fields
    are stable for the lifetime of the M5 candle.
    r   )candle_age_secondsis_candle_closed)	candle_timer   nowr   utc	timestampV16_M5_BAR_SECONDSdataclassesreplace)rD   now_utc_nowage	is_closeds        r%   _refresh_candle_agerT      sp     1)7x||HLL/ID>>d...2D
DCqIy r$   T)frozenc                  >   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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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	ed,<   d	ed-<   d	ed.<   ded/<   ded0<   ded1<   ded2<   ded3<   ded4<    ee5      Zd6ed7<   d8Zded9<   d:Z	ded;<   d:Z
ded<<    ee5      Zd6ed=<   d@d>Zy?)ATechSnapshotu$  
    Frozen typed snapshot of indicator state for one symbol at one tick.

    Frozen = no accidental mutation by Brain code (every field reflects
    the "moment of computation"). When the orchestrator advances time it
    builds a fresh TechSnapshot — never mutates the previous one.
    r6   r)   floatpriceopenr   rI   boolrH   rG   rsirsi_prevrsi_h1rsi_h4atr_m5_points	atr_ratio
vol_regime	vol_spikemarket_structureh1_struct_bullh1_struct_beartrend_maturityregimeregime_reasonz	list[str]regime_near_trendingdeviation_pct
divergencemacd_deceleratingmacd_hist_lastcandle_strengthhammershooting_starbull_engulfingbear_engulfingdojiOptional[str]	doji_typepiercing
dark_cloudmorning_starevening_starvolume_weakbuy_absorptionsell_absorptionvwapvwap_deviation_pctbiasallowed_directionh1_compatibility	h1_reason)default_factorydict[str, Any]
swing_datar   consecutive_sl_count        	tick_size
tick_valueextrac                    ddl m}  ||       S )a  
        Flat dict view for consumers that pre-date TechSnapshot.

        Used at the trade_opener boundary (V16-day-1 trade_opener still
        accepts tech_now: dict). New consumers should read fields via
        attribute access; this helper is a thin compatibility bridge.
        r   )asdict)rN   r   )r4   r   s     r%   to_dictzTechSnapshot.to_dict  s     	'd|r$   N)r7   r   )r9   r:   r;   r<   __annotations__r   dictr   r   r   r   r   r   r#   r$   r%   rW   rW      s`    KL
K  
JOMM OO K## O
 L
JN K IN "'t!<J< !"#! IuJ "$7E>7	r$   rW   5min1hour4houri,  rM      
      c                ~   | i S d}d}|D ]  \  }}t        t        | |d            s|} n t        t        | dd      xs d      }t        t        | dd      xs d      }t        t        | d||kD              }t        |d      t        t        t        | d	d      xs d      d      t        t        t        | d
d      xs d      d      t        t        t        | dd      xs d      d      t        t        t        | dd      xs d      d      t        t        | dd            |t        t        t        | dd      xs d      d      t	        t        | dd      xs d      t        t        | dd            t        t        | dd            t        t        | dd            t	        t        | dd      xs d      |dS )u   Return the standard set of tech fields to embed in brain_log entries.

    Empty dict when `tech` is None — call sites can safely splat the
    result with `**` even on reject paths that never built a snapshot.
    )	)rr   BULL_ENGULF)rs   BEAR_ENGULF)rp   HAMMER)rq   SHOOTING_STAR)ry   MORNING_STAR)rz   EVENING_STAR)rw   PIERCING)rx   
DARK_CLOUD)rt   DOJI Fr\   r   r]   bouncing_rsi   r^   r_   r      rn      rm   ra   r   re   rf   r{   rl   )rsi_m5r^   r_   	h1_compat	macd_hist
macd_decelpatternra   r   re   rf   r{   rl   r   )r[   getattrrX   roundr6   )techpattern_flagspattern_labelattrlabelrsi_nowr]   r   s           r%   tech_log_fieldsr   9  s    |	
M M$ edE*+!M GD%-45GWT:s3:s;Hng6HIJL+gdHc&B&Ic JANgdHc&B&Ic JANgd4F&L&SPS TVWXgd4Dc&J&Qc RTUVwt-@%HI'gdK&E&L MqQgdFB7=2>wt-=uEFwt-=uEFwt]EBCgdL"=CD& r$      d   2   r         ?)	r   bars_m5bars_h1bars_h4direction_for_swingrP   min_candle_timelag_max_retrieslag_retry_sleep_secondsc                  K   |duxs |dk7  }|j                  | t        |       d{   }|t        |      t        k  ryt	        |      }|
dkD  r||
k  rt        |      D ]e  }t        j                  |       d{    |j                  | t        |       d{   }|t        |      t        k  rTt	        |      }||
k\  se n ||
k  rt        | ||
      |dkD  r|s| |fnd}||t        v rt        t        |   |	      S |j                  | t        |       d{   }|j                  | t        |       d{   }||yt        |      t        k  st        |      t        k  ryt        |      }t!        |j"                  d         }t!        |j%                  d      j'                         j"                  d         }t)        j*                  |      st)        j*                  |      s|dk  ry||dz   z  }||dz  kD  rdnd	}t-        ||       }t/        |      }t/        |      }t/        |      }t!        |j"                  d         }t!        |j"                  d
         }t!        |j"                  d         }t!        |j"                  d         } t!        |d   j1                  d      j'                         j"                  d         }!t!        |d   j"                  d         }"|"|!z
  |!z  dz  }#d|j2                  v rdnd}$|$|j2                  v ret!        ||$   j"                  d         }%t!        ||$   j%                  d      j'                         j"                  d         }&t5        |%|&dz  kD        }'nd}'t7        |      \  }(})t9        |      \  }*}+t;        |      \  },}-t=        |      \  }.}/t?        |      \  }0}1tA        |      }2tC        ||      \  }3}4tE        |      \  }5}6t	        |      }7|	|	n"tG        jH                  tJ        jL                        }8|7dkD  r |8jO                         |7z
  tP        z
  }9|9dk\  }:nd}9d}:tS        ||d   tU        |d         ||'t5        |d         t5        |d               \  };}<}=tW        |      }>i }?|dv ro|dkD  r>|dk  r9ddl,}@t[        dtU        |@j]                  |@j_                  |                         }And}Ata        ||d|At!        |d   j"                  d               }?tc        |      \  }B}Cte        dJi d| dt!        |d   j"                  d         dt!        |d   j"                  d         d |7d!|:d"|9d#|d$|d%|d&| d'|d(|d)|d*|'d|d   dt5        |d         dt5        |d         dtU        |d         d+|;d,|<d-|=d.|#d/tg        ||      d0|Bd1|Cd2ti        |      d3|(d4|)d5|*d6|+d7|-d8|,d9|.d:|/d;|0d<|1d=|2d>|3d?|4d@|5dA|6dB|>jj                  dC|>jl                  dD|>jn                  dE|>jp                  dF|?dG|dH|dI|}D|ts        |D       DS 7 *7 7 7 :7 w)Ku-  
    Async-fetch bars from `provider` and compute the full TechSnapshot.

    Args:
        symbol: instrument root.
        provider: MarketDataProvider supplying OHLCV bars.
        tick_size, tick_value: from config_futures.ASSETS_MAP[symbol].
        consecutive_sl_count: anti-revenge counter (caller-managed).
        bars_m5/h1/h4: how many bars to request per timeframe.
        direction_for_swing: if "BUY"/"SELL", populate swing_data;
                             None leaves it {}.

    Returns:
        TechSnapshot, or None if any timeframe has insufficient bars
        (V15 build_tech_dict parity).

    Caching: candle-keyed by (symbol, candle_time_M5). On a cache hit
    only the M5 probe fetch runs; H1+H4 fetches and indicator math are
    skipped. `candle_age_seconds` / `is_candle_closed` are recomputed
    at lookup so callers always see fresh wall-clock-derived fields.
    Cache bypass: when `direction_for_swing` is set OR
    `consecutive_sl_count != 0`, the cache is neither read nor written
    (these inputs change the snapshot but aren't in the key). The
    orchestrator's hot path passes neither, so the cache applies there.

    V17 bug #2 — broker lag sentinel:
        If `min_candle_time` > 0, verify df5.iloc[-1].time >= min_candle_time.
        If lower (broker hasn't published the expected M5 candle yet),
        retry the M5 fetch up to `lag_max_retries` times with
        `lag_retry_sleep_seconds` backoff. If still lagged after retries,
        raise StaleDataError so the caller can skip without consuming
        the candle.
    Nr   )r)   r0   r1      g&.>r   HIGHNORMALcloser   )spang      Y@volumetick_volumer   Fr   rd   rg   re   rf   )r^   rd   rg   ra   rc   re   rf   )BUYSELLr   r   )	directionlookbackra   price_decimalsentry_pricer)   rY   rZ   rI   rH   rG   r\   r]   r^   r_   r`   ra   rb   rc   rh   ri   rj   rk   rl   rm   rn   ro   rp   rq   rr   rs   rt   rv   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r#   ):get_bars	V16_TF_M5r@   _MIN_M5_extract_candle_timerangeasynciosleepr.   r   rT   	V16_TF_H1	V16_TF_H4_MIN_H1_MIN_H4r   rX   ilocrollingmeanpdisnar   r   ewmcolumnsr[   r   r   r   r   r   r   r   r   r   rJ   r   rK   rL   rM   r   r   r   mathmaxfloorlog10r   r   rW   r   r   r   r   r   r   rE   )Er)   providerr   r   r   r   r   r   r   rP   r   r   r   cache_bypassdf5candle_time_probe_	cache_keydf1df4
atr_seriesatr_curatr_avgra   rb   structrsi_m5_seriesrsi_h1_seriesrsi_h4_seriesrsi_curr]   r^   r_   ema50_h1last_h1_closerk   vol_collast_volavg_volrc   rp   rq   bull_engbear_engrv   has_dojirw   rx   ry   rz   r{   r|   r}   r~   vwap_devrI   rQ   rG   rH   rh   ri   near_trendingr   r   r   r   r   rn   rD   sE                                                                        r%   build_tech_snapshotr   j  s    d 	4'D+?1+D 
 !!&)W=
=C
{c#h(,S1 0?B' 	A-- 7888 ))&)WEEC{c#h0 4S 9 O3	 . !2%4  !, 
"#=A  k!9";y#97CC !!&)W=
=C!!&)W=
=C
{ck
3x'SX/ #JJOOB'(GJ&&r*//166r:;G	www2777+w!|7T>*I"Ws]2J #3/F SMMSMMSMMM&&r*+G]''+,H=%%b)*F=%%b)*F S\%%2%.335::2>?H#g,++B/0M#h.(:eCM #ckk1h}G#++W**2./G,,R0557<<R@AGaK/0		 (,FM',Hh#C.Ix-c2Hj!3C!8L,"3'K&5c7&C#NO (,ND( 's+K )7x||HLL/IDQ"nn.<@RR-2   ,< 236"234F#345F#345,(FM= 's+D "$Jo-q=Y] SDJJy4I)J%K$KLNN*))c'l//34

 ":#!>J 22CL%%b)*2 3v;##B'(2  	2
 *2 .2 2 2 2 2 2 2 2 2   232  F#345!2" F#345#2$ 6"234%2& '2( $)2* ++2, $-2. #36/20 %122 &324 -S1526 728 $92:  ;2<  =2> ?2@ A2B C2D E2F "G2H "I2J  K2L &M2N (O2P Q2R $S2T YYU2V 00W2X ..Y2Z ..[2\ ]2^ 2_2` a2b c2Df 	4(Ku > 9E, >
=s`   &Y!YAY!=Y>Y!Y)Y!AY!!Y"Y! YTY!Y!Y!Y!Y!c                   d}dD ]  }|| j                   v s|} n |rj	 | |   j                  d   }t        |t        j                  t
        f      r,t        t        j                  |      j                               S t        |      S t        | j                  t        j                        r'	 t        | j                  d   j                               S y# t        $ r Y yw xY w# t        $ r Y yw xY w)z}
    Extract M5 last-bar timestamp as UTC unix seconds. Used by Brain
    same-candle dedup. Returns 0 when unavailable.
    N)timerL   r   r   r   )r   r   
isinstancer   	Timestampr   r   rL   	ExceptionindexDatetimeIndex)r   col_tscts_vals       r%   r   r   ]  s    
 F. F 	[%%b)F&2<<":;2<</99;<<v; #))R--.	syy}..011   		
  		s*   AC =
C -%C# 	C C #	C/.C/)r7   r8   r?   )r)   ru   r7   r8   )rC   z'tuple[str, int]'rD   'TechSnapshot'r7   r8   )rD   r
  rP   Optional[datetime]r7   r
  )r   zOptional['TechSnapshot']r7   r   )r)   r6   r   r   r   rX   r   rX   r   r   r   r   r   r   r   r   r   ru   rP   r  r   r   r   r   r   rX   r7   zOptional[TechSnapshot])r   zpd.DataFramer7   r   )9r<   
__future__r   r   rN   r   r   r   r   typingr   r	   pandasr   analysis.biasr
   r   analysis.indicatorsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   analysis.market_datar   analysis.regimer   r   r   r    r&   r,   r  r.   rE   rT   rW   r   r   r   rM   r   r   r   r   r   r   r#   r$   r%   <module>r     s  8t #   ( '    5    " 4 ,  682 7 
!&
Y 
"1
#5, $` ` `N 			  C  

".n !")-"&%(pp !p 	p
 p p p p p 'p  p p p #p pfr$   