交易所 API 改为仅服务器配置:前端去掉密钥、新机示例为空,并防止坏钥反复请求触发 Gate 封 IP。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -74,10 +74,9 @@ TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true
|
||||
|
||||
# 是否开启 Gate 实盘下单(false=只做本地流程,true=真实下单)
|
||||
LIVE_TRADING_ENABLED=true
|
||||
# Gate API Key(实盘)
|
||||
GATE_API_KEY=REPLACE_WITH_GATE_API_KEY
|
||||
# Gate API Secret(实盘)
|
||||
GATE_API_SECRET=REPLACE_WITH_GATE_API_SECRET
|
||||
# Gate API(仅服务器 .env 配置;新机保持为空,填真钥后重启;错误密钥反复请求易导致 Gate 封 IP)
|
||||
GATE_API_KEY=
|
||||
GATE_API_SECRET=
|
||||
# 保证金模式:cross=全仓,isolated=逐仓
|
||||
GATE_TD_MODE=cross
|
||||
# 持仓筛选:hedge=双向持仓下按多空腿过滤;其它值(如 single)不按腿过滤
|
||||
|
||||
@@ -35,6 +35,11 @@ import sys
|
||||
if _REPO_ROOT not in sys.path:
|
||||
sys.path.insert(0, _REPO_ROOT)
|
||||
from lib.paths import common_static_dir
|
||||
from lib.exchange.api_credentials_lib import (
|
||||
is_exchange_auth_error,
|
||||
load_markets_public_fallback,
|
||||
normalize_api_credential,
|
||||
)
|
||||
from lib.ai.ai_client import ai_generate, ai_review, ai_short_advice
|
||||
from lib.ai.ai_review_lib import (
|
||||
build_journal_ai_chart_path,
|
||||
@@ -346,8 +351,8 @@ def _resolve_app_tz():
|
||||
|
||||
APP_TZ = _resolve_app_tz()
|
||||
LIVE_TRADING_ENABLED = os.getenv("LIVE_TRADING_ENABLED", "false").lower() == "true"
|
||||
GATE_API_KEY = (os.getenv("GATE_API_KEY") or "").strip()
|
||||
GATE_API_SECRET = (os.getenv("GATE_API_SECRET") or "").strip()
|
||||
GATE_API_KEY = normalize_api_credential(os.getenv("GATE_API_KEY"))
|
||||
GATE_API_SECRET = normalize_api_credential(os.getenv("GATE_API_SECRET"))
|
||||
GATE_TD_MODE = (os.getenv("GATE_TD_MODE") or "cross").strip().lower()
|
||||
GATE_POS_MODE = (os.getenv("GATE_POS_MODE") or "hedge").strip().lower()
|
||||
# 永续仓位止盈止损触发单:POST /futures/{settle}/price_orders,order_type=close-*-position(全平)
|
||||
@@ -478,6 +483,8 @@ if GATE_API_KEY and GATE_API_SECRET:
|
||||
exchange.apiKey = GATE_API_KEY
|
||||
exchange.secret = GATE_API_SECRET
|
||||
MARKETS_LOADED = False
|
||||
# 鉴权失败后停止私有 API,避免坏钥反复签名;Gate 尤其易封 IP
|
||||
EXCHANGE_AUTH_DISABLED_MSG = ""
|
||||
ACCOUNT_BALANCE_CACHE = {
|
||||
"updated_at": 0.0,
|
||||
"funding_usdt": None,
|
||||
@@ -2547,6 +2554,8 @@ def enrich_order_item(raw_item, current_capital):
|
||||
|
||||
|
||||
def ensure_exchange_live_ready():
|
||||
if EXCHANGE_AUTH_DISABLED_MSG:
|
||||
return False, EXCHANGE_AUTH_DISABLED_MSG
|
||||
if not LIVE_TRADING_ENABLED:
|
||||
return False, "未开启实盘下单(LIVE_TRADING_ENABLED=false)"
|
||||
if not (GATE_API_KEY and GATE_API_SECRET):
|
||||
@@ -2581,9 +2590,23 @@ def order_row_key_signal_type(row):
|
||||
|
||||
def exchange_private_api_configured():
|
||||
"""仅表示已配置密钥;与是否允许下单(LIVE_TRADING_ENABLED)无关,用于只读拉仓等."""
|
||||
if EXCHANGE_AUTH_DISABLED_MSG:
|
||||
return False
|
||||
return bool(GATE_API_KEY and GATE_API_SECRET)
|
||||
|
||||
|
||||
def _disable_private_api_after_auth_error(exc):
|
||||
global EXCHANGE_AUTH_DISABLED_MSG, GATE_API_KEY, GATE_API_SECRET
|
||||
from lib.exchange.api_credentials_lib import strip_ccxt_credentials
|
||||
|
||||
strip_ccxt_credentials(exchange)
|
||||
GATE_API_KEY = ""
|
||||
GATE_API_SECRET = ""
|
||||
EXCHANGE_AUTH_DISABLED_MSG = (
|
||||
f"API 鉴权失败,已停止私有请求(请在服务器 .env 修正密钥后重启;勿反复试错以免 Gate 封 IP): {exc}"
|
||||
)
|
||||
|
||||
|
||||
def _extract_usdt_total(balance):
|
||||
usdt_info = balance.get("USDT", {}) if isinstance(balance, dict) else {}
|
||||
total_map = balance.get("total", {}) if isinstance(balance, dict) else {}
|
||||
@@ -2841,11 +2864,15 @@ def get_exchange_capitals(force=False):
|
||||
return ACCOUNT_BALANCE_CACHE["funding_usdt"], ACCOUNT_BALANCE_CACHE["trading_usdt"]
|
||||
try:
|
||||
ACCOUNT_BALANCE_CACHE["funding_usdt"] = _fetch_gate_funding_usdt()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
if is_exchange_auth_error(e):
|
||||
_disable_private_api_after_auth_error(e)
|
||||
ACCOUNT_BALANCE_CACHE["funding_usdt"] = None
|
||||
try:
|
||||
ACCOUNT_BALANCE_CACHE["trading_usdt"] = _fetch_usdt_by_types(["swap", "spot"])
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
if is_exchange_auth_error(e):
|
||||
_disable_private_api_after_auth_error(e)
|
||||
# 勿保留上一次成功请求的旧值:鉴权失败时否则会误以为「合约余额仍能读」
|
||||
ACCOUNT_BALANCE_CACHE["trading_usdt"] = None
|
||||
ACCOUNT_BALANCE_CACHE["updated_at"] = now_ts
|
||||
@@ -3345,7 +3372,15 @@ def calc_trend_manual_breakeven_stop(direction, entry_price, offset_pct=None):
|
||||
def ensure_markets_loaded(force=False):
|
||||
global MARKETS_LOADED
|
||||
if force or not MARKETS_LOADED:
|
||||
exchange.load_markets(reload=force)
|
||||
try:
|
||||
exchange.load_markets(reload=force)
|
||||
except Exception as e:
|
||||
# Gate 对无效签名/坏钥敏感,失败后立即改公开 markets,勿反复带钥请求
|
||||
if is_exchange_auth_error(e) and (getattr(exchange, "apiKey", None) or getattr(exchange, "secret", None)):
|
||||
_disable_private_api_after_auth_error(e)
|
||||
load_markets_public_fallback(exchange, reload=True)
|
||||
else:
|
||||
raise
|
||||
MARKETS_LOADED = True
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user