--- name: trading_bot_architecture description: Key architecture decisions for the bifitnex-trading-2 bot — stop-loss, sell logic, order sizing, exposure limits type: project --- ## SELL 使用實際持倉量 SELL 訂單直接用 `sell_amount`(實際持倉量),不經 USDT→amount 反算,避免精度誤差導致殘餘持倉或 Bitfinex 500 錯誤。 **Why:** 價格變動導致反算數量與實際持倉有誤差,賣超會 500,賣不完留殘餘。 **How to apply:** `risk_manager.py` SELL 時設定 `signal["sell_amount"]`,`trader.py` SELL 優先用此值並 truncate 到 8 位小數。 ## 止損單依賴交易所即時資料 每次 cron 週期從交易所拉 active orders,比對持倉是否有正確的 EXCHANGE STOP 單,缺少或不匹配就自動補上。不依賴本地 `stop_order_id` 記憶。 **Why:** 本地記憶容易因 BUY 加倉、手動刪單等情況失準。 **How to apply:** `main.py` 2b 區塊用 `fetch_active_orders()` + wallet balances 做 backfill。 ## 止損/停利閾值 - 止損: -3% (`STOP_LOSS_PCT = 0.03`) - 停利: +5% (`TAKE_PROFIT_PCT = 0.05`) ## 無總曝險上限 已移除 `check_total_exposure` 檢查,可用餘額可以扣到 0。BUY 只受:(1) 可用 USDT 餘額 (2) 單幣 30% 上限 限制。 **Why:** 未實現利潤推高持倉市值超過帳戶餘額時,總曝險限制會誤擋正常 BUY。 ## 下單基準 = USDT 餘額 + 持倉總成本 `validate_trade()` 中 `total_balance = available_usdt + Σ(entry_price × amount)`,不用交易所錢包餘額。 **Why:** 交易所 `total_balance_usdt` 只反映 USDT 錢包,不含其他幣種持倉價值,導致下單量過小被 MIN_ORDER 擋掉。 **How to apply:** `risk_manager.py` line 111-116。Position limit 也用同一個 total_balance(不再呼叫 `check_position_limit`),避免判斷不一致。 ## 交易後刷新錢包 step 10 交易完成後,若有任何成交(executed 或 tp_closed),重新 `fetch_account_status()` + `sync_with_exchange()` 確保下一筆交易用最新餘額。 ## Slack 報告格式 `_build_portfolio_one_liner` 顯示:總值、總收益%(= 總值/總成本-1)、可用 USDT、持倉筆數。低於 MIN_ORDER_AMOUNT 的殘留倉位不計入。