💬 Forum

PatternLiquidityHunting_Strategy

EvgeniyShlyakhtin/Best-AI-Trading-Crypto-via-Freqtrade/user_data/strategies/0.bk/PatternLiquidityHunting_Strategy.py · ⑂1 · first seen 2026-07-19 · repo updated 2025-04-19

Basics mode: spot timeframe: 5m
Settings stoploss: -0.03 has minimal roi trailing custom stoploss process only new candles startup candle count: 100 hyperopt hyperopt params: 10
Indicators RSI Stochastic ta
Concepts breakout mean_reversion trailing
6 related strategies ( identical code, similar name)

Each tile is a different kind of check — from an instant code lint to full sandboxed backtests and forward tests on recent data. Not sure what a check actually proves? See the FAQ →

Source

Download Raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
"""
Pattern Recognition + Liquidity Hunting Strategy
Chiến lược kết hợp nhận diện mẫu hình nến và săn thanh khoản
Tối ưu cho khung thời gian 5m
"""

import numpy as np
import pandas as pd
from datetime import datetime, timezone
import ta
from pandas import DataFrame
from functools import reduce
from typing import Dict, List, Optional, Tuple
import logging
import os

# Định nghĩa các class và hàm cần thiết để chiến lược có thể chạy mà không cần freqtrade
class IStrategy:
    """Base class for strategies"""
    def __init__(self, config: dict) -> None:
        self.config = config
        self.dp = DataProvider()

class DataProvider:
    """Mock DataProvider class"""
    def __init__(self):
        self.data = {}
        
    def current_whitelist(self):
        return ["BTC/USDT"]
        
    def get_pair_dataframe(self, pair, timeframe):
        # Return empty dataframe
        return pd.DataFrame()
        
    def get_analyzed_dataframe(self, pair, timeframe):
        # Return empty dataframe and empty dict
        return pd.DataFrame(), {}

class CategoricalParameter:
    """Parameter with categorical values"""
    def __init__(self, categories, default, space="buy", optimize=True):
        self.categories = categories
        self.default = default
        self.value = default
        self.space = space
        self.optimize = optimize

class DecimalParameter:
    """Parameter with decimal values"""
    def __init__(self, low, high, default, space="buy", optimize=True):
        self.low = low
        self.high = high
        self.default = default
        self.value = default
        self.space = space
        self.optimize = optimize

class IntParameter:
    """Parameter with integer values"""
    def __init__(self, low, high, default, space="buy", optimize=True):
        self.low = low
        self.high = high
        self.default = default
        self.value = default
        self.space = space
        self.optimize = optimize

class Trade:
    """Mock Trade class"""
    def __init__(self, is_short=False):
        self.is_short = is_short

logger = logging.getLogger(__name__)

def detect_candlestick_patterns(dataframe: pd.DataFrame) -> pd.DataFrame:
    """
    Phát hiện các mẫu hình nến Nhật
    """
    # Tạo một bản sao để tránh thay đổi dataframe gốc
    df = dataframe.copy()
    
    # Tính thân nến, bóng trên và bóng dưới
    df['body'] = abs(df['close'] - df['open'])
    df['upper_shadow'] = df['high'] - df[['open', 'close']].max(axis=1)
    df['lower_shadow'] = df[['open', 'close']].min(axis=1) - df['low']
    df['body_pct'] = 100 * df['body'] / (df['high'] - df['low'])
    df['upper_shadow_pct'] = 100 * df['upper_shadow'] / (df['high'] - df['low'])
    df['lower_shadow_pct'] = 100 * df['lower_shadow'] / (df['high'] - df['low'])
    
    # Xác định nến tăng/giảm
    df['bullish_candle'] = df['close'] > df['open']
    df['bearish_candle'] = df['close'] < df['open']
    
    # Hammer (búa) - nến tăng hoặc giảm với bóng dưới dài ít nhất 2x thân
    df['hammer'] = (
        (df['lower_shadow'] > 2 * df['body']) &
        (df['upper_shadow'] < 0.3 * df['body']) &
        (df['body_pct'] < 40) &
        (df['lower_shadow_pct'] > 50)
    )
    
    # Inverted Hammer (búa ngược) - nến tăng hoặc giảm với bóng trên dài ít nhất 2x thân
    df['inverted_hammer'] = (
        (df['upper_shadow'] > 2 * df['body']) &
        (df['lower_shadow'] < 0.3 * df['body']) &
        (df['body_pct'] < 40) &
        (df['upper_shadow_pct'] > 50)
    )
    
    # Bullish Engulfing (nến bao phủ tăng)
    df['bullish_engulfing'] = (
        (df['bullish_candle']) &
        (df['bearish_candle'].shift(1)) &
        (df['close'] > df['open'].shift(1)) &
        (df['open'] < df['close'].shift(1))
    )
    
    # Bearish Engulfing (nến bao phủ giảm)
    df['bearish_engulfing'] = (
        (df['bearish_candle']) &
        (df['bullish_candle'].shift(1)) &
        (df['close'] < df['open'].shift(1)) &
        (df['open'] > df['close'].shift(1))
    )
    
    # Morning Star (sao mai)
    df['morning_star'] = (
        (df['bearish_candle'].shift(2)) &
        (df['body'].shift(1) < 0.5 * df['body'].shift(2)) &
        (df['bullish_candle']) &
        (df['body'] > 0.5 * df['body'].shift(2)) &
        (df['close'] > (df['open'].shift(2) + df['close'].shift(2)) / 2)
    )
    
    # Evening Star (sao hôm)
    df['evening_star'] = (
        (df['bullish_candle'].shift(2)) &
        (df['body'].shift(1) < 0.5 * df['body'].shift(2)) &
        (df['bearish_candle']) &
        (df['body'] > 0.5 * df['body'].shift(2)) &
        (df['close'] < (df['open'].shift(2) + df['close'].shift(2)) / 2)
    )
    
    # Doji (chữ thập)
    df['doji'] = (df['body'] < 0.1 * (df['high'] - df['low']))
    
    # Spinning Top (con quay)
    df['spinning_top'] = (
        (df['body'] < 0.4 * (df['high'] - df['low'])) &
        (df['upper_shadow'] > 0.2 * (df['high'] - df['low'])) &
        (df['lower_shadow'] > 0.2 * (df['high'] - df['low']))
    )
    
    # Three White Soldiers (ba chiến binh trắng)
    df['three_white_soldiers'] = (
        (df['bullish_candle']) &
        (df['bullish_candle'].shift(1)) &
        (df['bullish_candle'].shift(2)) &
        (df['close'] > df['close'].shift(1)) &
        (df['close'].shift(1) > df['close'].shift(2)) &
        (df['open'] > df['open'].shift(1)) &
        (df['open'].shift(1) > df['open'].shift(2)) &
        (df['open'] > df['close'].shift(1) * 0.97) &  # Open không quá thấp
        (df['open'].shift(1) > df['close'].shift(2) * 0.97)
    )
    
    # Three Black Crows (ba quạ đen)
    df['three_black_crows'] = (
        (df['bearish_candle']) &
        (df['bearish_candle'].shift(1)) &
        (df['bearish_candle'].shift(2)) &
        (df['close'] < df['close'].shift(1)) &
        (df['close'].shift(1) < df['close'].shift(2)) &
        (df['open'] < df['open'].shift(1)) &
        (df['open'].shift(1) < df['open'].shift(2)) &
        (df['open'] < df['close'].shift(1) * 1.03) &  # Open không quá cao
        (df['open'].shift(1) < df['close'].shift(2) * 1.03)
    )
    
    # Piercing Line (đường xuyên thủng)
    df['piercing_line'] = (
        (df['bearish_candle'].shift(1)) &
        (df['bullish_candle']) &
        (df['close'] > (df['open'].shift(1) + df['close'].shift(1)) / 2) &
        (df['close'] < df['open'].shift(1))
    )
    
    # Dark Cloud Cover (mây đen che phủ)
    df['dark_cloud_cover'] = (
        (df['bullish_candle'].shift(1)) &
        (df['bearish_candle']) &
        (df['close'] < (df['open'].shift(1) + df['close'].shift(1)) / 2) &
        (df['close'] > df['close'].shift(1))
    )
    
    # Tweezer Top/Bottom (nhíp trên/dưới)
    df['tweezer_top'] = (
        (df['bullish_candle'].shift(1)) &
        (df['bearish_candle']) &
        (abs(df['high'] - df['high'].shift(1)) / df['high'] < 0.001) &
        (df['close'] < df['close'].shift(1))
    )
    
    df['tweezer_bottom'] = (
        (df['bearish_candle'].shift(1)) &
        (df['bullish_candle']) &
        (abs(df['low'] - df['low'].shift(1)) / df['low'] < 0.001) &
        (df['close'] > df['close'].shift(1))
    )
    
    # Gập (Gap) - khoảng trống
    df['gap_up'] = (df['low'] > df['high'].shift(1) * 1.005)
    df['gap_down'] = (df['high'] < df['low'].shift(1) * 0.995)
    
    return df

def detect_liquidity_hunting(dataframe: pd.DataFrame, atr_multiplier: float = 1.5) -> pd.DataFrame:
    """
    Phát hiện liquidity hunts - những nơi giá đột biến để săn stop loss
    
    - Stop Hunt Up: Giá vượt lên cao và sau đó quay đầu giảm
    - Stop Hunt Down: Giá rớt xuống thấp và sau đó quay đầu tăng
    """
    df = dataframe.copy()
    
    # Tính ATR (Average True Range)
    df['atr'] = ta.volatility.average_true_range(
        high=df['high'],
        low=df['low'],
        close=df['close'],
        window=14
    )
    
    # Tính Bollinger Bands
    df['upper_band'] = ta.volatility.bollinger_hband(
        close=df['close'],
        window=20,
        window_dev=2
    )
    
    df['lower_band'] = ta.volatility.bollinger_lband(
        close=df['close'],
        window=20,
        window_dev=2
    )
    
    # Mẫu hình Stop Hunt Up (Giá vượt lên cao rồi quay đầu)
    df['stop_hunt_up'] = (
        (df['high'] > df['upper_band']) &  # Vượt upper band
        (df['close'] < df['close'].shift(1)) &  # Giá đóng cửa giảm
        ((df['high'] - df['close']) > atr_multiplier * df['atr']) &  # Bóng trên dài
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.2)  # Volume cao
    )
    
    # Mẫu hình Stop Hunt Down (Giá giảm mạnh rồi quay đầu)
    df['stop_hunt_down'] = (
        (df['low'] < df['lower_band']) &  # Giảm dưới lower band
        (df['close'] > df['close'].shift(1)) &  # Giá đóng cửa tăng
        ((df['close'] - df['low']) > atr_multiplier * df['atr']) &  # Bóng dưới dài
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.2)  # Volume cao
    )
    
    # Phát hiện vùng tập trung lệnh - nơi nhiều stop losses tập trung
    # Thường là nơi giá dao động trong range nhỏ rồi đột ngột breakout
    
    # Tính Bollinger Band Width
    df['bb_width'] = (df['upper_band'] - df['lower_band']) / df['close']
    
    # Tích lũy trước bùng nổ (vùng nhiều lệnh tích lũy)
    df['consolidation'] = (
        (df['bb_width'] < df['bb_width'].rolling(window=20).mean() * 0.85) &
        (df['volume'] < df['volume'].rolling(window=20).mean() * 0.8)
    )
    
    # Breakout khỏi vùng tích lũy với volume cao
    df['breakout_up'] = (
        df['consolidation'].shift(1) &
        (df['close'] > df['upper_band']) &
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.5)
    )
    
    df['breakout_down'] = (
        df['consolidation'].shift(1) &
        (df['close'] < df['lower_band']) &
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.5)
    )
    
    # Phát hiện Fakeout (giả vỡ)
    df['fakeout_up'] = (
        (df['close'].shift(1) > df['upper_band'].shift(1)) &  # Đóng cửa trên upper band
        (df['close'] < df['upper_band']) &  # Đóng cửa dưới upper band
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.3)  # Volume cao
    )
    
    df['fakeout_down'] = (
        (df['close'].shift(1) < df['lower_band'].shift(1)) &  # Đóng cửa dưới lower band
        (df['close'] > df['lower_band']) &  # Đóng cửa trên lower band
        (df['volume'] > df['volume'].rolling(window=20).mean() * 1.3)  # Volume cao
    )
    
    return df

class PatternLiquidityHunting_Strategy(IStrategy):
    """
    Chiến lược kết hợp nhận diện mẫu hình nến và săn thanh khoản
    
    - Sử dụng các mẫu hình nến Nhật để xác định xu hướng đảo chiều
    - Kết hợp săn Liquidity Hunts - tín hiệu mà large players/bots săn lệnh Stop Loss
    - Tìm các vùng thanh khoản cao để chờ phá vỡ
    - Tối ưu cho giao dịch tần suất cao (5m)
    """
    
    # Tham số cho nhận diện mẫu hình
    candle_low_high_ratio = DecimalParameter(1.5, 4.0, default=2.0, space="buy", optimize=True)
    candle_body_pct = DecimalParameter(10.0, 50.0, default=30.0, space="buy", optimize=True)
    
    # Tham số cho liquidity hunting
    atr_multiplier = DecimalParameter(1.0, 3.0, default=1.5, space="buy", optimize=True)
    volume_surge_threshold = DecimalParameter(1.2, 2.5, default=1.5, space="buy", optimize=True)
    consolidation_threshold = DecimalParameter(0.7, 0.9, default=0.8, space="buy", optimize=True)
    
    # Tham số cho trend
    ema_short = IntParameter(5, 15, default=8, space="buy", optimize=True)
    ema_long = IntParameter(20, 50, default=26, space="buy", optimize=True)
    
    # Tham số cho RSI
    rsi_period = IntParameter(7, 21, default=14, space="buy", optimize=True)
    rsi_buy = IntParameter(20, 40, default=30, space="buy", optimize=True)
    rsi_sell = IntParameter(60, 80, default=70, space="sell", optimize=True)
    
    # Đòn bẩy và stoploss
    leverage_default = 3
    leverage_max = 8
    stoploss = -0.03
    
    # Time-in-force và ROI
    timeframe = '5m'
    
    minimal_roi = {
        "0": 0.03,     # 3%
        "15": 0.02,    # 2% sau 15 phút
        "30": 0.01,    # 1% sau 30 phút
        "60": 0.005    # 0.5% sau 60 phút
    }
    
    # Trailing stop
    trailing_stop = True
    trailing_stop_positive = 0.01  # 1%
    trailing_stop_positive_offset = 0.015  # 1.5%
    trailing_only_offset_is_reached = True
    
    # Khởi tạo
    startup_candle_count = 100
    process_only_new_candles = True
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Thêm các chỉ báo vào dataframe"""
        
        # Nhận diện mẫu hình nến
        pattern_df = detect_candlestick_patterns(dataframe)
        for column in pattern_df.columns:
            if column not in dataframe.columns:
                dataframe[column] = pattern_df[column]
        
        # Phát hiện liquidity hunts
        hunt_df = detect_liquidity_hunting(dataframe, atr_multiplier=self.atr_multiplier.value)
        for column in hunt_df.columns:
            if column not in dataframe.columns:
                dataframe[column] = hunt_df[column]
        
        # Thêm EMA
        for length in range(5, 51, 5):
            dataframe[f'ema_{length}'] = ta.trend.ema_indicator(
                close=dataframe['close'],
                window=length
            )
        
        # Thêm RSI
        for length in range(7, 22):
            dataframe[f'rsi_{length}'] = ta.momentum.rsi(
                close=dataframe['close'],
                window=length
            )
        
        # Chỉ báo Bollinger Bands
        bollinger = ta.volatility.BollingerBands(
            close=dataframe['close'],
            window=20,
            window_dev=2
        )
        dataframe['bb_middle'] = bollinger.bollinger_mavg()
        dataframe['bb_upper'] = bollinger.bollinger_hband()
        dataframe['bb_lower'] = bollinger.bollinger_lband()
        dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle']
        
        # Chỉ báo Stochastic
        stoch = ta.momentum.StochasticOscillator(
            high=dataframe['high'],
            low=dataframe['low'],
            close=dataframe['close'],
            window=14,
            smooth_window=3
        )
        dataframe['stoch_k'] = stoch.stoch()
        dataframe['stoch_d'] = stoch.stoch_signal()
        
        # Xác định xu hướng tổng thể
        ema_short = self.ema_short.value
        ema_long = self.ema_long.value
        
        dataframe['uptrend'] = dataframe[f'ema_{ema_short}'] > dataframe[f'ema_{ema_long}']
        dataframe['downtrend'] = dataframe[f'ema_{ema_short}'] < dataframe[f'ema_{ema_long}']
        
        # Xác định điểm lý tưởng để vào lệnh
        
        # 1. Mẫu hình nến đảo chiều bullish
        dataframe['reversal_bullish'] = (
            dataframe['hammer'] |
            dataframe['bullish_engulfing'] |
            dataframe['morning_star'] |
            dataframe['piercing_line'] |
            dataframe['tweezer_bottom'] |
            dataframe['three_white_soldiers']
        )
        
        # 2. Mẫu hình nến đảo chiều bearish
        dataframe['reversal_bearish'] = (
            dataframe['inverted_hammer'] |
            dataframe['bearish_engulfing'] |
            dataframe['evening_star'] |
            dataframe['dark_cloud_cover'] |
            dataframe['tweezer_top'] |
            dataframe['three_black_crows']
        )
        
        # 3. Điểm săn thanh khoản trong xu hướng
        dataframe['liquidity_signal_long'] = (
            (dataframe['stop_hunt_down'] | dataframe['fakeout_down']) &
            (~dataframe['downtrend'])
        )
        
        dataframe['liquidity_signal_short'] = (
            (dataframe['stop_hunt_up'] | dataframe['fakeout_up']) &
            (~dataframe['uptrend'])
        )
        
        # 4. Breakout từ vùng tích lũy
        dataframe['breakout_signal_long'] = (
            dataframe['breakout_up'] &
            (dataframe[f'rsi_{self.rsi_period.value}'] < 70)
        )
        
        dataframe['breakout_signal_short'] = (
            dataframe['breakout_down'] &
            (dataframe[f'rsi_{self.rsi_period.value}'] > 30)
        )
        
        # 5. Tổng hợp tất cả các tín hiệu long/short
        dataframe['combined_buy_signal'] = (
            (dataframe['reversal_bullish'] & (dataframe[f'rsi_{self.rsi_period.value}'] < self.rsi_buy.value)) |
            (dataframe['liquidity_signal_long']) |
            (dataframe['breakout_signal_long'])
        )
        
        dataframe['combined_sell_signal'] = (
            (dataframe['reversal_bearish'] & (dataframe[f'rsi_{self.rsi_period.value}'] > self.rsi_sell.value)) |
            (dataframe['liquidity_signal_short']) |
            (dataframe['breakout_signal_short'])
        )
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Xác định tín hiệu vào lệnh"""
        dataframe.loc[:, 'enter_long'] = 0
        dataframe.loc[:, 'enter_short'] = 0
        
        # Điều kiện vào lệnh Long
        conditions_long = []
        
        # 1. Mẫu hình đảo chiều bullish + RSI thấp + Volume cao
        conditions_long.append(
            (dataframe['reversal_bullish']) &
            (dataframe[f'rsi_{self.rsi_period.value}'] < self.rsi_buy.value) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * 1.2)
        )
        
        # 2. Stop Hunt Down (săn thanh khoản dưới và bật lên)
        conditions_long.append(
            (dataframe['stop_hunt_down'] | dataframe['fakeout_down']) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * self.volume_surge_threshold.value) &
            (~dataframe['downtrend'])
        )
        
        # 3. Breakout lên từ vùng tích lũy
        conditions_long.append(
            (dataframe['breakout_up']) &
            (dataframe[f'rsi_{self.rsi_period.value}'] < 70) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * self.volume_surge_threshold.value)
        )
        
        # 4. Hammer cạnh vùng hỗ trợ
        conditions_long.append(
            (dataframe['hammer']) &
            (dataframe['close'] < dataframe['bb_lower'] * 1.01) &
            (dataframe[f'rsi_{self.rsi_period.value}'] < 40) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean())
        )
        
        # 5. Gap up với volume cao
        conditions_long.append(
            (dataframe['gap_up']) &
            (dataframe['uptrend']) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * 1.5)
        )
        
        # Kết hợp điều kiện
        if conditions_long:
            dataframe.loc[reduce(lambda x, y: x | y, conditions_long), 'enter_long'] = 1
        
        # Điều kiện vào lệnh Short
        conditions_short = []
        
        # 1. Mẫu hình đảo chiều bearish + RSI cao + Volume cao
        conditions_short.append(
            (dataframe['reversal_bearish']) &
            (dataframe[f'rsi_{self.rsi_period.value}'] > self.rsi_sell.value) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * 1.2)
        )
        
        # 2. Stop Hunt Up (săn thanh khoản trên và đi xuống)
        conditions_short.append(
            (dataframe['stop_hunt_up'] | dataframe['fakeout_up']) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * self.volume_surge_threshold.value) &
            (~dataframe['uptrend'])
        )
        
        # 3. Breakout xuống từ vùng tích lũy
        conditions_short.append(
            (dataframe['breakout_down']) &
            (dataframe[f'rsi_{self.rsi_period.value}'] > 30) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * self.volume_surge_threshold.value)
        )
        
        # 4. Inverted Hammer cạnh vùng kháng cự
        conditions_short.append(
            (dataframe['inverted_hammer']) &
            (dataframe['close'] > dataframe['bb_upper'] * 0.99) &
            (dataframe[f'rsi_{self.rsi_period.value}'] > 60) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean())
        )
        
        # 5. Gap down với volume cao
        conditions_short.append(
            (dataframe['gap_down']) &
            (dataframe['downtrend']) &
            (dataframe['volume'] > dataframe['volume'].rolling(window=20).mean() * 1.5)
        )
        
        # Kết hợp điều kiện
        if conditions_short:
            dataframe.loc[reduce(lambda x, y: x | y, conditions_short), 'enter_short'] = 1
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Xác định tín hiệu thoát lệnh"""
        dataframe.loc[:, 'exit_long'] = 0
        dataframe.loc[:, 'exit_short'] = 0
        
        # Điều kiện thoát Long
        conditions_exit_long = []
        
        # 1. Mẫu hình đảo chiều bearish
        conditions_exit_long.append(
            dataframe['reversal_bearish']
        )
        
        # 2. Đụng Bollinger Band trên + RSI cao
        conditions_exit_long.append(
            (dataframe['close'] > dataframe['bb_upper'] * 0.98) &
            (dataframe[f'rsi_{self.rsi_period.value}'] > 70)
        )
        
        # 3. Stop hunt up xuất hiện
        conditions_exit_long.append(
            dataframe['stop_hunt_up']
        )
        
        # 4. EMA cross down
        conditions_exit_long.append(
            (dataframe[f'ema_{self.ema_short.value}'] < dataframe[f'ema_{self.ema_long.value}']) &
            (dataframe[f'ema_{self.ema_short.value}'].shift(1) > dataframe[f'ema_{self.ema_long.value}'].shift(1))
        )
        
        # 5. Stochastic cross down từ vùng overbought
        conditions_exit_long.append(
            (dataframe['stoch_k'] < dataframe['stoch_d']) &
            (dataframe['stoch_k'].shift(1) > dataframe['stoch_d'].shift(1)) &
            (dataframe['stoch_k'].shift(1) > 80)
        )
        
        # Kết hợp điều kiện
        if conditions_exit_long:
            dataframe.loc[reduce(lambda x, y: x | y, conditions_exit_long), 'exit_long'] = 1
        
        # Điều kiện thoát Short
        conditions_exit_short = []
        
        # 1. Mẫu hình đảo chiều bullish
        conditions_exit_short.append(
            dataframe['reversal_bullish']
        )
        
        # 2. Đụng Bollinger Band dưới + RSI thấp
        conditions_exit_short.append(
            (dataframe['close'] < dataframe['bb_lower'] * 1.02) &
            (dataframe[f'rsi_{self.rsi_period.value}'] < 30)
        )
        
        # 3. Stop hunt down xuất hiện
        conditions_exit_short.append(
            dataframe['stop_hunt_down']
        )
        
        # 4. EMA cross up
        conditions_exit_short.append(
            (dataframe[f'ema_{self.ema_short.value}'] > dataframe[f'ema_{self.ema_long.value}']) &
            (dataframe[f'ema_{self.ema_short.value}'].shift(1) < dataframe[f'ema_{self.ema_long.value}'].shift(1))
        )
        
        # 5. Stochastic cross up từ vùng oversold
        conditions_exit_short.append(
            (dataframe['stoch_k'] > dataframe['stoch_d']) &
            (dataframe['stoch_k'].shift(1) < dataframe['stoch_d'].shift(1)) &
            (dataframe['stoch_k'].shift(1) < 20)
        )
        
        # Kết hợp điều kiện
        if conditions_exit_short:
            dataframe.loc[reduce(lambda x, y: x | y, conditions_exit_short), 'exit_short'] = 1
        
        return dataframe
    
    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                proposed_leverage: float, max_leverage: float, entry_tag: Optional[str],
                side: str, **kwargs) -> float:
        """
        Điều chỉnh đòn bẩy dựa trên mẫu hình nến và chất lượng của tín hiệu
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        if len(dataframe) < 1:
            return self.leverage_default
        
        last_candle = dataframe.iloc[-1]
        
        # Đánh giá mức độ rõ ràng của mẫu hình
        clear_pattern = False
        high_volume = False
        strong_signal = False
        
        # Kiểm tra tín hiệu rõ ràng và khối lượng cao
        if side == 'long':
            # Mẫu hình rõ ràng
            if last_candle['reversal_bullish'] or last_candle['hammer'] or last_candle['morning_star']:
                clear_pattern = True
            
            # Stop hunt down hoặc fakeout rõ ràng
            if last_candle['stop_hunt_down'] or (last_candle['fakeout_down'] and last_candle['close'] > last_candle['bb_lower'] * 1.05):
                strong_signal = True
                
            # Khối lượng cao
            if last_candle['volume'] > last_candle['volume'].rolling(window=20).mean() * 2.0:
                high_volume = True
                
        else:  # side == 'short'
            # Mẫu hình rõ ràng
            if last_candle['reversal_bearish'] or last_candle['inverted_hammer'] or last_candle['evening_star']:
                clear_pattern = True
            
            # Stop hunt up hoặc fakeout rõ ràng
            if last_candle['stop_hunt_up'] or (last_candle['fakeout_up'] and last_candle['close'] < last_candle['bb_upper'] * 0.95):
                strong_signal = True
                
            # Khối lượng cao
            if last_candle['volume'] > last_candle['volume'].rolling(window=20).mean() * 2.0:
                high_volume = True
        
        # Tính toán đòn bẩy dựa trên chất lượng tín hiệu
        if clear_pattern and high_volume and strong_signal:
            leverage = min(self.leverage_max, max_leverage)  # Đòn bẩy tối đa
        elif (clear_pattern and high_volume) or (high_volume and strong_signal) or (clear_pattern and strong_signal):
            leverage = min(6, max_leverage)  # Đòn bẩy cao
        elif clear_pattern or high_volume or strong_signal:
            leverage = min(4, max_leverage)  # Đòn bẩy trung bình cao
        else:
            leverage = self.leverage_default  # Đòn bẩy mặc định
            
        return leverage
    
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Stoploss động dựa trên mẫu hình nến và Bollinger Bands
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        if len(dataframe) < 1:
            return self.stoploss
        
        last_candle = dataframe.iloc[-1]
        
        # Với lợi nhuận dương, sử dụng Bollinger Band làm stoploss
        if current_profit > 0.015:  # > 1.5% lợi nhuận
            if not trade.is_short:
                # Long trade: stoploss tại middle band
                bb_middle = last_candle['bb_middle']
                sl_distance = (bb_middle / current_rate) - 1
                return max(sl_distance, -0.03)  # Không ít hơn -3%
            else:
                # Short trade: stoploss tại middle band
                bb_middle = last_candle['bb_middle']
                sl_distance = 1 - (bb_middle / current_rate)
                return max(sl_distance, -0.03)  # Không ít hơn -3%
        
        return self.stoploss  # Sử dụng stoploss mặc định
    
    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                           time_in_force: str, current_time: datetime, entry_tag: Optional[str],
                           side: str, **kwargs) -> bool:
        """
        Xác nhận vào lệnh với lọc thêm
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        if len(dataframe) < 1:
            return False
        
        last_candle = dataframe.iloc[-1]
        
        # Không vào lệnh khi BB Width quá nhỏ (volatility quá thấp)
        if last_candle['bb_width'] < 0.03:
            return False
        
        # Không vào lệnh khi volume quá thấp
        if last_candle['volume'] < last_candle['volume'].rolling(window=20).mean() * 0.7:
            return False
        
        # Không vào lệnh long khi RSI quá cao
        if side == 'long' and last_candle[f'rsi_{self.rsi_period.value}'] > 75:
            return False
            
        # Không vào lệnh short khi RSI quá thấp
        if side == 'short' and last_candle[f'rsi_{self.rsi_period.value}'] < 25:
            return False
        
        return True