119 lines
3.1 KiB
Go
119 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
QMTBaseURL string
|
|
QMTToken string
|
|
AccountType string
|
|
AccountID string
|
|
HostKey string
|
|
APIHost string
|
|
DataDir string
|
|
HTTPTimeout time.Duration
|
|
OrderTimeout time.Duration
|
|
LoopInterval time.Duration
|
|
OpenMoney float64
|
|
MinCashRatio float64
|
|
LossTriggerPct float64
|
|
GridStepPct float64
|
|
MinProfitPct float64
|
|
AdoptExisting bool
|
|
ReadyCacheStart int
|
|
WatchTimeout time.Duration
|
|
ReboundThreshold float64
|
|
}
|
|
|
|
func loadConfig() Config {
|
|
cfg := Config{
|
|
QMTBaseURL: env("QMT_BASE_URL", "http://127.0.0.1:10086"),
|
|
QMTToken: env("QMT_TOKEN", "QMTbyYanweidong"),
|
|
AccountType: env("QMT_ACCOUNT", "stock"),
|
|
AccountID: env("ACCOUNT_ID", ""),
|
|
HostKey: env("HOST_KEY", ""),
|
|
APIHost: strings.TrimRight(env("API_HOST", "http://139.224.247.176:13499"), "/"),
|
|
DataDir: env("DATA_DIR", "D:/qmt_strategy_state"),
|
|
HTTPTimeout: durationEnv("HTTP_TIMEOUT_SEC", 5) * time.Second,
|
|
OrderTimeout: durationEnv("ORDER_TIMEOUT_SEC", 60) * time.Second,
|
|
LoopInterval: durationEnv("LOOP_INTERVAL_SEC", 30) * time.Second,
|
|
OpenMoney: floatEnv("OPEN_MONEY", 5000),
|
|
MinCashRatio: floatEnv("MIN_CASH_RATIO", 0.1),
|
|
LossTriggerPct: floatEnv("LOSS_TRIGGER_PCT", -30),
|
|
GridStepPct: floatEnv("GRID_STEP_PCT", 1),
|
|
MinProfitPct: floatEnv("MIN_PROFIT_PCT", 2),
|
|
AdoptExisting: boolEnv("ADOPT_EXISTING_POSITIONS", true),
|
|
ReadyCacheStart: intEnv("READY_CACHE_START", 925),
|
|
WatchTimeout: durationEnv("WATCH_TIMEOUT_SEC", 300) * time.Second,
|
|
ReboundThreshold: floatEnv("REBOUND_THRESHOLD", 0.61),
|
|
}
|
|
if strings.TrimSpace(cfg.AccountID) == "" {
|
|
logf("ERROR", "ACCOUNT_ID 为空")
|
|
os.Exit(1)
|
|
}
|
|
if strings.TrimSpace(cfg.HostKey) == "" {
|
|
logf("ERROR", "HOST_KEY 为空")
|
|
os.Exit(1)
|
|
}
|
|
if cfg.MinCashRatio < 0 || cfg.MinCashRatio >= 1 {
|
|
logf("ERROR", "MIN_CASH_RATIO 必须在 [0, 1)")
|
|
os.Exit(1)
|
|
}
|
|
if cfg.OpenMoney <= 0 {
|
|
logf("ERROR", "OPEN_MONEY 必须大于 0")
|
|
os.Exit(1)
|
|
}
|
|
if err := os.MkdirAll(cfg.DataDir, 0o755); err != nil {
|
|
logf("ERROR", "创建 DATA_DIR 失败: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
func env(key, fallback string) string {
|
|
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func intEnv(key string, fallback int) int {
|
|
v := strings.TrimSpace(os.Getenv(key))
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return n
|
|
}
|
|
|
|
func floatEnv(key string, fallback float64) float64 {
|
|
v := strings.TrimSpace(os.Getenv(key))
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
f, err := strconv.ParseFloat(v, 64)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return f
|
|
}
|
|
|
|
func durationEnv(key string, fallbackSec int) time.Duration {
|
|
return time.Duration(intEnv(key, fallbackSec))
|
|
}
|
|
|
|
func boolEnv(key string, fallback bool) bool {
|
|
v := strings.ToLower(strings.TrimSpace(os.Getenv(key)))
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
return v == "1" || v == "true" || v == "yes"
|
|
}
|