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:
14
.env.example
14
.env.example
@@ -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.
7511
results/dogeusdt/strategy_sweep_20260317_172011.json
Normal file
7511
results/dogeusdt/strategy_sweep_20260317_172011.json
Normal file
File diff suppressed because it is too large
Load Diff
7511
results/trxusdt/strategy_sweep_20260317_171133.json
Normal file
7511
results/trxusdt/strategy_sweep_20260317_171133.json
Normal file
File diff suppressed because it is too large
Load Diff
7579
results/xrpusdt/strategy_sweep_20260317_172135.json
Normal file
7579
results/xrpusdt/strategy_sweep_20260317_172135.json
Normal file
File diff suppressed because it is too large
Load Diff
18
src/bot.py
18
src/bot.py
@@ -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()
|
||||
|
||||
|
||||
@@ -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,
|
||||
))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user