refactor: 移除 gate_bot,统一为三所架构并更新文档

删除 crypto_monitor_gate_bot 目录,中控与子代理改为 binance/okx/gate 三账户;
文档与 UI 文案「四所」改为「三所」;新增清库前一次性配置备份脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-04 22:00:08 +08:00
parent be51eee73f
commit 9f67de3677
138 changed files with 26395 additions and 40057 deletions
+212 -213
View File
@@ -1,213 +1,212 @@
#!/usr/bin/env python3
"""
将每日自动划转相关项写入所实例 .env(已有值保留,缺失则追加;可选强制改金额/开关)。
用法(仓库根目录):
python scripts/sync_four_exchange_transfer_env.py
python scripts/sync_four_exchange_transfer_env.py --dry-run
python scripts/sync_four_exchange_transfer_env.py --set-amount 50
python scripts/sync_four_exchange_transfer_env.py --enable-auto-transfer
不修改 API 密钥与其它自定义项;若 .env 不存在则跳过(请先从 .env.example 复制)。
完整说明见 docs/env-sync-scripts.md
"""
from __future__ import annotations
import argparse
import os
import re
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTANCES = (
"crypto_monitor_binance",
"crypto_monitor_okx",
"crypto_monitor_gate",
"crypto_monitor_gate_bot",
)
COMMENT_BLOCK = (
"# 自动划转:北京时间 AUTO_TRANSFER_BJ_HOUR 点将 swap 调整至 AUTO_TRANSFER_AMOUNT"
"不足 funding→swap、超出 swap→funding;持仓中不划转"
)
DEFAULTS = {
"AUTO_TRANSFER_ENABLED": "false",
"AUTO_TRANSFER_FROM": "funding",
"AUTO_TRANSFER_TO": "swap",
"TRANSFER_CCY": "USDT",
"AUTO_TRANSFER_BJ_HOUR": "8",
}
DEFAULT_AMOUNT = "50"
BINANCE_ONLY = {
"BINANCE_FUNDING_INCLUDE_SPOT": "false",
}
def _parse_env(path: str) -> list[str]:
if not os.path.isfile(path):
return []
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return f.read().replace("\r\n", "\n").replace("\r", "\n").splitlines()
def _env_get(lines: list[str], key: str) -> str | None:
pat = re.compile(r"^\s*" + re.escape(key) + r"\s*=\s*(.*)\s*$")
for line in lines:
m = pat.match(line)
if m:
return m.group(1).strip().strip('"').strip("'")
return None
def _upsert(lines: list[str], key: str, value: str) -> list[str]:
pat = re.compile(r"^\s*" + re.escape(key) + r"\s*=")
out = []
replaced = False
for line in lines:
if pat.match(line):
if not replaced:
out.append(f"{key}={value}")
replaced = True
continue
out.append(line)
if not replaced:
if out and out[-1].strip():
out.append("")
out.append(f"{key}={value}")
return out
def _insert_before(lines: list[str], anchor_key: str, insert: list[str]) -> list[str]:
pat = re.compile(r"^\s*" + re.escape(anchor_key) + r"\s*=")
for i, line in enumerate(lines):
if pat.match(line):
return lines[:i] + insert + lines[i:]
if lines and lines[-1].strip():
return lines + [""] + insert
return lines + insert
def _resolve_default_amount(lines: list[str]) -> str:
amount = _env_get(lines, "AUTO_TRANSFER_AMOUNT")
if amount is not None:
return amount
daily = _env_get(lines, "DAILY_START_CAPITAL")
if daily is not None:
return daily
return DEFAULT_AMOUNT
def _ensure_key(
lines: list[str],
key: str,
value: str,
*,
force: bool,
) -> list[str]:
if force or _env_get(lines, key) is None:
return _upsert(lines, key, value)
return lines
def _ensure_transfer_block(
lines: list[str],
extra: dict[str, str],
*,
force_amount: str | None,
force_enabled: str | None,
) -> list[str]:
amount = force_amount if force_amount is not None else _resolve_default_amount(lines)
had_amount = _env_get(lines, "AUTO_TRANSFER_AMOUNT") is not None
if not had_amount and COMMENT_BLOCK not in lines:
lines = _insert_before(
lines,
"AUTO_TRANSFER_ENABLED",
[COMMENT_BLOCK],
)
if _env_get(lines, "AUTO_TRANSFER_ENABLED") is None:
lines = _insert_before(
lines,
"BALANCE_REFRESH_SECONDS",
[COMMENT_BLOCK],
)
lines = _ensure_key(
lines,
"AUTO_TRANSFER_AMOUNT",
amount,
force=force_amount is not None,
)
for k, v in DEFAULTS.items():
if k == "AUTO_TRANSFER_ENABLED" and force_enabled is not None:
lines = _upsert(lines, k, force_enabled)
else:
lines = _ensure_key(lines, k, v, force=False)
for k, v in extra.items():
lines = _ensure_key(lines, k, v, force=False)
return lines
def sync_one(
dir_name: str,
dry_run: bool,
*,
set_amount: str | None,
enable_auto: bool | None,
) -> str:
env_path = os.path.join(REPO, dir_name, ".env")
if not os.path.isfile(env_path):
return f"SKIP {dir_name}: 无 .env(请 cp .env.example .env"
old_lines = _parse_env(env_path)
extra = dict(BINANCE_ONLY) if dir_name == "crypto_monitor_binance" else {}
force_enabled = "true" if enable_auto is True else None
new_lines = _ensure_transfer_block(
old_lines,
extra,
force_amount=set_amount,
force_enabled=force_enabled,
)
enabled = _env_get(new_lines, "AUTO_TRANSFER_ENABLED") or DEFAULTS["AUTO_TRANSFER_ENABLED"]
amt = _env_get(new_lines, "AUTO_TRANSFER_AMOUNT") or DEFAULT_AMOUNT
if new_lines == old_lines:
return f"OK {dir_name}: ENABLED={enabled} AMOUNT={amt}"
if dry_run:
return f"DRY {dir_name}: 将更新 ENABLED={enabled} AMOUNT={amt}"
with open(env_path, "w", encoding="utf-8", newline="\n") as f:
f.write("\n".join(new_lines))
if new_lines and new_lines[-1].strip():
f.write("\n")
return f"DONE {dir_name}: ENABLED={enabled} AMOUNT={amt}"
def main():
ap = argparse.ArgumentParser(description="四所 .env 自动划转项同步")
ap.add_argument("--dry-run", action="store_true")
ap.add_argument(
"--set-amount",
metavar="U",
help=f"强制四所 AUTO_TRANSFER_AMOUNT(缺省补全默认 {DEFAULT_AMOUNT}",
)
ap.add_argument(
"--enable-auto-transfer",
action="store_true",
help="强制四所 AUTO_TRANSFER_ENABLED=true",
)
args = ap.parse_args()
for name in INSTANCES:
print(
sync_one(
name,
args.dry_run,
set_amount=args.set_amount,
enable_auto=True if args.enable_auto_transfer else None,
)
)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
将每日自动划转相关项写入所实例 .env(已有值保留,缺失则追加;可选强制改金额/开关)。
用法(仓库根目录):
python scripts/sync_four_exchange_transfer_env.py
python scripts/sync_four_exchange_transfer_env.py --dry-run
python scripts/sync_four_exchange_transfer_env.py --set-amount 50
python scripts/sync_four_exchange_transfer_env.py --enable-auto-transfer
不修改 API 密钥与其它自定义项;若 .env 不存在则跳过(请先从 .env.example 复制)。
完整说明见 docs/env-sync-scripts.md
"""
from __future__ import annotations
import argparse
import os
import re
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTANCES = (
"crypto_monitor_binance",
"crypto_monitor_okx",
"crypto_monitor_gate",
)
COMMENT_BLOCK = (
"# 自动划转:北京时间 AUTO_TRANSFER_BJ_HOUR 点将 swap 调整至 AUTO_TRANSFER_AMOUNT"
"不足 funding→swap、超出 swap→funding;持仓中不划转"
)
DEFAULTS = {
"AUTO_TRANSFER_ENABLED": "false",
"AUTO_TRANSFER_FROM": "funding",
"AUTO_TRANSFER_TO": "swap",
"TRANSFER_CCY": "USDT",
"AUTO_TRANSFER_BJ_HOUR": "8",
}
DEFAULT_AMOUNT = "50"
BINANCE_ONLY = {
"BINANCE_FUNDING_INCLUDE_SPOT": "false",
}
def _parse_env(path: str) -> list[str]:
if not os.path.isfile(path):
return []
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return f.read().replace("\r\n", "\n").replace("\r", "\n").splitlines()
def _env_get(lines: list[str], key: str) -> str | None:
pat = re.compile(r"^\s*" + re.escape(key) + r"\s*=\s*(.*)\s*$")
for line in lines:
m = pat.match(line)
if m:
return m.group(1).strip().strip('"').strip("'")
return None
def _upsert(lines: list[str], key: str, value: str) -> list[str]:
pat = re.compile(r"^\s*" + re.escape(key) + r"\s*=")
out = []
replaced = False
for line in lines:
if pat.match(line):
if not replaced:
out.append(f"{key}={value}")
replaced = True
continue
out.append(line)
if not replaced:
if out and out[-1].strip():
out.append("")
out.append(f"{key}={value}")
return out
def _insert_before(lines: list[str], anchor_key: str, insert: list[str]) -> list[str]:
pat = re.compile(r"^\s*" + re.escape(anchor_key) + r"\s*=")
for i, line in enumerate(lines):
if pat.match(line):
return lines[:i] + insert + lines[i:]
if lines and lines[-1].strip():
return lines + [""] + insert
return lines + insert
def _resolve_default_amount(lines: list[str]) -> str:
amount = _env_get(lines, "AUTO_TRANSFER_AMOUNT")
if amount is not None:
return amount
daily = _env_get(lines, "DAILY_START_CAPITAL")
if daily is not None:
return daily
return DEFAULT_AMOUNT
def _ensure_key(
lines: list[str],
key: str,
value: str,
*,
force: bool,
) -> list[str]:
if force or _env_get(lines, key) is None:
return _upsert(lines, key, value)
return lines
def _ensure_transfer_block(
lines: list[str],
extra: dict[str, str],
*,
force_amount: str | None,
force_enabled: str | None,
) -> list[str]:
amount = force_amount if force_amount is not None else _resolve_default_amount(lines)
had_amount = _env_get(lines, "AUTO_TRANSFER_AMOUNT") is not None
if not had_amount and COMMENT_BLOCK not in lines:
lines = _insert_before(
lines,
"AUTO_TRANSFER_ENABLED",
[COMMENT_BLOCK],
)
if _env_get(lines, "AUTO_TRANSFER_ENABLED") is None:
lines = _insert_before(
lines,
"BALANCE_REFRESH_SECONDS",
[COMMENT_BLOCK],
)
lines = _ensure_key(
lines,
"AUTO_TRANSFER_AMOUNT",
amount,
force=force_amount is not None,
)
for k, v in DEFAULTS.items():
if k == "AUTO_TRANSFER_ENABLED" and force_enabled is not None:
lines = _upsert(lines, k, force_enabled)
else:
lines = _ensure_key(lines, k, v, force=False)
for k, v in extra.items():
lines = _ensure_key(lines, k, v, force=False)
return lines
def sync_one(
dir_name: str,
dry_run: bool,
*,
set_amount: str | None,
enable_auto: bool | None,
) -> str:
env_path = os.path.join(REPO, dir_name, ".env")
if not os.path.isfile(env_path):
return f"SKIP {dir_name}: 无 .env(请 cp .env.example .env"
old_lines = _parse_env(env_path)
extra = dict(BINANCE_ONLY) if dir_name == "crypto_monitor_binance" else {}
force_enabled = "true" if enable_auto is True else None
new_lines = _ensure_transfer_block(
old_lines,
extra,
force_amount=set_amount,
force_enabled=force_enabled,
)
enabled = _env_get(new_lines, "AUTO_TRANSFER_ENABLED") or DEFAULTS["AUTO_TRANSFER_ENABLED"]
amt = _env_get(new_lines, "AUTO_TRANSFER_AMOUNT") or DEFAULT_AMOUNT
if new_lines == old_lines:
return f"OK {dir_name}: ENABLED={enabled} AMOUNT={amt}"
if dry_run:
return f"DRY {dir_name}: 将更新 ENABLED={enabled} AMOUNT={amt}"
with open(env_path, "w", encoding="utf-8", newline="\n") as f:
f.write("\n".join(new_lines))
if new_lines and new_lines[-1].strip():
f.write("\n")
return f"DONE {dir_name}: ENABLED={enabled} AMOUNT={amt}"
def main():
ap = argparse.ArgumentParser(description="三所 .env 自动划转项同步")
ap.add_argument("--dry-run", action="store_true")
ap.add_argument(
"--set-amount",
metavar="U",
help=f"强制三所 AUTO_TRANSFER_AMOUNT(缺省补全默认 {DEFAULT_AMOUNT}",
)
ap.add_argument(
"--enable-auto-transfer",
action="store_true",
help="强制三所 AUTO_TRANSFER_ENABLED=true",
)
args = ap.parse_args()
for name in INSTANCES:
print(
sync_one(
name,
args.dry_run,
set_amount=args.set_amount,
enable_auto=True if args.enable_auto_transfer else None,
)
)
if __name__ == "__main__":
main()