交易所 API 改为仅服务器配置:前端去掉密钥、新机示例为空,并防止坏钥反复请求触发 Gate 封 IP。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Vendored
+3
-10
@@ -19,10 +19,7 @@ from lib.env.env_schema import (
|
||||
# 各所「交易所与实盘」字段(顺序即页面顺序)
|
||||
_EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
"okx": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
("OKX_API_KEY", "API Key", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_SECRET", "API Secret", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_PASSPHRASE", "API Passphrase", "OKX 必填"),
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程;API Key 请在服务器实例目录 .env 配置后重启"),
|
||||
("OKX_TD_MODE", "保证金模式", ""),
|
||||
("OKX_POS_MODE", "持仓模式", ""),
|
||||
("OKX_POSITION_INST_TYPE", "仓位查询类型", "如 SWAP"),
|
||||
@@ -34,17 +31,13 @@ _EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
),
|
||||
],
|
||||
"binance": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
("BINANCE_API_KEY", "API Key", "永续子账户"),
|
||||
("BINANCE_API_SECRET", "API Secret", "永续子账户"),
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程;API Key 请在服务器实例目录 .env 配置后重启"),
|
||||
("BINANCE_MARGIN_MODE", "保证金模式", ""),
|
||||
("BINANCE_POSITION_MODE", "持仓模式", ""),
|
||||
("BINANCE_ACCOUNT_LABEL", "账户备注", "企业微信推送中显示"),
|
||||
],
|
||||
"gate": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
("GATE_API_KEY", "API Key", "永续子账户"),
|
||||
("GATE_API_SECRET", "API Secret", "永续子账户"),
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程;API Key 请在服务器实例目录 .env 配置后重启"),
|
||||
("GATE_TD_MODE", "保证金模式", ""),
|
||||
("GATE_POS_MODE", "持仓模式", ""),
|
||||
("GATE_ACCOUNT_LABEL", "账户备注", "企业微信推送中显示"),
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
"""交易所 API 凭证规范化.
|
||||
|
||||
新机 .env 密钥应为空;示例占位符不得注入 ccxt,否则鉴权失败且
|
||||
(尤其 Gate)反复签名请求易触发 IP 封禁.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
_PLACEHOLDER_EXACT = frozenset(
|
||||
{
|
||||
"你的密钥",
|
||||
"your-api-key",
|
||||
"your_api_key",
|
||||
"your-api-secret",
|
||||
"your_api_secret",
|
||||
"todo",
|
||||
"xxx",
|
||||
"changeme",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def normalize_api_credential(value: Optional[str]) -> str:
|
||||
"""去空白;空/占位符一律视为未配置."""
|
||||
s = (value or "").strip().strip('"').strip("'")
|
||||
if not s:
|
||||
return ""
|
||||
upper = s.upper()
|
||||
if upper.startswith("REPLACE_WITH"):
|
||||
return ""
|
||||
if upper.startswith("CHANGE_TO"):
|
||||
return ""
|
||||
if s.lower() in _PLACEHOLDER_EXACT:
|
||||
return ""
|
||||
return s
|
||||
|
||||
|
||||
def credentials_configured(*parts: Optional[str]) -> bool:
|
||||
return all(bool(normalize_api_credential(p)) for p in parts)
|
||||
|
||||
|
||||
def is_exchange_auth_error(exc: BaseException) -> bool:
|
||||
"""鉴权/无效 Key 类错误(用于停掉后续签名请求,避免 Gate 封 IP)."""
|
||||
name = type(exc).__name__
|
||||
if name in ("AuthenticationError", "PermissionDenied", "InvalidNonce"):
|
||||
return True
|
||||
msg = str(exc)
|
||||
markers = (
|
||||
"Invalid Api-Key",
|
||||
"Invalid API-key",
|
||||
"Invalid API Key",
|
||||
"INVALID_KEY",
|
||||
"Invalid key",
|
||||
"API key is invalid",
|
||||
"api key not found",
|
||||
"Signature",
|
||||
"INVALID_SIGNATURE",
|
||||
"401",
|
||||
"-2008",
|
||||
"-2014",
|
||||
"-2015",
|
||||
"10003", # Gate: invalid key often
|
||||
"INVALID_KEY",
|
||||
)
|
||||
low = msg.lower()
|
||||
if "api" in low and ("key" in low or "sign" in low) and (
|
||||
"invalid" in low or "incorrect" in low or "not found" in low
|
||||
):
|
||||
return True
|
||||
return any(m in msg for m in markers)
|
||||
|
||||
|
||||
def strip_ccxt_credentials(exchange: Any) -> None:
|
||||
"""内存中清空密钥,后续只走公开接口,避免继续带坏钥签名."""
|
||||
try:
|
||||
exchange.apiKey = ""
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
exchange.secret = ""
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
exchange.password = ""
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def load_markets_public_fallback(exchange: Any, *, reload: bool = False) -> None:
|
||||
"""鉴权失败后去掉密钥再拉公开 markets(最多再请求一次)."""
|
||||
strip_ccxt_credentials(exchange)
|
||||
exchange.load_markets(reload=reload)
|
||||
Reference in New Issue
Block a user