feat: add per-symbol strategy params with sweep-optimized values

Support per-symbol strategy parameters (ATR_SL_MULT_XRPUSDT, etc.)
via env vars, falling back to global defaults. Sweep results:
- XRPUSDT: SL=1.5 TP=4.0 ADX=30 (PF 2.39, Sharpe 61.0)
- TRXUSDT: SL=1.0 TP=4.0 ADX=30 (PF 3.87, Sharpe 62.8)
- DOGEUSDT: SL=2.0 TP=2.0 ADX=30 (PF 1.80, Sharpe 44.1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
21in7
2026-03-17 17:28:14 +09:00
parent 106eaf182b
commit 55c20012a3
9 changed files with 22658 additions and 6 deletions

View File

@@ -8,11 +8,25 @@ DISCORD_WEBHOOK_URL=
ML_THRESHOLD=0.55
NO_ML_FILTER=true
MAX_SAME_DIRECTION=2
# Global defaults (fallback when no per-symbol override)
ATR_SL_MULT=2.0
ATR_TP_MULT=2.0
SIGNAL_THRESHOLD=3
ADX_THRESHOLD=25
VOL_MULTIPLIER=2.5
# Per-symbol strategy params (2026-03-17 sweep optimized)
ATR_SL_MULT_XRPUSDT=1.5
ATR_TP_MULT_XRPUSDT=4.0
ADX_THRESHOLD_XRPUSDT=30
ATR_SL_MULT_TRXUSDT=1.0
ATR_TP_MULT_TRXUSDT=4.0
ADX_THRESHOLD_TRXUSDT=30
ATR_SL_MULT_DOGEUSDT=2.0
ATR_TP_MULT_DOGEUSDT=2.0
ADX_THRESHOLD_DOGEUSDT=30
DASHBOARD_API_URL=http://10.1.10.24:8000
BINANCE_TESTNET_API_KEY=
BINANCE_TESTNET_API_SECRET=

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@ class TradingBot:
def __init__(self, config: Config, symbol: str = None, risk: RiskManager = None):
self.config = config
self.symbol = symbol or config.symbol
self.strategy = config.get_symbol_params(self.symbol)
self.exchange = BinanceFuturesClient(config, symbol=self.symbol)
self.notifier = DiscordNotifier(config.discord_webhook_url)
self.risk = risk or RiskManager(config)
@@ -141,9 +142,9 @@ class TradingBot:
df_with_indicators = ind.calculate_all()
raw_signal, signal_detail = ind.get_signal(
df_with_indicators,
signal_threshold=self.config.signal_threshold,
adx_threshold=self.config.adx_threshold,
volume_multiplier=self.config.volume_multiplier,
signal_threshold=self.strategy.signal_threshold,
adx_threshold=self.strategy.adx_threshold,
volume_multiplier=self.strategy.volume_multiplier,
)
current_price = df_with_indicators["close"].iloc[-1]
@@ -198,8 +199,8 @@ class TradingBot:
logger.info(f"[{self.symbol}] 포지션 크기: 잔고={per_symbol_balance:.2f}/{balance:.2f} USDT, 증거금비율={margin_ratio:.1%}, 수량={quantity}")
stop_loss, take_profit = Indicators(df).get_atr_stop(
df, signal, price,
atr_sl_mult=self.config.atr_sl_mult,
atr_tp_mult=self.config.atr_tp_mult,
atr_sl_mult=self.strategy.atr_sl_mult,
atr_tp_mult=self.strategy.atr_tp_mult,
)
notional = quantity * price
@@ -429,7 +430,12 @@ class TradingBot:
self._is_reentering = False
async def run(self):
logger.info(f"[{self.symbol}] 봇 시작, 레버리지 {self.config.leverage}x")
s = self.strategy
logger.info(
f"[{self.symbol}] 봇 시작, 레버리지 {self.config.leverage}x | "
f"SL={s.atr_sl_mult}x TP={s.atr_tp_mult}x Signal≥{s.signal_threshold} "
f"ADX≥{s.adx_threshold} Vol≥{s.volume_multiplier}x"
)
await self._recover_position()
await self._init_oi_history()

View File

@@ -5,6 +5,16 @@ from dotenv import load_dotenv
load_dotenv()
@dataclass
class SymbolStrategyParams:
"""Per-symbol strategy parameters (from sweep optimization)."""
atr_sl_mult: float = 2.0
atr_tp_mult: float = 2.0
signal_threshold: int = 3
adx_threshold: float = 25.0
volume_multiplier: float = 2.5
@dataclass
class Config:
api_key: str = ""
@@ -57,3 +67,24 @@ class Config:
corr_env = os.getenv("CORRELATION_SYMBOLS", "BTCUSDT,ETHUSDT")
self.correlation_symbols = [s.strip() for s in corr_env.split(",") if s.strip()]
# Per-symbol strategy params: {symbol: SymbolStrategyParams}
self._symbol_params: dict[str, SymbolStrategyParams] = {}
for sym in self.symbols:
self._symbol_params[sym] = SymbolStrategyParams(
atr_sl_mult=float(os.getenv(f"ATR_SL_MULT_{sym}", str(self.atr_sl_mult))),
atr_tp_mult=float(os.getenv(f"ATR_TP_MULT_{sym}", str(self.atr_tp_mult))),
signal_threshold=int(os.getenv(f"SIGNAL_THRESHOLD_{sym}", str(self.signal_threshold))),
adx_threshold=float(os.getenv(f"ADX_THRESHOLD_{sym}", str(self.adx_threshold))),
volume_multiplier=float(os.getenv(f"VOL_MULTIPLIER_{sym}", str(self.volume_multiplier))),
)
def get_symbol_params(self, symbol: str) -> SymbolStrategyParams:
"""Get strategy params for a symbol. Falls back to global defaults."""
return self._symbol_params.get(symbol, SymbolStrategyParams(
atr_sl_mult=self.atr_sl_mult,
atr_tp_mult=self.atr_tp_mult,
signal_threshold=self.signal_threshold,
adx_threshold=self.adx_threshold,
volume_multiplier=self.volume_multiplier,
))