Files
crypto_monitor/scripts/sync_four_exchange_transfer_env.py
T
dekun 29b0634c6d feat: bidirectional daily auto-transfer with position skip
Rebalance swap to AUTO_TRANSFER_AMOUNT at Beijing hour: top up from funding or sweep excess back. Skip and WeChat notify when active positions exist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 09:57:25 +08:00

116 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
将自动划转 / 币安资金账户相关项写入四所实例 .env(已存在则更新,缺失则追加)。
用法(仓库根目录):
python scripts/sync_four_exchange_transfer_env.py
python scripts/sync_four_exchange_transfer_env.py --dry-run
不修改 API 密钥与其它自定义项;若 .env 不存在则跳过(请先从 .env.example 复制)。
"""
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",
)
# 四所统一(页顶「将 swap 调整至 XU」= AUTO_TRANSFER_AMOUNT,双向归集,与 DAILY_START_CAPITAL 独立)
COMMON_KEYS = {
"AUTO_TRANSFER_ENABLED": "false",
"AUTO_TRANSFER_FROM": "funding",
"AUTO_TRANSFER_TO": "swap",
"TRANSFER_CCY": "USDT",
"AUTO_TRANSFER_BJ_HOUR": "8",
}
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 _ensure_transfer_block(lines: list[str], extra: dict[str, str]) -> list[str]:
daily = _env_get(lines, "DAILY_START_CAPITAL")
amount = _env_get(lines, "AUTO_TRANSFER_AMOUNT")
if amount is None:
amount = daily if daily else "30"
keys = dict(COMMON_KEYS)
keys["AUTO_TRANSFER_AMOUNT"] = amount
keys.update(extra)
for k, v in keys.items():
lines = _upsert(lines, k, v)
return lines
def sync_one(dir_name: str, dry_run: bool) -> 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"
lines = _parse_env(env_path)
extra = dict(BINANCE_ONLY) if dir_name == "crypto_monitor_binance" else {}
new_lines = _ensure_transfer_block(lines, extra)
if new_lines == lines:
return f"OK {dir_name}: 已是最新"
if dry_run:
return f"DRY {dir_name}: 将更新 {len([1 for a,b in zip(lines,new_lines) if a!=b])}+ 行"
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")
amt = _env_get(new_lines, "AUTO_TRANSFER_AMOUNT")
return f"DONE {dir_name}: AUTO_TRANSFER_AMOUNT={amt}"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
for name in INSTANCES:
print(sync_one(name, args.dry_run))
if __name__ == "__main__":
main()