From 2db5635602a2e057b8db212681cdff303b986ce1 Mon Sep 17 00:00:00 2001 From: tinh Date: Sat, 21 Mar 2026 14:15:50 +0700 Subject: [PATCH] rsi update --- Quant_Hiber_Risk_Stargy/src/signal/rsi_mtf.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Quant_Hiber_Risk_Stargy/src/signal/rsi_mtf.py diff --git a/Quant_Hiber_Risk_Stargy/src/signal/rsi_mtf.py b/Quant_Hiber_Risk_Stargy/src/signal/rsi_mtf.py new file mode 100644 index 0000000..7122afb --- /dev/null +++ b/Quant_Hiber_Risk_Stargy/src/signal/rsi_mtf.py @@ -0,0 +1,88 @@ +from __future__ import annotations +import pandas as pd +import pandas_ta as ta + +class RSIMultiTimeframe: + def __init__(self, rsi_period: int = 14, low: float = 30, high: float = 70, mid: float = 50): + self.rsi_period = rsi_period + self.low = low + self.high = high + self.mid = mid + + def compute_rsi(self, df: pd.DataFrame, tf_name: str) -> pd.Series: + # Trả về Series để tiết kiệm bộ nhớ thay vì DataFrame + return ta.rsi(df["close"], length=self.rsi_period).rename(f"rsi_{tf_name}") + + def align_timeframes( + self, + df_m15: pd.DataFrame, + df_h1: pd.DataFrame, + df_h4: pd.DataFrame, + ) -> pd.DataFrame: + # Sắp xếp index để merge_asof hoạt động chính xác + df_m15 = df_m15.sort_index() + + # TÍNH RSI VÀ SHIFT 1 NẾN: + # Việc shift(1) đảm bảo M15 chỉ sử dụng RSI của nến H1/H4 ĐÃ ĐÓNG HOÀN TOÀN + rsi_h1 = self.compute_rsi(df_h1, "h1").shift(1) + rsi_h4 = self.compute_rsi(df_h4, "h4").shift(1) + rsi_m15 = self.compute_rsi(df_m15, "m15") # M15 không shift vì ta coi như trigger tại giá đóng nến M15 + + # Merge H1 + df = pd.merge_asof( + df_m15, + rsi_h1, + left_index=True, + right_index=True, + direction="backward", + ) + + # Merge H4 + df = pd.merge_asof( + df, + rsi_h4, + left_index=True, + right_index=True, + direction="backward", + ) + + df["rsi_m15"] = rsi_m15 + + # Bỏ qua các nến đầu tiên bị NaN do chưa đủ chu kỳ RSI + return df.dropna(subset=["rsi_h4", "rsi_h1", "rsi_m15"]) + + def classify(self, df: pd.DataFrame) -> pd.DataFrame: + df = df.copy() + + # Thêm vùng đệm (Buffer) 52/48 để tránh nhiễu tại vùng 50 + df["trend"] = 0 + df.loc[df["rsi_h4"] > 52, "trend"] = 1 + df.loc[df["rsi_h4"] < 48, "trend"] = -1 + + df["bias"] = 0 + df.loc[df["rsi_h1"] > 50, "bias"] = 1 + df.loc[df["rsi_h1"] < 50, "bias"] = -1 + + # TRIGGER (M15) - Giữ nguyên logic Reversal của bạn vì nó rất tốt + df["rsi_prev"] = df["rsi_m15"].shift(1) + df["trigger_buy"] = (df["rsi_prev"] < self.low) & (df["rsi_m15"] >= self.low) + df["trigger_sell"] = (df["rsi_prev"] > self.high) & (df["rsi_m15"] <= self.high) + + return df + + def generate_signal(self, df: pd.DataFrame) -> pd.DataFrame: + df["signal"] = 0 + # Mua khi: H4 Trend tăng + H1 Bias tăng + M15 RSI cắt lên từ vùng Oversold + buy_cond = (df["trend"] == 1) & (df["bias"] == 1) & df["trigger_buy"] + # Bán khi: H4 Trend giảm + H1 Bias giảm + M15 RSI cắt xuống từ vùng Overbought + sell_cond = (df["trend"] == -1) & (df["bias"] == -1) & df["trigger_sell"] + + df.loc[buy_cond, "signal"] = 1 + df.loc[sell_cond, "signal"] = -1 + return df + + def run(self, df_m15: pd.DataFrame, df_h1: pd.DataFrame, df_h4: pd.DataFrame) -> pd.DataFrame: + df = self.align_timeframes(df_m15, df_h1, df_h4) + df = self.classify(df) + df = self.generate_signal(df) + return df \ No newline at end of file