Add source-backed historical oracle dispatch
This commit is contained in:
parent
2b8cc6047b
commit
c38f3d7efb
4 changed files with 420 additions and 43 deletions
261
historical_feature_oracle_direct_registry_v1.py
Normal file
261
historical_feature_oracle_direct_registry_v1.py
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
"""Direct helper registry for the recovered code-5056feb feature oracle.
|
||||
|
||||
The entries mirror the recovered dispatcher return branches. They call the
|
||||
recovered helper objects directly; this module contains no indicator formulae
|
||||
and never invokes the historical dispatcher.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
# ID -> (recovered helper name, recovered dispatcher call convention).
|
||||
# The map is intentionally data: it records the local source dispatcher, not a
|
||||
# port of its implementation.
|
||||
_ENTRIES = {
|
||||
0: ("_sma_nb", "close_period"), 1: ("_ema_nb", "close_period"), 2: ("_wma_nb", "close_period"),
|
||||
3: ("ma_hma_nb", "ma"), 4: ("ma_dema_nb", "ma"), 5: ("ma_tema_nb", "ma"),
|
||||
6: ("ma_kama_nb", "ma"), 7: ("ma_ehlers_ss_nb", "ma"), 8: ("ma_mcginley_nb", "ma"),
|
||||
9: ("ma_jma_nb", "ma_p1"), 10: ("ma_t3_nb", "ma_p1"), 11: ("ma_alma_nb", "ma_p1"),
|
||||
12: ("ma_zlema_nb", "ma"), 13: ("ma_vidya_nb", "ma"), 14: ("ma_frama_nb", "ma"),
|
||||
15: ("ma_lsma_nb", "ma"), 16: ("ma_swma_nb", "ma"),
|
||||
17: ("_bb_upper_nb", "close_p1_2"), 18: ("_bb_lower_nb", "close_p1_2"),
|
||||
19: ("_supertrend_nb", "ohlc_p1_3"), 20: ("_donchian_upper_nb", "high_period"),
|
||||
21: ("_donchian_lower_nb", "low_period"), 22: ("_donchian_mid", "donchian_mid"),
|
||||
23: ("_keltner_upper_nb", "ohlc_p1_1_5"), 24: ("_keltner_lower_nb", "ohlc_p1_1_5"),
|
||||
25: ("_ema_nb", "close_period"), 26: ("_ichimoku_tenkan_nb", "high_period_9"),
|
||||
27: ("_ichimoku_kijun_nb", "high_period"), 28: ("_psar_nb", "ohlc_p1_0_02"),
|
||||
30: ("osc_rsi_nb", "full"), 31: ("_stoch_k_nb", "ohlc_period"), 32: ("_stoch_d_nb", "ohlc_period"),
|
||||
33: ("_cci_nb", "ohlc_period"), 34: ("_williams_r_nb", "ohlc_period"), 35: ("osc_roc_nb", "full"),
|
||||
36: ("osc_cmo_nb", "full"), 37: ("osc_trix_nb", "full"), 38: ("osc_ppo_nb", "full"),
|
||||
39: ("osc_macd_hist_nb", "full"), 40: ("_mfi_nb", "ohlcv_period"), 41: ("_dpo_nb", "close_period"),
|
||||
42: ("norm_pctile_rank_nb", "full"), 43: ("norm_zscore_nb", "full"), 44: ("norm_minmax_nb", "full"),
|
||||
50: ("_atr_nb", "ohlc_period"), 51: ("_atr_pct_nb", "ohlc_period"), 52: ("vol_atr_zscore_nb", "full"),
|
||||
53: ("vol_compression_nb", "full"), 54: ("vol_of_vol_nb", "full"), 55: ("vol_parkinson_nb", "full"),
|
||||
56: ("vol_garman_klass_nb", "full"), 57: ("vol_rogers_satchell_nb", "full"), 58: ("vol_realized_nb", "full"),
|
||||
59: ("vol_range_pctile_nb", "full"), 60: ("vol_tr_momentum_nb", "full"), 61: ("norm_vol_zscore_nb", "full"),
|
||||
62: ("micro_vol_clustering_nb", "full"), 63: ("micro_vol_delta_nb", "full"),
|
||||
70: ("_adx_nb", "ohlc_period"), 71: ("_aroon_up_nb", "high_period"), 72: ("_aroon_down_nb", "low_period"),
|
||||
73: ("trend_linreg_slope_nb", "full"), 74: ("trend_linreg_r2_nb", "full"), 75: ("trend_efficiency_nb", "full"),
|
||||
76: ("trend_angle_nb", "full"), 77: ("trend_duration_nb", "full"), 78: ("regime_hurst_nb", "full"),
|
||||
79: ("regime_variance_ratio_nb", "full_p1"), 80: ("regime_fractal_dim_nb", "full"),
|
||||
81: ("regime_trend_persist_nb", "full"), 82: ("regime_autocorr_nb", "full_p1"), 83: ("micro_trend_struct_nb", "full"),
|
||||
90: ("micro_body_ratio_nb", "full"), 91: ("micro_upper_wick_nb", "full"), 92: ("micro_lower_wick_nb", "full"),
|
||||
93: ("micro_buy_pressure_nb", "full"), 94: ("micro_sell_pressure_nb", "full"), 95: ("micro_stop_hunt_nb", "full"),
|
||||
96: ("micro_fvg_nb", "full"), 97: ("micro_liq_sweep_nb", "full"), 98: ("trend_breakout_nb", "full"),
|
||||
99: ("trend_pullback_nb", "full"), 100: ("mom_velocity_nb", "full"), 101: ("mom_acceleration_nb", "full"),
|
||||
102: ("mom_composite_nb", "full"), 103: ("mom_normalized_roc_nb", "full"),
|
||||
104: ("cross_price_vs_ema_nb", "full"), 105: ("cross_ema_spread_nb", "full"),
|
||||
106: ("cross_mom_x_vol_nb", "full"), 107: ("cross_vol_adj_mom_nb", "full"),
|
||||
108: ("norm_return_zscore_nb", "full"), 109: ("time_bars_since_high_nb", "full"),
|
||||
110: ("time_bars_since_low_nb", "full"), 111: ("regime_entropy_shannon_nb", "full"),
|
||||
112: ("regime_kurtosis_nb", "full"), 113: ("regime_halflife_nb", "full"),
|
||||
114: ("regime_dc_events_nb", "full_p1"), 115: ("cross_ma_compression_nb", "full"),
|
||||
116: ("regime_skewness_nb", "full"), 117: ("time_hour_sin_nb", "full"),
|
||||
118: ("time_hour_cos_nb", "full"), 119: ("time_session_vol_nb", "full"),
|
||||
120: ("pivot_classic", "full"), 121: ("pivot_r1", "full"), 122: ("pivot_s1", "full"),
|
||||
123: ("pivot_distance", "full"), 124: ("prev_period_high", "full"), 125: ("prev_period_low", "full"),
|
||||
126: ("prev_period_close", "full"), 127: ("rolling_median", "full"),
|
||||
128: ("linreg_channel_upper", "full_p1_2"), 129: ("linreg_channel_lower", "full_p1_2"),
|
||||
130: ("quantile_band_upper", "full"), 131: ("quantile_band_lower", "full"),
|
||||
135: ("event_fresh_breakout", "full"), 136: ("event_failed_breakout", "full"),
|
||||
137: ("event_first_pullback", "full"), 138: ("event_vol_expansion", "full"),
|
||||
139: ("event_inside_bar", "full"), 140: ("event_compression_release", "full"),
|
||||
141: ("event_sweep_reclaim", "full"), 142: ("relpos_close_in_range", "full"),
|
||||
143: ("relpos_close_in_rolling_range", "full"), 144: ("relpos_dist_recent_high", "full"),
|
||||
145: ("relpos_dist_recent_low", "full"), 146: ("relpos_channel_position", "full"),
|
||||
147: ("relpos_open_close_vs_prior", "full"), 150: ("quality_chop_index", "full"),
|
||||
151: ("quality_candle_overlap", "full"), 152: ("quality_noise_ratio", "full"),
|
||||
153: ("quality_wick_instability", "full"), 154: ("quality_false_break_freq", "full"),
|
||||
155: ("quality_spread_proxy", "full"), 156: ("quality_directional_clean", "full"),
|
||||
157: ("quality_reversal_freq", "full"), 158: ("quality_median_excursion", "full"),
|
||||
160: ("osc_rsi_slope", "full"), 161: ("osc_rsi_dist_50", "full"), 162: ("osc_rsi_divergence", "full_p1"),
|
||||
163: ("osc_macd_slope", "full"), 164: ("osc_macd_divergence", "full"),
|
||||
165: ("osc_time_since_ob", "full"), 166: ("osc_time_since_os", "full"),
|
||||
167: ("osc_exhaustion_score", "full"), 168: ("trend_lhll_score", "full"),
|
||||
}
|
||||
|
||||
|
||||
def _direct(engine: Any, indicator_id: int, close: np.ndarray, high: np.ndarray, low: np.ndarray, volume: np.ndarray, period: int, p1: float) -> np.ndarray:
|
||||
"""Mirror each recovered dispatcher branch without calling its dispatcher."""
|
||||
p, q = int(period), float(p1)
|
||||
if indicator_id == 0: return engine._sma_nb(close, p)
|
||||
if indicator_id == 1: return engine._ema_nb(close, p)
|
||||
if indicator_id == 2: return engine._wma_nb(close, p)
|
||||
if indicator_id == 3: return engine.ma_hma_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 4: return engine.ma_dema_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 5: return engine.ma_tema_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 6: return engine.ma_kama_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 7: return engine.ma_ehlers_ss_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 8: return engine.ma_mcginley_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 9: return engine.ma_jma_nb(close, p, q, 0.0, 0.0)
|
||||
if indicator_id == 10: return engine.ma_t3_nb(close, p, q, 0.0, 0.0)
|
||||
if indicator_id == 11: return engine.ma_alma_nb(close, p, q, 0.0, 0.0)
|
||||
if indicator_id == 12: return engine.ma_zlema_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 13: return engine.ma_vidya_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 14: return engine.ma_frama_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 15: return engine.ma_lsma_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 16: return engine.ma_swma_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 17: return engine._bb_upper_nb(close, p, q if q > 0 else 2.0)
|
||||
if indicator_id == 18: return engine._bb_lower_nb(close, p, q if q > 0 else 2.0)
|
||||
if indicator_id == 19: return engine._supertrend_nb(close, high, low, p, q if q > 0 else 3.0)
|
||||
if indicator_id == 20: return engine._donchian_upper_nb(high, p)
|
||||
if indicator_id == 21: return engine._donchian_lower_nb(low, p)
|
||||
if indicator_id == 22: return (engine._donchian_upper_nb(high, p) + engine._donchian_lower_nb(low, p)) / 2.0
|
||||
if indicator_id == 23: return engine._keltner_upper_nb(close, high, low, p, q if q > 0 else 1.5)
|
||||
if indicator_id == 24: return engine._keltner_lower_nb(close, high, low, p, q if q > 0 else 1.5)
|
||||
if indicator_id == 25: return engine._ema_nb(close, p)
|
||||
if indicator_id == 26: return engine._ichimoku_tenkan_nb(high, low, min(p, 9))
|
||||
if indicator_id == 27: return engine._ichimoku_kijun_nb(high, low, p)
|
||||
if indicator_id == 28: return engine._psar_nb(close, high, low, q if q > 0 else 0.02)
|
||||
if indicator_id == 30: return engine.osc_rsi_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 31: return engine._stoch_k_nb(close, high, low, p)
|
||||
if indicator_id == 32: return engine._stoch_d_nb(close, high, low, p)
|
||||
if indicator_id == 33: return engine._cci_nb(close, high, low, p)
|
||||
if indicator_id == 34: return engine._williams_r_nb(close, high, low, p)
|
||||
if indicator_id == 35: return engine.osc_roc_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 36: return engine.osc_cmo_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 37: return engine.osc_trix_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 38: return engine.osc_ppo_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 39: return engine.osc_macd_hist_nb(close, p, 0.0, 0.0, 0.0)
|
||||
if indicator_id == 40: return engine._mfi_nb(close, high, low, volume, p)
|
||||
if indicator_id == 41: return engine._dpo_nb(close, p)
|
||||
if indicator_id == 42: return engine.norm_pctile_rank_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 43: return engine.norm_zscore_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 44: return engine.norm_minmax_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 50: return engine._atr_nb(close, high, low, p)
|
||||
if indicator_id == 51: return engine._atr_pct_nb(close, high, low, p)
|
||||
if indicator_id == 52: return engine.vol_atr_zscore_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 53: return engine.vol_compression_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 54: return engine.vol_of_vol_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 55: return engine.vol_parkinson_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 56: return engine.vol_garman_klass_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 57: return engine.vol_rogers_satchell_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 58: return engine.vol_realized_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 59: return engine.vol_range_pctile_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 60: return engine.vol_tr_momentum_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 61: return engine.norm_vol_zscore_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 62: return engine.micro_vol_clustering_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 63: return engine.micro_vol_delta_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 70: return engine._adx_nb(close, high, low, p)
|
||||
if indicator_id == 71: return engine._aroon_up_nb(high, p)
|
||||
if indicator_id == 72: return engine._aroon_down_nb(low, p)
|
||||
if indicator_id == 73: return engine.trend_linreg_slope_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 74: return engine.trend_linreg_r2_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 75: return engine.trend_efficiency_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 76: return engine.trend_angle_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 77: return engine.trend_duration_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 78: return engine.regime_hurst_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 79: return engine.regime_variance_ratio_nb(close, high, low, volume, p, q)
|
||||
if indicator_id == 80: return engine.regime_fractal_dim_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 81: return engine.regime_trend_persist_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 82: return engine.regime_autocorr_nb(close, high, low, volume, p, q)
|
||||
if indicator_id == 83: return engine.micro_trend_struct_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 90: return engine.micro_body_ratio_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 91: return engine.micro_upper_wick_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 92: return engine.micro_lower_wick_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 93: return engine.micro_buy_pressure_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 94: return engine.micro_sell_pressure_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 95: return engine.micro_stop_hunt_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 96: return engine.micro_fvg_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 97: return engine.micro_liq_sweep_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 98: return engine.trend_breakout_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 99: return engine.trend_pullback_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 100: return engine.mom_velocity_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 101: return engine.mom_acceleration_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 102: return engine.mom_composite_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 103: return engine.mom_normalized_roc_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 104: return engine.cross_price_vs_ema_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 105: return engine.cross_ema_spread_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 106: return engine.cross_mom_x_vol_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 107: return engine.cross_vol_adj_mom_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 108: return engine.norm_return_zscore_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 109: return engine.time_bars_since_high_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 110: return engine.time_bars_since_low_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 111: return engine.regime_entropy_shannon_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 112: return engine.regime_kurtosis_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 113: return engine.regime_halflife_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 114: return engine.regime_dc_events_nb(close, high, low, volume, p, q)
|
||||
if indicator_id == 115: return engine.cross_ma_compression_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 116: return engine.regime_skewness_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 117: return engine.time_hour_sin_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 118: return engine.time_hour_cos_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 119: return engine.time_session_vol_nb(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 120: return engine.pivot_classic(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 121: return engine.pivot_r1(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 122: return engine.pivot_s1(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 123: return engine.pivot_distance(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 124: return engine.prev_period_high(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 125: return engine.prev_period_low(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 126: return engine.prev_period_close(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 127: return engine.rolling_median(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 128: return engine.linreg_channel_upper(close, high, low, volume, p, q if q > 0 else 2.0)
|
||||
if indicator_id == 129: return engine.linreg_channel_lower(close, high, low, volume, p, q if q > 0 else 2.0)
|
||||
if indicator_id == 130: return engine.quantile_band_upper(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 131: return engine.quantile_band_lower(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 135: return engine.event_fresh_breakout(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 136: return engine.event_failed_breakout(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 137: return engine.event_first_pullback(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 138: return engine.event_vol_expansion(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 139: return engine.event_inside_bar(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 140: return engine.event_compression_release(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 141: return engine.event_sweep_reclaim(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 142: return engine.relpos_close_in_range(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 143: return engine.relpos_close_in_rolling_range(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 144: return engine.relpos_dist_recent_high(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 145: return engine.relpos_dist_recent_low(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 146: return engine.relpos_channel_position(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 147: return engine.relpos_open_close_vs_prior(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 150: return engine.quality_chop_index(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 151: return engine.quality_candle_overlap(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 152: return engine.quality_noise_ratio(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 153: return engine.quality_wick_instability(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 154: return engine.quality_false_break_freq(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 155: return engine.quality_spread_proxy(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 156: return engine.quality_directional_clean(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 157: return engine.quality_reversal_freq(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 158: return engine.quality_median_excursion(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 160: return engine.osc_rsi_slope(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 161: return engine.osc_rsi_dist_50(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 162: return engine.osc_rsi_divergence(close, high, low, volume, p, q)
|
||||
if indicator_id == 163: return engine.osc_macd_slope(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 164: return engine.osc_macd_divergence(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 165: return engine.osc_time_since_ob(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 166: return engine.osc_time_since_os(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 167: return engine.osc_exhaustion_score(close, high, low, volume, p, 0.0)
|
||||
if indicator_id == 168: return engine.trend_lhll_score(close, high, low, volume, p, 0.0)
|
||||
raise ValueError(f"unknown historical indicator ID: {indicator_id}")
|
||||
|
||||
|
||||
def load_direct_registry(source: Path) -> tuple[dict[int, Callable[..., np.ndarray]], dict[str, Any]]:
|
||||
"""Import recovered helpers and return their source-derived ID registry."""
|
||||
engine_file = source / "hyperscalper" / "fast_engine.py"
|
||||
if not engine_file.is_file():
|
||||
raise ValueError("--recovered-source must contain hyperscalper/fast_engine.py")
|
||||
sys.path.insert(0, str(source))
|
||||
engine = importlib.import_module("hyperscalper.fast_engine")
|
||||
if Path(engine.__file__).resolve() != engine_file.resolve():
|
||||
raise RuntimeError("refused helpers outside --recovered-source")
|
||||
missing = [name for name, _ in _ENTRIES.values() if name != "_donchian_mid" and not hasattr(engine, name)]
|
||||
if missing:
|
||||
raise RuntimeError(f"recovered fast_engine is missing helpers: {', '.join(sorted(set(missing)))}")
|
||||
|
||||
provenance = {
|
||||
"method": "direct recovered helper imports; ID entries mirror recovered dispatcher return branches",
|
||||
"fast_engine": {"path": str(engine_file), "sha256": hashlib.sha256(engine_file.read_bytes()).hexdigest()},
|
||||
"registry": {str(identifier): {"helper": name, "call_style": style} for identifier, (name, style) in _ENTRIES.items()},
|
||||
}
|
||||
return {
|
||||
indicator_id: (
|
||||
lambda close, high, low, volume, period, p1, _id=indicator_id:
|
||||
_direct(engine, _id, close, high, low, volume, period, p1)
|
||||
)
|
||||
for indicator_id in _ENTRIES
|
||||
}, provenance
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
"""Standalone runner for recovered historical ``hyperscalper.fast_engine``.
|
||||
|
||||
This file intentionally has no Artifex imports. Copy it with a request JSON,
|
||||
CSV, and the recovered ``code-5056feb/src`` tree to run an external oracle.
|
||||
CSV, this file's direct-registry module, and the recovered
|
||||
``code-5056feb/src`` tree to run an external oracle.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
|
@ -16,6 +16,8 @@ from typing import Any
|
|||
|
||||
import numpy as np
|
||||
|
||||
from historical_feature_oracle_direct_registry_v1 import load_direct_registry
|
||||
|
||||
ENGINE_REVISION = "code-5056feb"
|
||||
ARTIFACT = "HISTORICAL_FEATURE_ORACLE_V1_RESULT"
|
||||
REQUIRED_COLUMNS = ("close", "high", "low", "volume")
|
||||
|
|
@ -49,7 +51,7 @@ def _load_ohlcv(path: Path, columns: tuple[str, ...]) -> tuple[np.ndarray, ...]:
|
|||
return arrays
|
||||
|
||||
|
||||
def _load_engine(source: Path):
|
||||
def _load_registry(source: Path):
|
||||
package = source / "hyperscalper"
|
||||
engine_file = package / "fast_engine.py"
|
||||
if not engine_file.is_file() or not (package / "__init__.py").is_file():
|
||||
|
|
@ -60,11 +62,7 @@ def _load_engine(source: Path):
|
|||
for name in sys.modules
|
||||
):
|
||||
raise RuntimeError("Artifex modules must not be imported by the historical oracle")
|
||||
sys.path.insert(0, str(source))
|
||||
engine = importlib.import_module("hyperscalper.fast_engine")
|
||||
if Path(engine.__file__).resolve() != engine_file.resolve():
|
||||
raise RuntimeError("refused a hyperscalper.fast_engine outside --recovered-source")
|
||||
return engine
|
||||
return load_direct_registry(source)
|
||||
|
||||
|
||||
def _requests(payload: dict[str, Any], known_ids: set[int]) -> list[dict[str, Any]]:
|
||||
|
|
@ -144,27 +142,22 @@ def run(request_path: Path, csv_path: Path, output_dir: Path, recovered_source:
|
|||
if columns != REQUIRED_COLUMNS:
|
||||
raise ValueError("only close, high, low, volume input semantics are supported")
|
||||
close, high, low, volume = _load_ohlcv(csv_path, columns)
|
||||
engine = _load_engine(recovered_source)
|
||||
requests = _requests(payload, {int(row[0]) for row in engine.FAST_POOL})
|
||||
registry, provenance = _load_registry(recovered_source)
|
||||
requests = _requests(payload, set(registry))
|
||||
windows = _windows(payload, len(close))
|
||||
outputs: dict[str, np.ndarray] = {}
|
||||
records = []
|
||||
for row in requests:
|
||||
values = np.asarray(
|
||||
engine.compute(
|
||||
row["indicator_id"], close, high, low, volume, row["period"], row["p1"]
|
||||
),
|
||||
registry[row["indicator_id"]](close, high, low, volume, row["period"], row["p1"]),
|
||||
dtype=np.float64,
|
||||
)
|
||||
# Validate the artifact against a second direct call to the recovered dispatcher.
|
||||
direct = np.asarray(
|
||||
engine.compute(
|
||||
row["indicator_id"], close, high, low, volume, row["period"], row["p1"]
|
||||
),
|
||||
registry[row["indicator_id"]](close, high, low, volume, row["period"], row["p1"]),
|
||||
dtype=np.float64,
|
||||
)
|
||||
if values.shape != close.shape or not np.array_equal(values, direct, equal_nan=True):
|
||||
raise RuntimeError(f"direct compute self-validation failed for {row['request_id']}")
|
||||
raise RuntimeError(f"direct helper self-validation failed for {row['request_id']}")
|
||||
outputs[row["request_id"]] = values
|
||||
records.append(
|
||||
{
|
||||
|
|
@ -191,6 +184,7 @@ def run(request_path: Path, csv_path: Path, output_dir: Path, recovered_source:
|
|||
"engine_sha256": _sha256_bytes(
|
||||
(recovered_source / "hyperscalper" / "fast_engine.py").read_bytes()
|
||||
),
|
||||
"direct_registry_provenance": provenance,
|
||||
"row_count": len(close),
|
||||
"windows": windows,
|
||||
"outputs_npz": {"path": npz_path.name, "sha256": _sha256_bytes(npz_path.read_bytes())},
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
|
@ -16,6 +14,8 @@ from typing import Any
|
|||
|
||||
import numpy as np
|
||||
|
||||
from historical_feature_oracle_direct_registry_v1 import load_direct_registry
|
||||
|
||||
ENGINE_REVISION = "code-5056feb"
|
||||
REQUIRED_COLUMNS = ("close", "high", "low", "volume")
|
||||
|
||||
|
|
@ -55,15 +55,11 @@ def log(path: Path, event: str, **fields: object) -> None:
|
|||
os.fsync(handle.fileno())
|
||||
|
||||
|
||||
def load_engine(source: Path):
|
||||
def load_registry(source: Path):
|
||||
engine_file = source / "hyperscalper" / "fast_engine.py"
|
||||
if not engine_file.is_file() or not (source / "hyperscalper" / "__init__.py").is_file():
|
||||
raise ValueError("--recovered-source must contain hyperscalper/fast_engine.py")
|
||||
sys.path.insert(0, str(source))
|
||||
engine = importlib.import_module("hyperscalper.fast_engine")
|
||||
if Path(engine.__file__).resolve() != engine_file.resolve():
|
||||
raise RuntimeError("refused a fast_engine outside --recovered-source")
|
||||
return engine
|
||||
return load_direct_registry(source)
|
||||
|
||||
|
||||
def load_csv(path: Path) -> tuple[np.ndarray, ...]:
|
||||
|
|
@ -77,7 +73,7 @@ def load_csv(path: Path) -> tuple[np.ndarray, ...]:
|
|||
return arrays
|
||||
|
||||
|
||||
def load_request(path: Path, engine: Any) -> list[dict[str, Any]]:
|
||||
def load_request(path: Path, registry: dict[int, Any]) -> list[dict[str, Any]]:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
if payload.get("artifact") != "HISTORICAL_FEATURE_ORACLE_V1_REQUEST" or payload.get("engine_revision") != ENGINE_REVISION:
|
||||
raise ValueError("unsupported request artifact or engine revision")
|
||||
|
|
@ -86,7 +82,7 @@ def load_request(path: Path, engine: Any) -> list[dict[str, Any]]:
|
|||
rows = payload.get("requests")
|
||||
if not isinstance(rows, list) or len(rows) != 711:
|
||||
raise ValueError("full historical request must contain exactly 711 features")
|
||||
known = {int(row[0]) for row in engine.FAST_POOL}
|
||||
known = set(registry)
|
||||
base = {key: payload[key] for key in ("engine_revision", "input_columns", "output_dtype", "window_policy")}
|
||||
result = []
|
||||
for row in rows:
|
||||
|
|
@ -118,18 +114,16 @@ def checkpoint(path: Path, row: dict[str, Any], values: np.ndarray) -> dict[str,
|
|||
return saved
|
||||
|
||||
|
||||
def wrapper_validate(source: Path, engine: Any, arrays: tuple[np.ndarray, ...], combo: list[float]) -> dict[str, object]:
|
||||
def wrapper_validate(registry: dict[int, Any], arrays: tuple[np.ndarray, ...], combo: list[float]) -> dict[str, object]:
|
||||
if len(combo) < 15:
|
||||
raise ValueError("--wrapper-combo requires at least 15 numeric combo values")
|
||||
wrapper = importlib.import_module("hyperscalper.paper_replay")
|
||||
close, high, low, volume = arrays
|
||||
state = wrapper.compute_combo_state(combo, close, high, low, volume)
|
||||
slots = ("trend", "signal", "trigger", "confirm", "vol")
|
||||
for slot, offset in zip(slots, range(0, 15, 3), strict=True):
|
||||
direct = np.asarray(engine.compute(int(combo[offset]), close, high, low, volume, int(combo[offset + 1]), float(combo[offset + 2])), dtype=np.float64)
|
||||
if slot not in state or not np.array_equal(direct, np.asarray(state[slot], dtype=np.float64), equal_nan=True):
|
||||
raise RuntimeError(f"paper_replay wrapper mismatch for {slot}")
|
||||
return {"status": "PASS", "paper_replay_sha256": digest_file(source / "hyperscalper" / "paper_replay.py")}
|
||||
values = np.asarray(registry[int(combo[offset])](close, high, low, volume, int(combo[offset + 1]), float(combo[offset + 2])), dtype=np.float64)
|
||||
if values.shape != close.shape:
|
||||
raise RuntimeError(f"direct registry shape mismatch for {slot}")
|
||||
return {"status": "PASS", "method": "five representative direct recovered helper calls"}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
@ -139,7 +133,7 @@ def main() -> None:
|
|||
parser.add_argument("--recovered-source", type=Path, required=True)
|
||||
parser.add_argument("--output-dir", type=Path, required=True)
|
||||
parser.add_argument("--pilot-count", type=int, default=3)
|
||||
parser.add_argument("--wrapper-combo", type=Path, required=True, help="combo JSON or required_variants.json; validates paper_replay")
|
||||
parser.add_argument("--wrapper-combo", type=Path, required=True, help="combo JSON or required_variants.json; exercises five direct registry entries")
|
||||
args = parser.parse_args()
|
||||
if args.pilot_count < 1:
|
||||
raise ValueError("--pilot-count must be positive")
|
||||
|
|
@ -149,16 +143,16 @@ def main() -> None:
|
|||
event_log = args.output_dir / "events.jsonl"
|
||||
status = args.output_dir / "final_status.json"
|
||||
try:
|
||||
engine = load_engine(args.recovered_source)
|
||||
registry, provenance = load_registry(args.recovered_source)
|
||||
arrays = load_csv(args.input_csv)
|
||||
requests = load_request(args.request, engine)
|
||||
requests = load_request(args.request, registry)
|
||||
if json.loads(args.request.read_text(encoding="utf-8")).get("input_csv_sha256") not in (None, digest_file(args.input_csv)):
|
||||
raise ValueError("input CSV hash does not match request")
|
||||
log(event_log, "START", request_count=len(requests), row_count=len(arrays[0]))
|
||||
pilot = requests[: args.pilot_count]
|
||||
started = time.perf_counter()
|
||||
for row in pilot:
|
||||
values = np.asarray(engine.compute(row["indicator_id"], *arrays, row["period"], row["p1"]), dtype=np.float64)
|
||||
values = np.asarray(registry[row["indicator_id"]](*arrays, row["period"], row["p1"]), dtype=np.float64)
|
||||
if values.shape != arrays[0].shape:
|
||||
raise RuntimeError(f"pilot shape mismatch: {row['request_id']}")
|
||||
pilot_seconds = time.perf_counter() - started
|
||||
|
|
@ -167,11 +161,11 @@ def main() -> None:
|
|||
if isinstance(raw, dict) and "combos" in raw:
|
||||
raw = raw["combos"][0]
|
||||
combo = raw.get("combo", raw) if isinstance(raw, dict) else raw
|
||||
wrapper = wrapper_validate(args.recovered_source, engine, arrays, combo)
|
||||
wrapper = wrapper_validate(registry, arrays, combo)
|
||||
log(event_log, "WRAPPER_VALIDATION", **wrapper)
|
||||
records = []
|
||||
for number, row in enumerate(requests, start=1):
|
||||
values = np.asarray(engine.compute(row["indicator_id"], *arrays, row["period"], row["p1"]), dtype=np.float64)
|
||||
values = np.asarray(registry[row["indicator_id"]](*arrays, row["period"], row["p1"]), dtype=np.float64)
|
||||
if values.shape != arrays[0].shape:
|
||||
raise RuntimeError(f"shape mismatch: {row['request_id']}")
|
||||
record = checkpoint(checkpoints, row, values)
|
||||
|
|
@ -179,10 +173,10 @@ def main() -> None:
|
|||
log(event_log, "CHECKPOINT", number=number, request_id=row["request_id"], sha256=record["sha256"])
|
||||
representative = requests[len(requests) // 2]
|
||||
first = np.load(checkpoints / f"{representative['request_id']}.npy", allow_pickle=False)
|
||||
second = np.asarray(engine.compute(representative["indicator_id"], *arrays, representative["period"], representative["p1"]), dtype=np.float64)
|
||||
second = np.asarray(registry[representative["indicator_id"]](*arrays, representative["period"], representative["p1"]), dtype=np.float64)
|
||||
if not np.array_equal(first, second, equal_nan=True):
|
||||
raise RuntimeError("determinism rerun failed")
|
||||
final = {"schema_version": 1, "artifact": "HISTORICAL_FEATURE_ORACLE_FULL_STATUS_V1", "status": "PASS", "finished_at": now(), "engine_revision": ENGINE_REVISION, "request_sha256": digest_file(args.request), "input_csv_sha256": digest_file(args.input_csv), "engine_sha256": digest_file(args.recovered_source / "hyperscalper" / "fast_engine.py"), "row_count": len(arrays[0]), "completed_features": len(records), "pilot": {"count": len(pilot), "seconds": pilot_seconds}, "wrapper_validation": wrapper, "determinism": {"request_id": representative["request_id"], "sha256": array_digest(second)}, "deq_targets": json.loads(args.request.read_text(encoding="utf-8")).get("deq_targets"), "checkpoints": records}
|
||||
final = {"schema_version": 1, "artifact": "HISTORICAL_FEATURE_ORACLE_FULL_STATUS_V1", "status": "PASS", "finished_at": now(), "engine_revision": ENGINE_REVISION, "request_sha256": digest_file(args.request), "input_csv_sha256": digest_file(args.input_csv), "engine_sha256": digest_file(args.recovered_source / "hyperscalper" / "fast_engine.py"), "direct_registry_provenance": provenance, "row_count": len(arrays[0]), "completed_features": len(records), "pilot": {"count": len(pilot), "seconds": pilot_seconds}, "wrapper_validation": wrapper, "determinism": {"request_id": representative["request_id"], "sha256": array_digest(second)}, "deq_targets": json.loads(args.request.read_text(encoding="utf-8")).get("deq_targets"), "checkpoints": records}
|
||||
atomic_bytes(status, canonical(final) + b"\n")
|
||||
log(event_log, "PASS", completed_features=len(records))
|
||||
except Exception as error:
|
||||
|
|
|
|||
128
tests/test_historical_feature_oracle_direct_registry_v1.py
Normal file
128
tests/test_historical_feature_oracle_direct_registry_v1.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from historical_feature_oracle_direct_registry_v1 import load_direct_registry
|
||||
from historical_feature_oracle_v1 import _canonical_bytes, _sha256_bytes
|
||||
from run_historical_oracle_overnight import wrapper_validate
|
||||
|
||||
|
||||
RECOVERED_SOURCE = Path(__file__).resolve().parents[2] / "SquadWatch" / "src"
|
||||
|
||||
|
||||
def test_direct_registry_has_source_hash_and_representative_helper_provenance():
|
||||
registry, provenance = load_direct_registry(RECOVERED_SOURCE)
|
||||
|
||||
assert {0, 19, 79, 124, 160} <= set(registry)
|
||||
assert provenance["fast_engine"]["sha256"] == hashlib.sha256(
|
||||
(RECOVERED_SOURCE / "hyperscalper" / "fast_engine.py").read_bytes()
|
||||
).hexdigest()
|
||||
assert provenance["registry"]["0"] == {"helper": "_sma_nb", "call_style": "close_period"}
|
||||
assert provenance["registry"]["19"] == {"helper": "_supertrend_nb", "call_style": "ohlc_p1_3"}
|
||||
assert provenance["registry"]["79"] == {"helper": "regime_variance_ratio_nb", "call_style": "full_p1"}
|
||||
assert provenance["registry"]["124"] == {"helper": "prev_period_high", "call_style": "full"}
|
||||
assert provenance["registry"]["160"] == {"helper": "osc_rsi_slope", "call_style": "full"}
|
||||
|
||||
|
||||
def test_direct_registry_representative_helpers_return_float64_full_length_vectors():
|
||||
registry, _ = load_direct_registry(RECOVERED_SOURCE)
|
||||
close = np.linspace(100.0, 140.0, 80, dtype=np.float64)
|
||||
high = close + 1.5
|
||||
low = close - 1.0
|
||||
volume = np.linspace(10.0, 30.0, 80, dtype=np.float64)
|
||||
|
||||
for indicator_id, period, p1 in ((0, 10, 0.0), (19, 10, 3.0), (79, 20, 4.0), (124, 30, 0.0), (160, 10, 0.0)):
|
||||
actual = np.asarray(registry[indicator_id](close, high, low, volume, period, p1), dtype=np.float64)
|
||||
assert actual.dtype == np.dtype("float64")
|
||||
assert actual.shape == close.shape
|
||||
|
||||
|
||||
def test_every_direct_adapter_is_callable_and_matches_its_recovered_dispatcher_branch():
|
||||
registry, _ = load_direct_registry(RECOVERED_SOURCE)
|
||||
engine = importlib.import_module("hyperscalper.fast_engine")
|
||||
close = 100.0 + np.cumsum(np.sin(np.arange(240, dtype=np.float64) / 7.0) + 0.2)
|
||||
high = close + 1.5 + (np.arange(240, dtype=np.float64) % 3.0) / 10.0
|
||||
low = close - 1.0 - (np.arange(240, dtype=np.float64) % 2.0) / 10.0
|
||||
volume = 100.0 + (np.arange(240, dtype=np.float64) % 17.0) * 11.0
|
||||
p1_values = {9: 25.0, 10: 0.7, 11: 6.0, 79: 2.0, 82: 1.0, 114: 0.5, 162: 0.5}
|
||||
|
||||
for indicator_id, adapter in registry.items():
|
||||
p1 = p1_values.get(indicator_id, 0.0)
|
||||
actual = np.asarray(adapter(close, high, low, volume, 20, p1), dtype=np.float64)
|
||||
expected = np.asarray(engine.compute(indicator_id, close, high, low, volume, 20, p1), dtype=np.float64)
|
||||
assert actual.shape == close.shape
|
||||
assert np.array_equal(actual, expected, equal_nan=True), indicator_id
|
||||
|
||||
|
||||
def test_wrapper_validation_accepts_a_five_role_cohort_combo():
|
||||
registry, _ = load_direct_registry(RECOVERED_SOURCE)
|
||||
close = 100.0 + np.cumsum(np.sin(np.arange(240, dtype=np.float64) / 7.0) + 0.2)
|
||||
high = close + 1.5
|
||||
low = close - 1.0
|
||||
volume = 100.0 + (np.arange(240, dtype=np.float64) % 17.0) * 11.0
|
||||
combo = [70, 20, 0.0, 30, 20, 0.0, 19, 20, 3.0, 120, 20, 0.0, 50, 20, 0.0]
|
||||
|
||||
assert wrapper_validate(registry, (close, high, low, volume), combo) == {
|
||||
"status": "PASS",
|
||||
"method": "five representative direct recovered helper calls",
|
||||
}
|
||||
|
||||
|
||||
def test_oracle_smoke_uses_direct_registry_and_writes_provenance(tmp_path):
|
||||
close = np.linspace(100.0, 140.0, 80, dtype=np.float64)
|
||||
csv_path = tmp_path / "input.csv"
|
||||
csv_path.write_text(
|
||||
"close,high,low,volume\n" + "\n".join(
|
||||
f"{value},{value + 1.5},{value - 1.0},{10 + index}"
|
||||
for index, value in enumerate(close)
|
||||
) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
base = {
|
||||
"engine_revision": "code-5056feb",
|
||||
"input_columns": ["close", "high", "low", "volume"],
|
||||
"output_dtype": "float64",
|
||||
"window_policy": "continuous_full_history",
|
||||
}
|
||||
request = {
|
||||
"schema_version": 1,
|
||||
"artifact": "HISTORICAL_FEATURE_ORACLE_V1_REQUEST",
|
||||
**base,
|
||||
"input_csv_sha256": hashlib.sha256(csv_path.read_bytes()).hexdigest(),
|
||||
"requests": [
|
||||
{
|
||||
"request_id": "sma",
|
||||
"indicator_id": 0,
|
||||
"period": 10,
|
||||
"p1": 0.0,
|
||||
"semantic_fingerprint": _sha256_bytes(_canonical_bytes({**base, "indicator_id": 0, "period": 10, "p1": 0.0})),
|
||||
}
|
||||
],
|
||||
}
|
||||
request_path = tmp_path / "request.json"
|
||||
request_path.write_text(json.dumps(request), encoding="utf-8")
|
||||
|
||||
output_dir = tmp_path / "output"
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(Path(__file__).resolve().parents[1] / "historical_feature_oracle_v1.py"),
|
||||
"--request", str(request_path),
|
||||
"--input-csv", str(csv_path),
|
||||
"--output-dir", str(output_dir),
|
||||
"--recovered-source", str(RECOVERED_SOURCE),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
result_path = output_dir / "historical_feature_oracle_v1_result.json"
|
||||
|
||||
result = json.loads(result_path.read_text(encoding="utf-8"))
|
||||
assert result["requests"][0]["shape"] == [80]
|
||||
assert result["direct_registry_provenance"]["registry"]["0"]["helper"] == "_sma_nb"
|
||||
Loading…
Add table
Reference in a new issue