chore: gate-only force-close policy and deploy pull script
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# 服务器上拉代码、同步 env、应用强制清仓策略并重启 PM2。
|
||||
# 用法(/opt/crypto_monitor 下 root):
|
||||
# bash deploy/pull_and_restart.sh
|
||||
# bash deploy/pull_and_restart.sh --dry-run
|
||||
set -euo pipefail
|
||||
|
||||
REPO="${REPO:-/opt/crypto_monitor}"
|
||||
DRY=()
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY=(--dry-run)
|
||||
echo "(dry-run mode)"
|
||||
fi
|
||||
|
||||
cd "$REPO"
|
||||
echo ">>> git pull"
|
||||
git pull
|
||||
|
||||
echo ">>> sync common trading env (binance + okx missing keys)"
|
||||
python3 scripts/sync_common_trading_env.py "${DRY[@]}"
|
||||
|
||||
echo ">>> force-close policy: gate=ON, binance/okx=OFF"
|
||||
python3 scripts/sync_common_trading_env.py --apply-force-close-policy "${DRY[@]}"
|
||||
|
||||
if [[ ${#DRY[@]} -gt 0 ]]; then
|
||||
echo "(dry-run, skip pm2 restart)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ">>> pm2 restart"
|
||||
pm2 restart crypto_gate crypto_binance crypto_okx manual-trading-hub manual-agent-gate 2>/dev/null \
|
||||
|| pm2 restart crypto-monitor-gate crypto-monitor-binance crypto-monitor-okx manual-trading-hub 2>/dev/null \
|
||||
|| pm2 restart all
|
||||
|
||||
echo ">>> FORCE_CLOSE settings"
|
||||
grep -E '^FORCE_CLOSE_' crypto_monitor_gate/.env crypto_monitor_binance/.env crypto_monitor_okx/.env || true
|
||||
|
||||
echo "done"
|
||||
@@ -111,9 +111,18 @@ python scripts/sync_four_exchange_position_sizing_env.py --set-buffer 0.98
|
||||
python scripts/sync_common_trading_env.py
|
||||
python scripts/sync_common_trading_env.py --dry-run
|
||||
python scripts/sync_common_trading_env.py --instances crypto_monitor_okx
|
||||
python scripts/sync_common_trading_env.py --apply-force-close-policy
|
||||
```
|
||||
|
||||
补全项含:`RECONCILE_*`、`PRICE_REFRESH_SECONDS`、`KEY_*` 门控、`FORCE_CLOSE_*`、`KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT` 等。
|
||||
补全项含:`RECONCILE_*`、`PRICE_REFRESH_SECONDS`、`KEY_*` 门控、`KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT` 等(**不含** `FORCE_CLOSE_*`,由 `--apply-force-close-policy` 单独处理)。
|
||||
|
||||
**强制清仓策略**(仅 Gate 开启):
|
||||
|
||||
```bash
|
||||
python scripts/sync_common_trading_env.py --apply-force-close-policy
|
||||
```
|
||||
|
||||
或服务器一键:`bash deploy/pull_and_restart.sh`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -50,12 +50,26 @@ SHARED_DEFAULTS: dict[str, str] = {
|
||||
"RECONCILE_STARTUP_GRACE_SEC": "90",
|
||||
"RECONCILE_FLAT_CONFIRM_POLLS": "3",
|
||||
"FULL_MARGIN_BUFFER_RATIO": "0.98",
|
||||
"FORCE_CLOSE_BJ_HOUR": "0",
|
||||
"FORCE_CLOSE_ENABLED": "false",
|
||||
"WECHAT_TIMEOUT_SECONDS": "10",
|
||||
"AI_TIMEOUT_SECONDS": "120",
|
||||
}
|
||||
|
||||
# 仅 Gate 启用 0 点强制清仓;币安/OKX 须保持关闭
|
||||
FORCE_CLOSE_POLICY: dict[str, dict[str, str]] = {
|
||||
"crypto_monitor_gate": {
|
||||
"FORCE_CLOSE_ENABLED": "true",
|
||||
"FORCE_CLOSE_BJ_HOUR": "0",
|
||||
},
|
||||
"crypto_monitor_binance": {
|
||||
"FORCE_CLOSE_ENABLED": "false",
|
||||
"FORCE_CLOSE_BJ_HOUR": "0",
|
||||
},
|
||||
"crypto_monitor_okx": {
|
||||
"FORCE_CLOSE_ENABLED": "false",
|
||||
"FORCE_CLOSE_BJ_HOUR": "0",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _parse_env(path: str) -> list[str]:
|
||||
if not os.path.isfile(path):
|
||||
@@ -116,10 +130,44 @@ def sync_one(dir_name: str, *, dry_run: bool, force: bool) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def apply_force_close_policy(*, dry_run: bool) -> bool:
|
||||
"""Gate 开启强制清仓;币安/OKX 强制关闭(覆盖已有值)。"""
|
||||
any_changed = False
|
||||
for dir_name, values in FORCE_CLOSE_POLICY.items():
|
||||
path = os.path.join(REPO, dir_name, ".env")
|
||||
if not os.path.isfile(path):
|
||||
print(f"skip (no .env): {dir_name}")
|
||||
continue
|
||||
lines = _parse_env(path)
|
||||
changed_keys: list[str] = []
|
||||
for key, val in values.items():
|
||||
cur = _env_get(lines, key)
|
||||
if cur != val:
|
||||
lines = _upsert(lines, key, val)
|
||||
changed_keys.append(key)
|
||||
if not changed_keys:
|
||||
print(f"ok (force-close policy): {dir_name}")
|
||||
continue
|
||||
any_changed = True
|
||||
print(f"force-close policy: {dir_name}")
|
||||
for key in changed_keys:
|
||||
print(f" = {key}={values[key]}")
|
||||
if not dry_run:
|
||||
text = "\n".join(lines).rstrip() + "\n"
|
||||
with open(path, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(text)
|
||||
return any_changed
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description="同步币安/OKX 共用 trading env(缺失项追加)")
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
ap.add_argument("--force", action="store_true", help="覆盖已有值(慎用)")
|
||||
ap.add_argument(
|
||||
"--apply-force-close-policy",
|
||||
action="store_true",
|
||||
help="Gate 开启 0 点强制清仓,币安/OKX 强制关闭",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--instances",
|
||||
nargs="+",
|
||||
@@ -133,6 +181,9 @@ def main() -> None:
|
||||
for inst in instances:
|
||||
if sync_one(inst, dry_run=args.dry_run, force=args.force):
|
||||
any_changed = True
|
||||
if args.apply_force_close_policy:
|
||||
if apply_force_close_policy(dry_run=args.dry_run):
|
||||
any_changed = True
|
||||
if args.dry_run and any_changed:
|
||||
print("(dry-run, 未写入)")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user