Download multi_timeframe/htf_pd_array.py from ICT-SMC/ICT-SMC-Framwork: direct link, hf CLI and curl.
- Browser
- Download file 11.3 kB
-
https://huggingface.co/ICT-SMC/ICT-SMC-Framwork/resolve/main/multi_timeframe/htf_pd_array.py
- Command line
-
hf download hf://ICT-SMC/ICT-SMC-Framwork/multi_timeframe/htf_pd_array.py
-
curl -L -o htf_pd_array.py https://huggingface.co/ICT-SMC/ICT-SMC-Framwork/resolve/main/multi_timeframe/htf_pd_array.py
11.3 kB
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| import numpy as np | |
| import pandas as pd | |
| class HTFPDArrayParams: | |
| """ | |
| HTF PD Array context v0.1. | |
| Consomme les zones HTF déjà calculées. | |
| Sources supportées: | |
| FVG | |
| Order Block | |
| OTE | |
| On cherche si le prix courant HTF se trouve dans une zone, | |
| et on expose ensuite cette information au LTF via alignement causal. | |
| priority: | |
| ordre utilisé quand plusieurs zones se chevauchent. | |
| max_age_bars: | |
| âge maximal d'une zone HTF. | |
| """ | |
| sources: tuple[str, ...] = ( | |
| "order_block", | |
| "fvg", | |
| "ote", | |
| ) | |
| priority: tuple[str, ...] = ( | |
| "order_block", | |
| "fvg", | |
| "ote", | |
| ) | |
| max_age_bars: int = 50 | |
| def __post_init__(self) -> None: | |
| allowed = { | |
| "order_block", | |
| "fvg", | |
| "ote", | |
| } | |
| unknown = ( | |
| set(self.sources) | |
| - allowed | |
| ) | |
| if unknown: | |
| raise ValueError( | |
| f"sources inconnues: {sorted(unknown)}" | |
| ) | |
| if self.max_age_bars < 1: | |
| raise ValueError( | |
| "max_age_bars doit être >= 1" | |
| ) | |
| def detect_htf_pd_array( | |
| df: pd.DataFrame, | |
| params: HTFPDArrayParams = HTFPDArrayParams(), | |
| ) -> pd.DataFrame: | |
| required = { | |
| "high", | |
| "low", | |
| "close", | |
| } | |
| if "order_block" in params.sources: | |
| required.update( | |
| { | |
| "ob_bull", | |
| "ob_bear", | |
| "ob_bull_low", | |
| "ob_bull_high", | |
| "ob_bear_low", | |
| "ob_bear_high", | |
| } | |
| ) | |
| if "fvg" in params.sources: | |
| required.update( | |
| { | |
| "fvg_bull", | |
| "fvg_bear", | |
| "fvg_bull_low", | |
| "fvg_bull_high", | |
| "fvg_bear_low", | |
| "fvg_bear_high", | |
| } | |
| ) | |
| if "ote" in params.sources: | |
| required.update( | |
| { | |
| "ote_bull_active", | |
| "ote_bear_active", | |
| "ote_bull_low", | |
| "ote_bull_high", | |
| "ote_bear_low", | |
| "ote_bear_high", | |
| } | |
| ) | |
| missing = required.difference( | |
| df.columns | |
| ) | |
| if missing: | |
| raise ValueError( | |
| "detect_htf_pd_array(): " | |
| f"colonnes manquantes: {sorted(missing)}" | |
| ) | |
| out = df.copy() | |
| n = len(out) | |
| close = out[ | |
| "close" | |
| ].to_numpy( | |
| dtype=np.float64, | |
| ) | |
| out["htf_pd_array_bull"] = False | |
| out["htf_pd_array_bear"] = False | |
| out["htf_pd_array_type"] = pd.Series( | |
| [None] * n, | |
| dtype="object", | |
| index=out.index, | |
| ) | |
| out["htf_pd_array_low"] = np.nan | |
| out["htf_pd_array_high"] = np.nan | |
| out["htf_pd_array_mid"] = np.nan | |
| out["htf_pd_array_position"] = np.nan | |
| out["htf_pd_array_source_idx"] = pd.Series( | |
| pd.array( | |
| [pd.NA] * n, | |
| dtype="Int64", | |
| ), | |
| index=out.index, | |
| ) | |
| out["htf_pd_array_age_bars"] = pd.Series( | |
| pd.array( | |
| [pd.NA] * n, | |
| dtype="Int64", | |
| ), | |
| index=out.index, | |
| ) | |
| bull_zones = [] | |
| bear_zones = [] | |
| # ========================================================================= | |
| # PRIORITY MAP | |
| # ========================================================================= | |
| priority = { | |
| name: | |
| i | |
| for i, name in enumerate( | |
| params.priority | |
| ) | |
| } | |
| def add_zone( | |
| container: list, | |
| zone_type: str, | |
| idx: int, | |
| low: float, | |
| high: float, | |
| ) -> None: | |
| if ( | |
| not np.isfinite(low) | |
| or not np.isfinite(high) | |
| or high <= low | |
| ): | |
| return | |
| container.append( | |
| { | |
| "type": | |
| zone_type, | |
| "idx": | |
| idx, | |
| "low": | |
| float(low), | |
| "high": | |
| float(high), | |
| } | |
| ) | |
| for i in range(n): | |
| # ===================================================================== | |
| # EXPIRE | |
| # ===================================================================== | |
| bull_zones = [ | |
| z | |
| for z in bull_zones | |
| if ( | |
| i - z["idx"] | |
| <= params.max_age_bars | |
| ) | |
| ] | |
| bear_zones = [ | |
| z | |
| for z in bear_zones | |
| if ( | |
| i - z["idx"] | |
| <= params.max_age_bars | |
| ) | |
| ] | |
| row = out.iloc[i] | |
| # ===================================================================== | |
| # ADD NEW HTF PD ARRAYS | |
| # ===================================================================== | |
| if "order_block" in params.sources: | |
| if bool( | |
| row["ob_bull"] | |
| ): | |
| add_zone( | |
| bull_zones, | |
| "order_block", | |
| i, | |
| row[ | |
| "ob_bull_low" | |
| ], | |
| row[ | |
| "ob_bull_high" | |
| ], | |
| ) | |
| if bool( | |
| row["ob_bear"] | |
| ): | |
| add_zone( | |
| bear_zones, | |
| "order_block", | |
| i, | |
| row[ | |
| "ob_bear_low" | |
| ], | |
| row[ | |
| "ob_bear_high" | |
| ], | |
| ) | |
| if "fvg" in params.sources: | |
| if bool( | |
| row["fvg_bull"] | |
| ): | |
| add_zone( | |
| bull_zones, | |
| "fvg", | |
| i, | |
| row[ | |
| "fvg_bull_low" | |
| ], | |
| row[ | |
| "fvg_bull_high" | |
| ], | |
| ) | |
| if bool( | |
| row["fvg_bear"] | |
| ): | |
| add_zone( | |
| bear_zones, | |
| "fvg", | |
| i, | |
| row[ | |
| "fvg_bear_low" | |
| ], | |
| row[ | |
| "fvg_bear_high" | |
| ], | |
| ) | |
| # OTE is already persistent, so only add on source-range changes. | |
| if "ote" in params.sources: | |
| if bool( | |
| row[ | |
| "ote_bull_active" | |
| ] | |
| ): | |
| source = row.get( | |
| "ote_bull_source_range_idx", | |
| pd.NA, | |
| ) | |
| source = ( | |
| int(source) | |
| if pd.notna(source) | |
| else i | |
| ) | |
| exists = any( | |
| ( | |
| z["type"] == "ote" | |
| and z["idx"] == source | |
| ) | |
| for z in bull_zones | |
| ) | |
| if not exists: | |
| add_zone( | |
| bull_zones, | |
| "ote", | |
| source, | |
| row[ | |
| "ote_bull_low" | |
| ], | |
| row[ | |
| "ote_bull_high" | |
| ], | |
| ) | |
| if bool( | |
| row[ | |
| "ote_bear_active" | |
| ] | |
| ): | |
| source = row.get( | |
| "ote_bear_source_range_idx", | |
| pd.NA, | |
| ) | |
| source = ( | |
| int(source) | |
| if pd.notna(source) | |
| else i | |
| ) | |
| exists = any( | |
| ( | |
| z["type"] == "ote" | |
| and z["idx"] == source | |
| ) | |
| for z in bear_zones | |
| ) | |
| if not exists: | |
| add_zone( | |
| bear_zones, | |
| "ote", | |
| source, | |
| row[ | |
| "ote_bear_low" | |
| ], | |
| row[ | |
| "ote_bear_high" | |
| ], | |
| ) | |
| # ===================================================================== | |
| # PRICE INSIDE ZONE | |
| # ===================================================================== | |
| price = close[i] | |
| candidates = [] | |
| for side, zones in ( | |
| ("bull", bull_zones), | |
| ("bear", bear_zones), | |
| ): | |
| for zone in zones: | |
| if ( | |
| zone["low"] | |
| <= price | |
| <= zone["high"] | |
| ): | |
| candidates.append( | |
| { | |
| **zone, | |
| "side": | |
| side, | |
| } | |
| ) | |
| if not candidates: | |
| continue | |
| # Prefer explicit type priority, then latest zone. | |
| selected = min( | |
| candidates, | |
| key=lambda z: ( | |
| priority.get( | |
| z["type"], | |
| 999, | |
| ), | |
| -z["idx"], | |
| ), | |
| ) | |
| label = out.index[i] | |
| low_value = selected[ | |
| "low" | |
| ] | |
| high_value = selected[ | |
| "high" | |
| ] | |
| size = ( | |
| high_value | |
| - low_value | |
| ) | |
| out.at[ | |
| label, | |
| "htf_pd_array_bull", | |
| ] = ( | |
| selected["side"] | |
| == "bull" | |
| ) | |
| out.at[ | |
| label, | |
| "htf_pd_array_bear", | |
| ] = ( | |
| selected["side"] | |
| == "bear" | |
| ) | |
| out.at[ | |
| label, | |
| "htf_pd_array_type", | |
| ] = selected[ | |
| "type" | |
| ] | |
| out.at[ | |
| label, | |
| "htf_pd_array_low", | |
| ] = low_value | |
| out.at[ | |
| label, | |
| "htf_pd_array_high", | |
| ] = high_value | |
| out.at[ | |
| label, | |
| "htf_pd_array_mid", | |
| ] = ( | |
| low_value | |
| + high_value | |
| ) / 2.0 | |
| out.at[ | |
| label, | |
| "htf_pd_array_position", | |
| ] = ( | |
| ( | |
| price | |
| - low_value | |
| ) | |
| / size | |
| if size > 0 | |
| else np.nan | |
| ) | |
| out.at[ | |
| label, | |
| "htf_pd_array_source_idx", | |
| ] = selected[ | |
| "idx" | |
| ] | |
| out.at[ | |
| label, | |
| "htf_pd_array_age_bars", | |
| ] = ( | |
| i | |
| - selected[ | |
| "idx" | |
| ] | |
| ) | |
| return out | |