feat: 所有参数移至config.json, 重构退出逻辑, 清理遗留接口
- 将所有硬编码参数迁移到 config.json (手续费率、止盈/止损阈值、 超时、腿间隔、加仓步进等) - 退出条件: 净利 >= take_profit_pct 止盈, 价差 <= 0 平仓 - 删除 Binance/dYdX 遗留代码 - 更新 README 文档 - Dashboard: 双交易所价格表、黑名单UI、按币名排序持仓 - Bitget WS: 文本ping保活 - 数据库: 重置, 无历史仓位
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds all system configuration.
|
||||
@@ -18,13 +19,37 @@ type Config struct {
|
||||
// Automated trading
|
||||
TradeEnabled bool
|
||||
TradeThreshold float64 // minimum profit % to execute trade
|
||||
TradeAmountUSD float64 // amount per trade in USDT
|
||||
TradeAmountUSD float64 // amount per leg in USDT
|
||||
TradeCooldownMs int // ms between trades of same coin
|
||||
MaxPositions int // max concurrent open positions (0 = unlimited)
|
||||
|
||||
// Capital
|
||||
InitialCapital float64 // starting capital in USD (for PnL % calculation)
|
||||
|
||||
// Blacklist — stale spread observation
|
||||
BlacklistDuration time.Duration // how long a coin stays blacklisted (0 = permanent)
|
||||
|
||||
// Test mode (no real API keys needed)
|
||||
TestMode bool
|
||||
TestMode bool
|
||||
MockSlippagePct float64 // simulated slippage per order (e.g. 0.01 = 0.01%)
|
||||
|
||||
// Exchange fee rates (% per order)
|
||||
TakerFeeBitget float64
|
||||
TakerFeeHyperLiquid float64
|
||||
|
||||
// Exit/risk parameters
|
||||
TakeProfitPct float64 // net profit % threshold for take-profit
|
||||
SpreadReverseExitPct float64 // spread reversal % threshold for exit
|
||||
PositionTimeout time.Duration // max position hold time before auto-close
|
||||
LegDelay time.Duration // delay between placing long and short legs
|
||||
|
||||
// Scale-in parameters
|
||||
ScaleStepPct float64 // spread widening % trigger for each scale level
|
||||
ScaleCooldown time.Duration // minimum time between scale-ins
|
||||
|
||||
// Entry sanity check: reject if price moved beyond this % in the wrong direction
|
||||
ReversalTolerancePct float64
|
||||
|
||||
// Bitget API
|
||||
BitgetAPIKey string
|
||||
BitgetAPISecret string
|
||||
@@ -46,6 +71,22 @@ type jsonConfig struct {
|
||||
TradeCooldownMs int `json:"trade_cooldown_ms"`
|
||||
AlertCooldownSec int `json:"alert_cooldown_sec"`
|
||||
MockSlippagePct float64 `json:"mock_slippage_pct"`
|
||||
MaxPositions int `json:"max_positions"`
|
||||
BlacklistDuration int `json:"blacklist_duration_sec"`
|
||||
InitialCapital float64 `json:"initial_capital"`
|
||||
|
||||
// New: exchange fees
|
||||
TakerFeeBitget float64 `json:"taker_fee_bitget"`
|
||||
TakerFeeHyperLiquid float64 `json:"taker_fee_hyperliquid"`
|
||||
|
||||
// New: exit/risk parameters
|
||||
TakeProfitPct float64 `json:"take_profit_pct"`
|
||||
SpreadReverseExitPct float64 `json:"spread_reverse_exit_pct"`
|
||||
PositionTimeoutSec int `json:"position_timeout_sec"`
|
||||
LegDelayMs int `json:"leg_delay_ms"`
|
||||
ReversalTolerancePct float64 `json:"reversal_tolerance_pct"`
|
||||
ScaleStepPct float64 `json:"scale_step_pct"`
|
||||
ScaleCooldownSec int `json:"scale_cooldown_sec"`
|
||||
}
|
||||
|
||||
func LoadConfig() *Config {
|
||||
@@ -89,10 +130,30 @@ func LoadConfig() *Config {
|
||||
TradeThreshold: getFloat("TRADE_THRESHOLD", jsonCfg.TradeThreshold),
|
||||
TradeAmountUSD: getFloat("TRADE_AMOUNT_USD", jsonCfg.TradeAmountUSD),
|
||||
TradeCooldownMs: int(getFloat("TRADE_COOLDOWN_MS", float64(jsonCfg.TradeCooldownMs))),
|
||||
MaxPositions: int(getFloat("MAX_POSITIONS", float64(jsonCfg.MaxPositions))),
|
||||
|
||||
InitialCapital: getFloat("INITIAL_CAPITAL", jsonCfg.InitialCapital),
|
||||
|
||||
BlacklistDuration: time.Duration(getFloat("BLACKLIST_DURATION_SEC", float64(jsonCfg.BlacklistDuration))) * time.Second,
|
||||
|
||||
TestMode: getBool("TEST_MODE", jsonCfg.TestMode),
|
||||
MockSlippagePct: getFloat("MOCK_SLIPPAGE_PCT", jsonCfg.MockSlippagePct),
|
||||
|
||||
// Exchange fee rates
|
||||
TakerFeeBitget: getFloat("TAKER_FEE_BITGET", jsonCfg.TakerFeeBitget),
|
||||
TakerFeeHyperLiquid: getFloat("TAKER_FEE_HYPERLIQUID", jsonCfg.TakerFeeHyperLiquid),
|
||||
|
||||
// Exit/risk parameters
|
||||
TakeProfitPct: getFloat("TAKE_PROFIT_PCT", jsonCfg.TakeProfitPct),
|
||||
SpreadReverseExitPct: getFloat("SPREAD_REVERSE_EXIT_PCT", jsonCfg.SpreadReverseExitPct),
|
||||
PositionTimeout: time.Duration(getFloat("POSITION_TIMEOUT_SEC", float64(jsonCfg.PositionTimeoutSec))) * time.Second,
|
||||
LegDelay: time.Duration(getFloat("LEG_DELAY_MS", float64(jsonCfg.LegDelayMs))) * time.Millisecond,
|
||||
ReversalTolerancePct: getFloat("REVERSAL_TOLERANCE_PCT", jsonCfg.ReversalTolerancePct),
|
||||
|
||||
// Scale-in parameters
|
||||
ScaleStepPct: getFloat("SCALE_STEP_PCT", jsonCfg.ScaleStepPct),
|
||||
ScaleCooldown: time.Duration(getFloat("SCALE_COOLDOWN_SEC", float64(jsonCfg.ScaleCooldownSec))) * time.Second,
|
||||
|
||||
BitgetAPIKey: getEnv("BITGET_API_KEY", ""),
|
||||
BitgetAPISecret: getEnv("BITGET_API_SECRET", ""),
|
||||
BitgetPassphrase: getEnv("BITGET_PASSPHRASE", ""),
|
||||
@@ -111,6 +172,24 @@ func loadJSONConfig() jsonConfig {
|
||||
TradeCooldownMs: 30000,
|
||||
AlertCooldownSec: 300,
|
||||
MockSlippagePct: 0.005,
|
||||
MaxPositions: 5, // default max 5 concurrent positions
|
||||
BlacklistDuration: 3600, // default 1 hour blacklist observation
|
||||
InitialCapital: 1000, // default $1000 starting capital
|
||||
|
||||
// Exchange fee rates
|
||||
TakerFeeBitget: 0.060, // 0.060%
|
||||
TakerFeeHyperLiquid: 0.045, // 0.045%
|
||||
|
||||
// Exit/risk parameters
|
||||
TakeProfitPct: 0.20, // 0.20% net profit take-profit
|
||||
SpreadReverseExitPct: 0.02, // 0.02% spread reversal exit
|
||||
PositionTimeoutSec: 1800, // 30 minutes
|
||||
LegDelayMs: 300, // 300ms between legs
|
||||
ReversalTolerancePct: 0.1, // 0.1% tolerance for entry sanity check
|
||||
|
||||
// Scale-in parameters
|
||||
ScaleStepPct: 0.10, // 0.10% spread widening per scale level
|
||||
ScaleCooldownSec: 5, // 5 seconds between scales
|
||||
}
|
||||
|
||||
data, err := os.ReadFile("config.json")
|
||||
@@ -145,6 +224,44 @@ func loadJSONConfig() jsonConfig {
|
||||
if cfg.MockSlippagePct != 0 {
|
||||
def.MockSlippagePct = cfg.MockSlippagePct
|
||||
}
|
||||
if cfg.MaxPositions != 0 {
|
||||
def.MaxPositions = cfg.MaxPositions
|
||||
}
|
||||
if cfg.BlacklistDuration != 0 {
|
||||
def.BlacklistDuration = cfg.BlacklistDuration
|
||||
}
|
||||
if cfg.InitialCapital != 0 {
|
||||
def.InitialCapital = cfg.InitialCapital
|
||||
}
|
||||
|
||||
// New config fields
|
||||
if cfg.TakerFeeBitget != 0 {
|
||||
def.TakerFeeBitget = cfg.TakerFeeBitget
|
||||
}
|
||||
if cfg.TakerFeeHyperLiquid != 0 {
|
||||
def.TakerFeeHyperLiquid = cfg.TakerFeeHyperLiquid
|
||||
}
|
||||
if cfg.TakeProfitPct != 0 {
|
||||
def.TakeProfitPct = cfg.TakeProfitPct
|
||||
}
|
||||
if cfg.SpreadReverseExitPct != 0 {
|
||||
def.SpreadReverseExitPct = cfg.SpreadReverseExitPct
|
||||
}
|
||||
if cfg.PositionTimeoutSec != 0 {
|
||||
def.PositionTimeoutSec = cfg.PositionTimeoutSec
|
||||
}
|
||||
if cfg.LegDelayMs != 0 {
|
||||
def.LegDelayMs = cfg.LegDelayMs
|
||||
}
|
||||
if cfg.ReversalTolerancePct != 0 {
|
||||
def.ReversalTolerancePct = cfg.ReversalTolerancePct
|
||||
}
|
||||
if cfg.ScaleStepPct != 0 {
|
||||
def.ScaleStepPct = cfg.ScaleStepPct
|
||||
}
|
||||
if cfg.ScaleCooldownSec != 0 {
|
||||
def.ScaleCooldownSec = cfg.ScaleCooldownSec
|
||||
}
|
||||
|
||||
// Boolean fields: zero default is false, so use OR logic
|
||||
// When JSON has true → true || false = true (override)
|
||||
|
||||
Reference in New Issue
Block a user