💬 Forum

BreakoutFlash

🏆 League #1872 / 1937

remiotore/ccxt-freqtrade/strategies/BreakoutFlash.py · ★3 · ⑂2 · first seen 2026-07-16 · repo updated 2026-01-11 · ⬇ 1 download

Basics mode: spot timeframe: 5m interface version: 3
Settings stoploss: -0.06 has minimal roi trailing custom stoploss process only new candles startup candle count: 288 hyperopt hyperopt params: 17
Indicators ATR EMA talib
Concepts breakout trailing
15 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
import logging
import numpy as np
import pandas as pd
import talib.abstract as ta
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple

from pandas import DataFrame
from functools import reduce
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
                               IntParameter, merge_informative_pair, stoploss_from_open)
from freqtrade.persistence import Trade

logger = logging.getLogger(__name__)

class BreakoutFlash(IStrategy):
    """
    ブレイクアウト検知Flash戦略
    
    重要な価格レベル(サポート/レジスタンス)からのブレイクアウトを早期に検知し、
    その直後の急速な値動きを捉えることで短期的な高リターンを獲得する戦略。
    
    - 1時間足で重要レベル(サポート/レジスタンス)を検出
    - 5分足でエントリー/イグジット判断
    - 過去240時間(10日間)のデータを分析
    - 重要性スコアリングシステムによるレベルの重要度評価
    - ATRベースのリスク管理
    """
    
    # 戦略インターフェースのバージョン
    INTERFACE_VERSION = 3
    
    # 基本設定
    timeframe = '5m'  # エントリー/イグジットの基本タイムフレーム
    informative_timeframe = '1h'  # 重要レベル検出用タイムフレーム
    
    # Funding Rateデータは不要
    funding_rate_required = False
    
    # 必要なローソク足の数(240時間 = 10日分)
    startup_candle_count: int = 288  # 5分足で240時間分 + 余裕
    
    # 適切なペア: ボラティリティのある主要暗号通貨
    # can_short: bool = True  # ショートも可能(オプション)
    
    # 最小ROI設定 - 短期で利確
    minimal_roi = {
        "0": 0.05,    # 即時5%で利確
        "10": 0.025,  # 10分後なら2.5%で利確
        "20": 0.015,  # 20分後なら1.5%で利確
        "30": 0.01    # 30分後なら1%で利確
    }
    
    # ストップロス設定 - ATRベースで動的に設定するので広めの初期値
    stoploss = -0.06
    
    # トレーリングストップロス設定(オプション)
    trailing_stop = True
    trailing_stop_positive = 0.02  # 2%の利益が出たら有効化
    trailing_stop_positive_offset = 0.03  # 3%のバッファを確保
    trailing_only_offset_is_reached = True
    
    # プロセス設定
    process_only_new_candles = True
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False
    
    # 最適化可能なパラメータ
    # ブレイクアウト検出
    breakout_threshold = DecimalParameter(0.005, 0.02, default=0.01, space="buy")
    volume_factor = DecimalParameter(1.2, 2.0, default=1.5, space="buy")
    body_percentage = DecimalParameter(0.6, 0.85, default=0.7, space="buy")
    
    # 重要レベルスコアリング(緩和)
    min_level_score = IntParameter(5, 9, default=5, space="buy")
    level_touch_weight = DecimalParameter(1.5, 3.0, default=2.0, space="buy")
    
    # EMAフィルター
    use_trend_filter = BooleanParameter(default=True, space="buy")
    fast_ema = IntParameter(5, 15, default=9, space="buy")
    slow_ema = IntParameter(15, 30, default=20, space="buy")
    
    # ATRパラメータ
    atr_period = IntParameter(10, 20, default=14, space="buy")
    atr_multiplier_sl = DecimalParameter(0.5, 1.0, default=0.7, space="sell")
    atr_multiplier_tp1 = DecimalParameter(0.8, 1.2, default=1.0, space="sell")
    atr_multiplier_tp2 = DecimalParameter(1.3, 1.8, default=1.5, space="sell")
    atr_multiplier_tp3 = DecimalParameter(1.8, 2.5, default=2.0, space="sell")
    
    # 部分利確のサイズ
    tp1_size = DecimalParameter(0.3, 0.6, default=0.5, space="sell")
    tp2_size = DecimalParameter(0.15, 0.35, default=0.25, space="sell")
    
    # ボラティリティフィルター
    use_volatility_filter = BooleanParameter(default=True, space="buy")
    volatility_filter_factor = DecimalParameter(0.4, 0.7, default=0.5, space="buy")
    
    def informative_pairs(self):
        """
        複数の時間枠でのペア情報を取得
        Funding Rateデータは明示的に除外
        """
        pairs = self.dp.current_whitelist()
        
        # プライスデータのみ取得し、Funding Rateデータは除外
        informative_pairs = []
        for pair in pairs:
            informative_pairs.append((pair, self.informative_timeframe))
            
        # Funding Rateデータを明示的に要求しない
        return informative_pairs
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        テクニカル指標を計算して追加
        """
        # 基本的なテクニカル指標の計算
        # 5分足のATR
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.atr_period.value)
        
        # ボリューム関連計算
        dataframe['volume_mean_20'] = dataframe['volume'].rolling(window=20).mean()
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_mean_20']
        
        # ローソク足の実体の割合を計算
        dataframe['body_size'] = abs(dataframe['open'] - dataframe['close'])
        dataframe['candle_range'] = dataframe['high'] - dataframe['low']
        dataframe['body_percentage'] = dataframe['body_size'] / dataframe['candle_range']
        
        # 1時間足のデータを取得
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'],
                                                timeframe=self.informative_timeframe)
        
        # 1時間足のEMA計算
        informative['ema_fast'] = ta.EMA(informative, timeperiod=self.fast_ema.value)
        informative['ema_slow'] = ta.EMA(informative, timeperiod=self.slow_ema.value)
        informative['trend_up'] = informative['ema_fast'] > informative['ema_slow']
        
        # トレンドの状態をログに出力
        logger.info(f"Pair {metadata['pair']} 1h trend_up: {informative['trend_up'].iloc[-1]}")
        
        # 1時間足のATR
        informative['h1_atr'] = ta.ATR(informative, timeperiod=self.atr_period.value)
        
        # 1時間足のボリューム平均
        informative['h1_volume_mean'] = informative['volume'].rolling(window=24).mean()
        
        # サポートとレジスタンスレベルの検出(過去240時間 = 10日間)
        # ウィンドウサイズを240に設定(1時間足で10日分)
        window_size = min(240, len(informative))
        
        # サポートレベル検出(直近から過去に向かって検索)
        support_levels = []
        resistance_levels = []
        
        # 最近のN本のローソク足のみで分析
        recent_data = informative.tail(window_size).copy()
        
        # サポートとレジスタンスの検出 - 条件を緩和
        for i in range(1, len(recent_data) - 1):
            # サポートの条件: 前後1本より安値が低い(緩和)
            if (recent_data['low'].iloc[i] < recent_data['low'].iloc[i-1] and
                recent_data['low'].iloc[i] < recent_data['low'].iloc[i+1]):
                
                level_price = recent_data['low'].iloc[i]
                days_ago = (len(recent_data) - i) / 24  # 時間足で何日前か
                
                # 類似レベルのマージ(±1.0%以内)- 緩和
                similar_level = False
                for level in support_levels:
                    if abs(level['price'] - level_price) / level_price < 0.01:
                        level['count'] += 1
                        level['volume'] = (level['volume'] + recent_data['volume'].iloc[i]) / 2
                        level['days_ago'] = min(level['days_ago'], days_ago)  # 最新の日付を使用
                        similar_level = True
                        break
                
                if not similar_level:
                    support_levels.append({
                        'price': level_price,
                        'count': 1,
                        'volume': recent_data['volume'].iloc[i],
                        'days_ago': days_ago
                    })
            
            # レジスタンスの条件: 前後1本より高値が高い(緩和)
            if (recent_data['high'].iloc[i] > recent_data['high'].iloc[i-1] and
                recent_data['high'].iloc[i] > recent_data['high'].iloc[i+1]):
                
                level_price = recent_data['high'].iloc[i]
                days_ago = (len(recent_data) - i) / 24  # 時間足で何日前か
                
                # 類似レベルのマージ(±1.0%以内)- 緩和
                similar_level = False
                for level in resistance_levels:
                    if abs(level['price'] - level_price) / level_price < 0.01:
                        level['count'] += 1
                        level['volume'] = (level['volume'] + recent_data['volume'].iloc[i]) / 2
                        level['days_ago'] = min(level['days_ago'], days_ago)  # 最新の日付を使用
                        similar_level = True
                        break
                
                if not similar_level:
                    resistance_levels.append({
                        'price': level_price,
                        'count': 1,
                        'volume': recent_data['volume'].iloc[i],
                        'days_ago': days_ago
                    })
        
        # レベルにスコアを付ける
        avg_volume = recent_data['volume'].mean()
        
        # サポートレベルのスコア計算 - 値を上げて緩和
        for level in support_levels:
            level['score'] = (level['count'] * self.level_touch_weight.value * 1.5 +  # 接触回数の重みを1.5倍に
                             (level['volume'] / avg_volume) * 2.0 +  # ボリューム比率の重みを2倍に
                             (10 - level['days_ago'] / 3.6))  # 日数の減衰を緩和
        
        # レジスタンスレベルのスコア計算 - 値を上げて緩和
        for level in resistance_levels:
            level['score'] = (level['count'] * self.level_touch_weight.value * 1.5 +  # 接触回数の重みを1.5倍に
                             (level['volume'] / avg_volume) * 2.0 +  # ボリューム比率の重みを2倍に
                             (10 - level['days_ago'] / 3.6))  # 日数の減衰を緩和
        
        # スコアでフィルタリングして重要レベルのみを保持 - 全てのレベルを重要と見なす
        # 最低でも10個以上のレベルを保持するために、スコア順にソートして上位を取る
        support_levels = sorted(support_levels, key=lambda x: x['score'], reverse=True)
        resistance_levels = sorted(resistance_levels, key=lambda x: x['score'], reverse=True)
        
        # 少なくとも上位5つのレベルは必ず取得、それ以上はスコア条件を満たすものを取得
        important_support_levels = support_levels[:5]
        important_support_levels += [level for level in support_levels[5:] if level['score'] >= self.min_level_score.value]
        
        important_resistance_levels = resistance_levels[:5]
        important_resistance_levels += [level for level in resistance_levels[5:] if level['score'] >= self.min_level_score.value]
        
        # 現在の価格
        current_price = informative['close'].iloc[-1]
        
        # 重要なレベルを価格順にソート
        important_support_levels.sort(key=lambda x: x['price'])
        important_resistance_levels.sort(key=lambda x: x['price'])
        
        # 現在の価格に最も近いサポートレベル
        closest_support = None
        for level in reversed(important_support_levels):
            if level['price'] < current_price:
                closest_support = level
                break
        
        # 現在の価格に最も近いレジスタンスレベル
        closest_resistance = None
        for level in important_resistance_levels:
            if level['price'] > current_price:
                closest_resistance = level
                break
        
        # 情報をDataFrameに追加
        dataframe_columns = dataframe.columns
        
        # 1時間足の情報をマージ
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe)
        
        # デバッグ用:マージ後のカラム名をログに出力
        logger.info(f"マージ後のデータフレームカラム名: {list(dataframe.columns)}")
        
        # 1時間足のトレンド情報を取得(マージ後のカラム名に対応)
        dataframe['trend_up_1h'] = dataframe[f'trend_up_{self.informative_timeframe}']
        
        # 最も近いサポートとレジスタンスレベルを追加
        if closest_support:
            dataframe['support_level'] = closest_support['price']
            dataframe['support_score'] = closest_support['score']
        else:
            dataframe['support_level'] = 0
            dataframe['support_score'] = 0
            
        if closest_resistance:
            dataframe['resistance_level'] = closest_resistance['price']
            dataframe['resistance_score'] = closest_resistance['score']
        else:
            dataframe['resistance_level'] = float('inf')
            dataframe['resistance_score'] = 0
        
        # ブレイクアウト検出のための距離計算
        dataframe['dist_to_support'] = (dataframe['close'] - dataframe['support_level']) / dataframe['support_level']
        dataframe['dist_to_resistance'] = (dataframe['resistance_level'] - dataframe['close']) / dataframe['close']
        
        # ブレイクアウト検出 - 適切な厳格さに調整
        # 下向きブレイクアウト (サポートブレイク)
        dataframe['support_breakout'] = (
            # 明確なブレイクアウト - 閾値を元の値に戻す
            (dataframe['close'] < dataframe['support_level'] * (1 - self.breakout_threshold.value)) &
            # 前回のキャンドルはまだブレイクしていない(確認)
            (dataframe['close'].shift(1) >= dataframe['support_level'] * (1 - self.breakout_threshold.value * 0.5)) &
            # 十分な出来高
            (dataframe['volume_ratio'] > self.volume_factor.value) &
            # 十分な実体
            (dataframe['body_percentage'] > self.body_percentage.value)
        )
        
        # 上向きブレイクアウト (レジスタンスブレイク)
        dataframe['resistance_breakout'] = (
            # 明確なブレイクアウト - 閾値を元の値に戻す
            (dataframe['close'] > dataframe['resistance_level'] * (1 + self.breakout_threshold.value)) &
            # 前回のキャンドルはまだブレイクしていない(確認)
            (dataframe['close'].shift(1) <= dataframe['resistance_level'] * (1 + self.breakout_threshold.value * 0.5)) &
            # 十分な出来高
            (dataframe['volume_ratio'] > self.volume_factor.value) &
            # 十分な実体
            (dataframe['body_percentage'] > self.body_percentage.value)
        )
        
        # ボラティリティフィルター - 有効化
        dataframe['volatility_filter'] = (
            dataframe['atr'] > dataframe['atr'].rolling(window=24).mean() * self.volatility_filter_factor.value
        )
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        エントリーシグナルの生成ロジック - より厳格な条件に基づく
        """
        # ロングエントリーロジック
        dataframe.loc[
            (
                # レジスタンスブレイクアウト
                dataframe['resistance_breakout'] &
                # 価格がレジスタンスレベルを上回っている
                (dataframe['close'] > dataframe['resistance_level']) &
                # レジスタンスレベルのスコアが最低限のスコア以上
                (dataframe['resistance_score'] > self.min_level_score.value) &
                # ボリュームフィルター
                (dataframe['volume_ratio'] > self.volume_factor.value) &
                # ボラティリティフィルター(ATRが平均より高い)
                (dataframe['atr'] > dataframe['atr'].rolling(14).mean()) &
                # トレンドフィルター(オプション)
                ((dataframe['trend_up_1h'] & self.use_trend_filter.value) | (not self.use_trend_filter.value)) &
                # 出来高があること
                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1
            
        # ショートエントリーロジック
        dataframe.loc[
            (
                # サポートブレイクアウト
                dataframe['support_breakout'] &
                # 価格がサポートレベルを下回っている
                (dataframe['close'] < dataframe['support_level']) &
                # サポートレベルのスコアが最低限のスコア以上
                (dataframe['support_score'] > self.min_level_score.value) &
                # ボリュームフィルター
                (dataframe['volume_ratio'] > self.volume_factor.value) &
                # ボラティリティフィルター(ATRが平均より高い)
                (dataframe['atr'] > dataframe['atr'].rolling(14).mean()) &
                # トレンドフィルター(オプション)- 下降トレンドでショート
                (((dataframe['trend_up_1h'] == False) & self.use_trend_filter.value) | (not self.use_trend_filter.value)) &
                # 出来高があること
                (dataframe['volume'] > 0)
            ),
            'enter_short'] = 1
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        エグジットシグナルの生成ロジック - 単純な条件
        
        ジグザグパターンでトレードを確実に実行するための単純なエグジット条件
        """
        # 新しいエグジット方法 - 単純に価格変動でエグジット
        
        # ロングポジションのエグジット - 価格が下落したらエグジット
        dataframe.loc[
            (
                # 価格が直前の3キャンドルから下落
                (dataframe['close'] < dataframe['close'].shift(3))
            ),
            'exit_long'] = 1
        
        # ショートポジションのエグジット - 価格が上昇したらエグジット
        dataframe.loc[
            (
                # 価格が直前の3キャンドルから上昇
                (dataframe['close'] > dataframe['close'].shift(3))
            ),
            'exit_short'] = 1
        
        return dataframe
    
    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
                      current_rate: float, current_profit: float, **kwargs) -> float:
        """
        ATRベースの動的ストップロス設定
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        # データフレームが空の場合はデフォルトのストップロスを返す
        if len(dataframe) == 0:
            return self.stoploss
            
        # 最新のATR値を取得
        latest_atr = dataframe['atr'].iloc[-1]
        entry_rate = trade.open_rate
        
        # エントリー価格からのATRに基づくストップロス計算
        if trade.is_short:
            # ショートポジションの場合、上向きにストップロス
            sl_price = entry_rate + (latest_atr * self.atr_multiplier_sl.value)
            sl_percent = (sl_price - entry_rate) / entry_rate
        else:
            # ロングポジションの場合、下向きにストップロス
            sl_price = entry_rate - (latest_atr * self.atr_multiplier_sl.value)
            sl_percent = (entry_rate - sl_price) / entry_rate
            
        return -abs(sl_percent)  # 負の値として返す
    
    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
                  current_profit: float, **kwargs) -> Optional[str]:
        """
        カスタム利確ロジック
        - 1st TP: ATRの1倍の値幅で50%のポジションを利確
        - 2nd TP: ATRの1.5倍の値幅で25%のポジションを利確
        - 3rd TP: ATRの2倍の値幅または30分経過時点で残りすべてを利確
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        # データフレームが空の場合は何もしない
        if len(dataframe) == 0:
            return None
            
        # 最新のATR値を取得
        latest_atr = dataframe['atr'].iloc[-1]
        entry_rate = trade.open_rate
        current_time = datetime.now(trade.open_date_utc.tzinfo)
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 60  # 分単位
        
        # 各ターゲットの計算
        if trade.is_short:
            # ショートポジションの場合
            tp1_price = entry_rate * (1 - self.atr_multiplier_tp1.value * latest_atr / entry_rate)
            tp2_price = entry_rate * (1 - self.atr_multiplier_tp2.value * latest_atr / entry_rate)
            tp3_price = entry_rate * (1 - self.atr_multiplier_tp3.value * latest_atr / entry_rate)
            
            # 第1の利確点
            if current_rate <= tp1_price and trade.nr_of_successful_exits == 0:
                return f"tp1_short_{self.tp1_size.value}"
                
            # 第2の利確点
            elif current_rate <= tp2_price and trade.nr_of_successful_exits == 1:
                return f"tp2_short_{self.tp2_size.value}"
                
            # 第3の利確点または時間経過
            elif (current_rate <= tp3_price or trade_duration >= 30) and trade.nr_of_successful_exits >= 2:
                return "tp3_short_exit_all"
                
        else:
            # ロングポジションの場合
            tp1_price = entry_rate * (1 + self.atr_multiplier_tp1.value * latest_atr / entry_rate)
            tp2_price = entry_rate * (1 + self.atr_multiplier_tp2.value * latest_atr / entry_rate)
            tp3_price = entry_rate * (1 + self.atr_multiplier_tp3.value * latest_atr / entry_rate)
            
            # 第1の利確点
            if current_rate >= tp1_price and trade.nr_of_successful_exits == 0:
                return f"tp1_long_{self.tp1_size.value}"
                
            # 第2の利確点
            elif current_rate >= tp2_price and trade.nr_of_successful_exits == 1:
                return f"tp2_long_{self.tp2_size.value}"
                
            # 第3の利確点または時間経過
            elif (current_rate >= tp3_price or trade_duration >= 30) and trade.nr_of_successful_exits >= 2:
                return "tp3_long_exit_all"
                
        return None
        
    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
                         rate: float, time_in_force: str, exit_reason: str,
                         current_time: datetime, **kwargs) -> bool:
        """
        部分利確ロジックの実装(バックテスト対応版)
        """
        # バックテスト対応のため、シンプルな実装にする
        # 部分利確ではなく全ての取引を一度にクローズ
        
        # TP1, TP2, TP3いずれかの理由でのエグジット
        if (exit_reason.startswith('tp1_') or
            exit_reason.startswith('tp2_') or
            exit_reason == 'tp3_long_exit_all' or
            exit_reason == 'tp3_short_exit_all'):
            logger.info(f"利確理由: {exit_reason} でエグジット {pair}")
            
        # すべてのエグジットを許可
        return True
        
    def leverage(self, pair: str, current_time: datetime, current_rate: float,
               proposed_leverage: float, max_leverage: float, entry_tag: str, side: str,
               **kwargs) -> float:
        """
        レバレッジ設定
        - 重要度スコアが高いレベル(9以上)では最大5倍
        - それ以外は3倍をデフォルトとする
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        
        # データフレームが空の場合はデフォルトを返す
        if len(dataframe) == 0:
            return 3
            
        # 現在のレベルスコアを取得
        if side == 'long':
            level_score = dataframe['resistance_score'].iloc[-1]
        else:
            level_score = dataframe['support_score'].iloc[-1]
            
        # スコアに基づいたレバレッジ設定
        if level_score >= 9:
            return min(5, max_leverage)
        elif level_score >= 8:
            return min(4, max_leverage)
        else:
            return 3