| 
 | 1 | +import numpy as np  | 
1 | 2 | from unittest import TestCase  | 
2 |  | -from pyindicators import is_divergence  | 
 | 3 | +from pyindicators import is_divergence, bearish_divergence_multi_dataframe, \  | 
 | 4 | +    PyIndicatorException  | 
3 | 5 | 
 
  | 
4 | 6 | import pandas as pd  | 
5 | 7 | 
 
  | 
@@ -100,3 +102,162 @@ def test_detect_bearish_sequence_with_wrong_params_pandas(self):  | 
100 | 102 |             "DateTime": pd.date_range("2021-01-01", periods=10, freq="D")  | 
101 | 103 |         })  | 
102 | 104 |         self.assertFalse(is_divergence(df, window_size=1, number_of_data_points=10, column_one="RSI_highs", column_two="Close_highs"))  | 
 | 105 | + | 
 | 106 | + | 
 | 107 | +class TestBearishDivergenceMultiDataFrame(TestCase):  | 
 | 108 | + | 
 | 109 | +    def test_bearish_divergence_detected(self):  | 
 | 110 | +        # Setup indicator (e.g., RSI) and price (e.g., Close) with divergence  | 
 | 111 | +        indicator = pd.DataFrame({  | 
 | 112 | +            "RSI": [50, 60, 70, 65, 60, 58, 55],  | 
 | 113 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 114 | +        price = pd.DataFrame({  | 
 | 115 | +            "Close": [100, 102, 105, 108, 110, 112, 115],  # Higher highs  | 
 | 116 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 117 | + | 
 | 118 | +        result = pd.DataFrame(index=indicator.index)  | 
 | 119 | + | 
 | 120 | +        # Force peaks manually for deterministic test  | 
 | 121 | +        indicator["RSI_highs"] = [0, 0, 0, 0, -1, 0, 0]  # Two indicator highs  | 
 | 122 | +        price["Close_highs"] = [0, 0, 0, 0, 1, 0, 0]    # Two price highs  | 
 | 123 | + | 
 | 124 | +        out = bearish_divergence_multi_dataframe(  | 
 | 125 | +            first_df=indicator,  | 
 | 126 | +            second_df=price,  | 
 | 127 | +            result_df=result,  | 
 | 128 | +            first_column="RSI",  | 
 | 129 | +            second_column="Close",  | 
 | 130 | +            window_size=2,  | 
 | 131 | +            result_column="bearish_divergence"  | 
 | 132 | +        )  | 
 | 133 | + | 
 | 134 | +        self.assertIn("bearish_divergence", out.columns)  | 
 | 135 | +        self.assertTrue(any(out["bearish_divergence"]))  | 
 | 136 | + | 
 | 137 | +        indicator = pd.DataFrame({  | 
 | 138 | +            "RSI": [50, 60, 70, 65, 60, 58, 55],  | 
 | 139 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 140 | +        price = pd.DataFrame({  | 
 | 141 | +            "Close": [100, 102, 105, 108, 110, 112, 115],  # Higher highs  | 
 | 142 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 143 | + | 
 | 144 | +        result = pd.DataFrame(index=indicator.index)  | 
 | 145 | + | 
 | 146 | +        # Force peaks manually for deterministic test  | 
 | 147 | +        indicator["RSI_highs"] = [0, 0, 0, 0, 1, 0, 0]  # Two indicator highs  | 
 | 148 | +        price["Close_highs"] = [0, 0, 0, 0, 1, 0, 0]  # Two price highs  | 
 | 149 | + | 
 | 150 | +        out = bearish_divergence_multi_dataframe(  | 
 | 151 | +            first_df=indicator,  | 
 | 152 | +            second_df=price,  | 
 | 153 | +            result_df=result,  | 
 | 154 | +            first_column="RSI",  | 
 | 155 | +            second_column="Close",  | 
 | 156 | +            window_size=2,  | 
 | 157 | +            result_column="bearish_divergence"  | 
 | 158 | +        )  | 
 | 159 | + | 
 | 160 | +        self.assertIn("bearish_divergence", out.columns)  | 
 | 161 | +        self.assertFalse(any(out["bearish_divergence"]))  | 
 | 162 | + | 
 | 163 | +        indicator = pd.DataFrame({  | 
 | 164 | +            "RSI": [50, 60, 70, 65, 60, 58, 55],  | 
 | 165 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 166 | +        price = pd.DataFrame({  | 
 | 167 | +            "Close": [100, 102, 105, 108, 110, 112, 115],  # Higher highs  | 
 | 168 | +        }, index=pd.date_range("2022-01-01", periods=7))  | 
 | 169 | + | 
 | 170 | +        result = pd.DataFrame(index=indicator.index)  | 
 | 171 | + | 
 | 172 | +        # Force peaks manually for deterministic test  | 
 | 173 | +        indicator["RSI_highs"] = [0, 0, 0, -1, 0, 0, 0]  # Two indicator highs  | 
 | 174 | +        price["Close_highs"] = [0, 0, 0, 0, 0, 1, 0]  # Two price highs  | 
 | 175 | + | 
 | 176 | +        out = bearish_divergence_multi_dataframe(  | 
 | 177 | +            first_df=indicator,  | 
 | 178 | +            second_df=price,  | 
 | 179 | +            result_df=result,  | 
 | 180 | +            first_column="RSI",  | 
 | 181 | +            second_column="Close",  | 
 | 182 | +            window_size=2,  | 
 | 183 | +            result_column="bearish_divergence"  | 
 | 184 | +        )  | 
 | 185 | + | 
 | 186 | +        self.assertIn("bearish_divergence", out.columns)  | 
 | 187 | +        self.assertFalse(any(out["bearish_divergence"]))  | 
 | 188 | + | 
 | 189 | +        out = bearish_divergence_multi_dataframe(  | 
 | 190 | +            first_df=indicator,  | 
 | 191 | +            second_df=price,  | 
 | 192 | +            result_df=result,  | 
 | 193 | +            first_column="RSI",  | 
 | 194 | +            second_column="Close",  | 
 | 195 | +            window_size=3,  | 
 | 196 | +            result_column="bearish_divergence"  | 
 | 197 | +        )  | 
 | 198 | + | 
 | 199 | +        self.assertIn("bearish_divergence", out.columns)  | 
 | 200 | +        self.assertTrue(any(out["bearish_divergence"]))  | 
 | 201 | + | 
 | 202 | +    def test_missing_column_exception(self):  | 
 | 203 | +        df1 = pd.DataFrame({"RSI": [50, 60]}, index=pd.date_range("2022-01-01", periods=2))  | 
 | 204 | +        df2 = pd.DataFrame({"Close": [100, 110]}, index=pd.date_range("2022-01-01", periods=2))  | 
 | 205 | +        result = pd.DataFrame(index=df1.index)  | 
 | 206 | + | 
 | 207 | +        with self.assertRaises(PyIndicatorException):  | 
 | 208 | +            bearish_divergence_multi_dataframe(  | 
 | 209 | +                first_df=df1.drop("RSI", axis=1),  | 
 | 210 | +                second_df=df2,  | 
 | 211 | +                result_df=result,  | 
 | 212 | +                first_column="RSI",  | 
 | 213 | +                second_column="Close"  | 
 | 214 | +            )  | 
 | 215 | + | 
 | 216 | +    def test_not_enough_data_exception(self):  | 
 | 217 | +        df1 = pd.DataFrame({"RSI": [50]}, index=pd.date_range("2022-01-01", periods=1))  | 
 | 218 | +        df2 = pd.DataFrame({"Close": [100]}, index=pd.date_range("2022-01-01", periods=1))  | 
 | 219 | +        result = pd.DataFrame(index=df1.index)  | 
 | 220 | + | 
 | 221 | +        # Assume detect_peaks adds _highs column  | 
 | 222 | +        df1["RSI_highs"] = [1]  | 
 | 223 | +        df2["Close_highs"] = [1]  | 
 | 224 | + | 
 | 225 | +        with self.assertRaises(PyIndicatorException):  | 
 | 226 | +            bearish_divergence_multi_dataframe(  | 
 | 227 | +                first_df=df1,  | 
 | 228 | +                second_df=df2,  | 
 | 229 | +                result_df=result,  | 
 | 230 | +                first_column="RSI",  | 
 | 231 | +                second_column="Close",  | 
 | 232 | +                window_size=3  | 
 | 233 | +            )  | 
 | 234 | + | 
 | 235 | +    def test_different_timeframes_align_correctly(self):  | 
 | 236 | +        daily_index = pd.date_range("2022-01-01", periods=2, freq="D")  | 
 | 237 | +        indicator_df = pd.DataFrame({  | 
 | 238 | +            "RSI": [65, 60],  | 
 | 239 | +        }, index=daily_index)  | 
 | 240 | + | 
 | 241 | +        # 2-hour close prices — only some times will match the daily timestamps  | 
 | 242 | +        two_hour_index = pd.date_range("2022-01-01", periods=12, freq="2h")  | 
 | 243 | +        price_df = pd.DataFrame({  | 
 | 244 | +            "Close": [100, 102, 105, 108, 110, 112, 115, 117, 120, 122, 125, 130]  | 
 | 245 | +        }, index=two_hour_index)  | 
 | 246 | + | 
 | 247 | +        result_df = pd.DataFrame(index=price_df.index)  | 
 | 248 | + | 
 | 249 | +        # Inject fake peaks  | 
 | 250 | +        indicator_df["RSI_highs"] = [-1, 0]  | 
 | 251 | +        price_df["Close_highs"] = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  | 
 | 252 | + | 
 | 253 | +        result = bearish_divergence_multi_dataframe(  | 
 | 254 | +            first_df=indicator_df,  | 
 | 255 | +            second_df=price_df,  | 
 | 256 | +            result_df=result_df,  | 
 | 257 | +            first_column="RSI",  | 
 | 258 | +            second_column="Close",  | 
 | 259 | +            window_size=2,  | 
 | 260 | +            result_column="bearish_divergence"  | 
 | 261 | +        )  | 
 | 262 | +        self.assertIn("bearish_divergence", result.columns)  | 
 | 263 | +        self.assertTrue(any(result["bearish_divergence"]))  | 
0 commit comments