Compare commits
2 Commits
0456d5fa2c
...
e6e79215fc
| Author | SHA1 | Date | |
|---|---|---|---|
| e6e79215fc | |||
| 29b0634c6d |
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
每日自动划转:北京时间指定整点小时内,将交易账户(AUTO_TRANSFER_TO)余额调整至目标额。
|
||||
|
||||
- 交易账户 < 目标:从资金账户划入差额
|
||||
- 交易账户 > 目标:将多余划回资金账户
|
||||
- 有 active 持仓:不划转,写账簿并企业微信说明
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
def run_auto_transfer_once_per_day(
|
||||
*,
|
||||
enabled: bool,
|
||||
bj_hour: int,
|
||||
target_amount: float,
|
||||
from_account: str,
|
||||
to_account: str,
|
||||
funds_decimals: int,
|
||||
get_db: Callable[[], Any],
|
||||
get_active_position_count: Callable[[Any], int],
|
||||
get_account_usdt_total: Callable[[str], float | None],
|
||||
execute_transfer_usdt: Callable[[float, str, str], tuple[bool, str, Any]],
|
||||
send_wechat_msg: Callable[[str], None],
|
||||
utc_now_dt: Callable[[], Any],
|
||||
app_tz: Any,
|
||||
utc_calendar_date_str: Callable[[], str],
|
||||
app_now_str: Callable[[], str],
|
||||
min_transfer: float = 0.01,
|
||||
) -> None:
|
||||
if not enabled:
|
||||
return
|
||||
utc_dt = utc_now_dt()
|
||||
bj = utc_dt.astimezone(app_tz)
|
||||
if bj.hour != bj_hour:
|
||||
return
|
||||
|
||||
transfer_day = utc_calendar_date_str()
|
||||
conn = get_db()
|
||||
exists = conn.execute(
|
||||
"SELECT id FROM transfer_logs WHERE transfer_type=? AND transfer_day=?",
|
||||
("auto_daily", transfer_day),
|
||||
).fetchone()
|
||||
if exists:
|
||||
conn.close()
|
||||
return
|
||||
|
||||
def _log(
|
||||
amount: float,
|
||||
fr: str,
|
||||
to: str,
|
||||
status: str,
|
||||
message: str,
|
||||
*,
|
||||
commit_close: bool = True,
|
||||
) -> None:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, amount, fr, to, status, message[:500]),
|
||||
)
|
||||
conn.commit()
|
||||
if commit_close:
|
||||
conn.close()
|
||||
|
||||
active = get_active_position_count(conn)
|
||||
if active > 0:
|
||||
msg = f"持仓中({active}笔),本次资金无划转"
|
||||
_log(0, from_account, to_account, "skipped", msg)
|
||||
send_wechat_msg(
|
||||
f"自动划转:{msg}\n"
|
||||
f"目标:{to_account} 调整至 {round(float(target_amount), funds_decimals)}U\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
target = round(float(target_amount), funds_decimals)
|
||||
trade_bal = get_account_usdt_total(to_account)
|
||||
if trade_bal is None:
|
||||
_log(
|
||||
0,
|
||||
from_account,
|
||||
to_account,
|
||||
"failed",
|
||||
f"读取{to_account}账户USDT失败",
|
||||
)
|
||||
return
|
||||
|
||||
trade = round(float(trade_bal), funds_decimals)
|
||||
diff = round(target - trade, funds_decimals)
|
||||
|
||||
if abs(diff) < min_transfer:
|
||||
_log(
|
||||
0,
|
||||
from_account,
|
||||
to_account,
|
||||
"skipped",
|
||||
f"{to_account}账户已为{trade}U(目标{target}U)",
|
||||
)
|
||||
return
|
||||
|
||||
if diff > 0:
|
||||
fr, to, amount = from_account, to_account, diff
|
||||
action = "划入"
|
||||
else:
|
||||
fr, to, amount = to_account, from_account, round(abs(diff), funds_decimals)
|
||||
action = "划出"
|
||||
|
||||
from_bal = get_account_usdt_total(fr)
|
||||
if from_bal is not None and round(float(from_bal), funds_decimals) < amount:
|
||||
cur = round(float(from_bal), funds_decimals)
|
||||
_log(amount, fr, to, "failed", f"{fr}账户USDT不足,需{amount}U,当前{cur}U")
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:{fr}余额不足,需{amount}U,当前{cur}U({action}至{to_account}目标{target}U)\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
ok, msg, _ = execute_transfer_usdt(amount, fr, to)
|
||||
_log(amount, fr, to, "success" if ok else "failed", msg)
|
||||
if ok:
|
||||
send_wechat_msg(
|
||||
f"自动划转成功:{to_account} {trade}U→目标{target}U,{action}{amount}U {fr}->{to}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
else:
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:计划{action}{amount}U {fr}->{to}(目标{target}U)\n原因:{msg}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
@@ -136,7 +136,7 @@ FULL_MARGIN_BUFFER_RATIO=0.98
|
||||
# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值)
|
||||
# =============================================================================
|
||||
AUTO_TRANSFER_ENABLED=false
|
||||
# 合约/交易账户(AUTO_TRANSFER_TO)补足到的 USDT 总额,非每日开仓基数
|
||||
# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转
|
||||
AUTO_TRANSFER_AMOUNT=30
|
||||
AUTO_TRANSFER_FROM=funding
|
||||
AUTO_TRANSFER_TO=swap
|
||||
|
||||
@@ -98,6 +98,7 @@ from key_monitor_full_margin_lib import (
|
||||
monitor_type_disallowed_in_full_margin,
|
||||
purge_disallowed_key_monitors,
|
||||
)
|
||||
from auto_transfer_daily_lib import run_auto_transfer_once_per_day
|
||||
from key_monitor_lib import (
|
||||
KEY_DIRECTION_WATCH,
|
||||
KEY_MONITOR_ALERT_ONLY_TYPES,
|
||||
@@ -2977,72 +2978,23 @@ def get_account_usdt_total(account_type):
|
||||
|
||||
|
||||
def auto_transfer_once_per_day():
|
||||
if not AUTO_TRANSFER_ENABLED:
|
||||
return
|
||||
utc_dt = utc_now_dt()
|
||||
bj = utc_dt.astimezone(APP_TZ)
|
||||
if bj.hour != AUTO_TRANSFER_BJ_HOUR:
|
||||
return
|
||||
transfer_day = utc_calendar_date_str()
|
||||
conn = get_db()
|
||||
exists = conn.execute(
|
||||
"SELECT id FROM transfer_logs WHERE transfer_type=? AND transfer_day=?",
|
||||
("auto_daily", transfer_day)
|
||||
).fetchone()
|
||||
if exists:
|
||||
conn.close()
|
||||
return
|
||||
target_amount = AUTO_TRANSFER_AMOUNT
|
||||
to_balance = get_account_usdt_total(AUTO_TRANSFER_TO)
|
||||
from_balance = get_account_usdt_total(AUTO_TRANSFER_FROM)
|
||||
if to_balance is None:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"读取{AUTO_TRANSFER_TO}账户USDT失败")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
needed = round(max(target_amount - float(to_balance), 0), FUNDS_DECIMALS)
|
||||
if needed <= 0:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "skipped", f"{AUTO_TRANSFER_TO}账户已达到目标{target_amount}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
if from_balance is not None and from_balance < needed:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"{AUTO_TRANSFER_FROM}账户USDT不足,需{needed}U,当前{round(from_balance, FUNDS_DECIMALS)}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:{AUTO_TRANSFER_FROM}余额不足,需{needed}U,当前{round(from_balance, FUNDS_DECIMALS)}U\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
ok, msg, _ = execute_transfer_usdt(needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO)
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "success" if ok else "failed", msg[:500])
|
||||
run_auto_transfer_once_per_day(
|
||||
enabled=AUTO_TRANSFER_ENABLED,
|
||||
bj_hour=AUTO_TRANSFER_BJ_HOUR,
|
||||
target_amount=AUTO_TRANSFER_AMOUNT,
|
||||
from_account=AUTO_TRANSFER_FROM,
|
||||
to_account=AUTO_TRANSFER_TO,
|
||||
funds_decimals=FUNDS_DECIMALS,
|
||||
get_db=get_db,
|
||||
get_active_position_count=get_active_position_count,
|
||||
get_account_usdt_total=get_account_usdt_total,
|
||||
execute_transfer_usdt=execute_transfer_usdt,
|
||||
send_wechat_msg=send_wechat_msg,
|
||||
utc_now_dt=utc_now_dt,
|
||||
app_tz=APP_TZ,
|
||||
utc_calendar_date_str=utc_calendar_date_str,
|
||||
app_now_str=app_now_str,
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if ok:
|
||||
send_wechat_msg(
|
||||
f"自动划转成功:补足到{target_amount}U,实际划转{needed}U "
|
||||
f"{AUTO_TRANSFER_FROM}->{AUTO_TRANSFER_TO}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
else:
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:计划补足到{target_amount}U,需划转{needed}U\n原因:{msg}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
|
||||
|
||||
def trading_day_reset_allows_new_open(now):
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
<div class="rule-tip">
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;界面时间为北京;将 {{ auto_transfer_to }} 补足到 {{ auto_transfer_amount }}U,来自 {{ auto_transfer_from }})
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;将 {{ auto_transfer_to }} 调整至 {{ auto_transfer_amount }}U:不足从 {{ auto_transfer_from }} 划入、超出划回 {{ auto_transfer_from }};<strong>持仓中不划转</strong>并微信通知)
|
||||
</div>
|
||||
<form action="/manual_transfer" method="post" class="form-row">
|
||||
<input name="amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额U" required>
|
||||
|
||||
@@ -142,7 +142,7 @@ FULL_MARGIN_BUFFER_RATIO=0.98
|
||||
# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值)
|
||||
# =============================================================================
|
||||
AUTO_TRANSFER_ENABLED=false
|
||||
# 交易账户(AUTO_TRANSFER_TO)补足到的 USDT 总额,非每日开仓基数
|
||||
# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转
|
||||
AUTO_TRANSFER_AMOUNT=30
|
||||
AUTO_TRANSFER_FROM=funding
|
||||
AUTO_TRANSFER_TO=swap
|
||||
|
||||
+17
-65
@@ -97,6 +97,7 @@ from key_monitor_full_margin_lib import (
|
||||
monitor_type_disallowed_in_full_margin,
|
||||
purge_disallowed_key_monitors,
|
||||
)
|
||||
from auto_transfer_daily_lib import run_auto_transfer_once_per_day
|
||||
from key_monitor_lib import (
|
||||
KEY_DIRECTION_WATCH,
|
||||
KEY_MONITOR_ALERT_ONLY_TYPES,
|
||||
@@ -2670,72 +2671,23 @@ def get_account_usdt_total(account_type):
|
||||
|
||||
|
||||
def auto_transfer_once_per_day():
|
||||
if not AUTO_TRANSFER_ENABLED:
|
||||
return
|
||||
utc_dt = utc_now_dt()
|
||||
bj = utc_dt.astimezone(APP_TZ)
|
||||
if bj.hour != AUTO_TRANSFER_BJ_HOUR:
|
||||
return
|
||||
transfer_day = utc_calendar_date_str()
|
||||
conn = get_db()
|
||||
exists = conn.execute(
|
||||
"SELECT id FROM transfer_logs WHERE transfer_type=? AND transfer_day=?",
|
||||
("auto_daily", transfer_day)
|
||||
).fetchone()
|
||||
if exists:
|
||||
conn.close()
|
||||
return
|
||||
target_amount = AUTO_TRANSFER_AMOUNT
|
||||
to_balance = get_account_usdt_total(AUTO_TRANSFER_TO)
|
||||
from_balance = get_account_usdt_total(AUTO_TRANSFER_FROM)
|
||||
if to_balance is None:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"读取{AUTO_TRANSFER_TO}账户USDT失败")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
needed = round(max(target_amount - float(to_balance), 0), 4)
|
||||
if needed <= 0:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "skipped", f"{AUTO_TRANSFER_TO}账户已达到目标{round(float(target_amount), 2)}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
if from_balance is not None and from_balance < needed:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"{AUTO_TRANSFER_FROM}账户USDT不足,需{round(needed, 2)}U,当前{round(from_balance, 2)}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:{AUTO_TRANSFER_FROM}余额不足,需{round(needed, 2)}U,当前{round(from_balance, 2)}U\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
ok, msg, _ = execute_transfer_usdt(needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO)
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "success" if ok else "failed", msg[:500])
|
||||
run_auto_transfer_once_per_day(
|
||||
enabled=AUTO_TRANSFER_ENABLED,
|
||||
bj_hour=AUTO_TRANSFER_BJ_HOUR,
|
||||
target_amount=AUTO_TRANSFER_AMOUNT,
|
||||
from_account=AUTO_TRANSFER_FROM,
|
||||
to_account=AUTO_TRANSFER_TO,
|
||||
funds_decimals=2,
|
||||
get_db=get_db,
|
||||
get_active_position_count=get_active_position_count,
|
||||
get_account_usdt_total=get_account_usdt_total,
|
||||
execute_transfer_usdt=execute_transfer_usdt,
|
||||
send_wechat_msg=send_wechat_msg,
|
||||
utc_now_dt=utc_now_dt,
|
||||
app_tz=APP_TZ,
|
||||
utc_calendar_date_str=utc_calendar_date_str,
|
||||
app_now_str=app_now_str,
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if ok:
|
||||
send_wechat_msg(
|
||||
f"自动划转成功:补足到{round(float(target_amount), 2)}U,实际划转{round(needed, 2)}U "
|
||||
f"{AUTO_TRANSFER_FROM}->{AUTO_TRANSFER_TO}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
else:
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:计划补足到{round(float(target_amount), 2)}U,需划转{round(needed, 2)}U\n原因:{msg}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
|
||||
|
||||
def trading_day_reset_allows_new_open(now):
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
<div class="rule-tip">
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;界面时间为北京;将 {{ auto_transfer_to }} 补足到 {{ auto_transfer_amount }}U,来自 {{ auto_transfer_from }})
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;将 {{ auto_transfer_to }} 调整至 {{ auto_transfer_amount }}U:不足从 {{ auto_transfer_from }} 划入、超出划回 {{ auto_transfer_from }};<strong>持仓中不划转</strong>并微信通知)
|
||||
</div>
|
||||
<form action="/manual_transfer" method="post" class="form-row">
|
||||
<input name="amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额U" required>
|
||||
|
||||
@@ -110,7 +110,7 @@ FULL_MARGIN_BUFFER_RATIO=0.98
|
||||
# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值)
|
||||
# =============================================================================
|
||||
AUTO_TRANSFER_ENABLED=false
|
||||
# 交易账户(AUTO_TRANSFER_TO)补足到的 USDT 总额,非每日开仓基数
|
||||
# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转
|
||||
AUTO_TRANSFER_AMOUNT=30
|
||||
AUTO_TRANSFER_FROM=funding
|
||||
AUTO_TRANSFER_TO=swap
|
||||
|
||||
@@ -49,6 +49,7 @@ from key_monitor_full_margin_lib import (
|
||||
monitor_type_disallowed_in_full_margin,
|
||||
purge_disallowed_key_monitors,
|
||||
)
|
||||
from auto_transfer_daily_lib import run_auto_transfer_once_per_day
|
||||
from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit_scope_add_order
|
||||
from order_monitor_display_lib import (
|
||||
apply_order_price_display_fields,
|
||||
@@ -2675,72 +2676,23 @@ def get_account_usdt_total(account_type):
|
||||
|
||||
|
||||
def auto_transfer_once_per_day():
|
||||
if not AUTO_TRANSFER_ENABLED:
|
||||
return
|
||||
utc_dt = utc_now_dt()
|
||||
bj = utc_dt.astimezone(APP_TZ)
|
||||
if bj.hour != AUTO_TRANSFER_BJ_HOUR:
|
||||
return
|
||||
transfer_day = utc_calendar_date_str()
|
||||
conn = get_db()
|
||||
exists = conn.execute(
|
||||
"SELECT id FROM transfer_logs WHERE transfer_type=? AND transfer_day=?",
|
||||
("auto_daily", transfer_day)
|
||||
).fetchone()
|
||||
if exists:
|
||||
conn.close()
|
||||
return
|
||||
target_amount = AUTO_TRANSFER_AMOUNT
|
||||
to_balance = get_account_usdt_total(AUTO_TRANSFER_TO)
|
||||
from_balance = get_account_usdt_total(AUTO_TRANSFER_FROM)
|
||||
if to_balance is None:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"读取{AUTO_TRANSFER_TO}账户USDT失败")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
needed = round(max(target_amount - float(to_balance), 0), 4)
|
||||
if needed <= 0:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "skipped", f"{AUTO_TRANSFER_TO}账户已达到目标{target_amount}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
if from_balance is not None and from_balance < needed:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"{AUTO_TRANSFER_FROM}账户USDT不足,需{needed}U,当前{round(from_balance,4)}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:{AUTO_TRANSFER_FROM}余额不足,需{needed}U,当前{round(from_balance,4)}U\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
ok, msg, _ = execute_transfer_usdt(needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO)
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "success" if ok else "failed", msg[:500])
|
||||
run_auto_transfer_once_per_day(
|
||||
enabled=AUTO_TRANSFER_ENABLED,
|
||||
bj_hour=AUTO_TRANSFER_BJ_HOUR,
|
||||
target_amount=AUTO_TRANSFER_AMOUNT,
|
||||
from_account=AUTO_TRANSFER_FROM,
|
||||
to_account=AUTO_TRANSFER_TO,
|
||||
funds_decimals=2,
|
||||
get_db=get_db,
|
||||
get_active_position_count=get_active_position_count,
|
||||
get_account_usdt_total=get_account_usdt_total,
|
||||
execute_transfer_usdt=execute_transfer_usdt,
|
||||
send_wechat_msg=send_wechat_msg,
|
||||
utc_now_dt=utc_now_dt,
|
||||
app_tz=APP_TZ,
|
||||
utc_calendar_date_str=utc_calendar_date_str,
|
||||
app_now_str=app_now_str,
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if ok:
|
||||
send_wechat_msg(
|
||||
f"自动划转成功:补足到{target_amount}U,实际划转{needed}U "
|
||||
f"{AUTO_TRANSFER_FROM}->{AUTO_TRANSFER_TO}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
else:
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:计划补足到{target_amount}U,需划转{needed}U\n原因:{msg}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
|
||||
|
||||
def trading_day_reset_allows_new_open(now):
|
||||
|
||||
@@ -311,7 +311,7 @@
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
<div class="rule-tip">
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;界面时间为北京;将 {{ auto_transfer_to }} 补足到 {{ money_fmt(auto_transfer_amount) }}U,来自 {{ auto_transfer_from }})
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;将 {{ auto_transfer_to }} 调整至 {{ money_fmt(auto_transfer_amount) }}U:不足从 {{ auto_transfer_from }} 划入、超出划回 {{ auto_transfer_from }};<strong>持仓中不划转</strong>并微信通知)
|
||||
</div>
|
||||
<form action="/manual_transfer" method="post" class="form-row">
|
||||
<input name="amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额U" required>
|
||||
|
||||
@@ -101,7 +101,7 @@ FULL_MARGIN_BUFFER_RATIO=0.98
|
||||
# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值)
|
||||
# =============================================================================
|
||||
AUTO_TRANSFER_ENABLED=false
|
||||
# 交易账户(AUTO_TRANSFER_TO)补足到的 USDT 总额,非每日开仓基数
|
||||
# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转
|
||||
AUTO_TRANSFER_AMOUNT=30
|
||||
AUTO_TRANSFER_FROM=funding
|
||||
AUTO_TRANSFER_TO=swap
|
||||
|
||||
+17
-65
@@ -97,6 +97,7 @@ from key_monitor_full_margin_lib import (
|
||||
monitor_type_disallowed_in_full_margin,
|
||||
purge_disallowed_key_monitors,
|
||||
)
|
||||
from auto_transfer_daily_lib import run_auto_transfer_once_per_day
|
||||
from key_monitor_lib import (
|
||||
KEY_DIRECTION_WATCH,
|
||||
KEY_MONITOR_ALERT_ONLY_TYPES,
|
||||
@@ -2356,72 +2357,23 @@ def get_account_usdt_total(account_type):
|
||||
|
||||
|
||||
def auto_transfer_once_per_day():
|
||||
if not AUTO_TRANSFER_ENABLED:
|
||||
return
|
||||
utc_dt = utc_now_dt()
|
||||
bj = utc_dt.astimezone(APP_TZ)
|
||||
if bj.hour != AUTO_TRANSFER_BJ_HOUR:
|
||||
return
|
||||
transfer_day = utc_calendar_date_str()
|
||||
conn = get_db()
|
||||
exists = conn.execute(
|
||||
"SELECT id FROM transfer_logs WHERE transfer_type=? AND transfer_day=?",
|
||||
("auto_daily", transfer_day)
|
||||
).fetchone()
|
||||
if exists:
|
||||
conn.close()
|
||||
return
|
||||
target_amount = AUTO_TRANSFER_AMOUNT
|
||||
to_balance = get_account_usdt_total(AUTO_TRANSFER_TO)
|
||||
from_balance = get_account_usdt_total(AUTO_TRANSFER_FROM)
|
||||
if to_balance is None:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"读取{AUTO_TRANSFER_TO}账户USDT失败")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
needed = round(max(target_amount - float(to_balance), 0), 4)
|
||||
if needed <= 0:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, 0, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "skipped", f"{AUTO_TRANSFER_TO}账户已达到目标{target_amount}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
if from_balance is not None and from_balance < needed:
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "failed", f"{AUTO_TRANSFER_FROM}账户USDT不足,需{round(needed, 2)}U,当前{round(from_balance, 2)}U")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:{AUTO_TRANSFER_FROM}余额不足,需{round(needed, 2)}U,当前{round(from_balance, 2)}U\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
return
|
||||
|
||||
ok, msg, _ = execute_transfer_usdt(needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO)
|
||||
conn.execute(
|
||||
"INSERT INTO transfer_logs (transfer_type, transfer_day, amount, from_account, to_account, status, message) VALUES (?,?,?,?,?,?,?)",
|
||||
("auto_daily", transfer_day, needed, AUTO_TRANSFER_FROM, AUTO_TRANSFER_TO, "success" if ok else "failed", msg[:500])
|
||||
run_auto_transfer_once_per_day(
|
||||
enabled=AUTO_TRANSFER_ENABLED,
|
||||
bj_hour=AUTO_TRANSFER_BJ_HOUR,
|
||||
target_amount=AUTO_TRANSFER_AMOUNT,
|
||||
from_account=AUTO_TRANSFER_FROM,
|
||||
to_account=AUTO_TRANSFER_TO,
|
||||
funds_decimals=FUNDS_DECIMALS,
|
||||
get_db=get_db,
|
||||
get_active_position_count=get_active_position_count,
|
||||
get_account_usdt_total=get_account_usdt_total,
|
||||
execute_transfer_usdt=execute_transfer_usdt,
|
||||
send_wechat_msg=send_wechat_msg,
|
||||
utc_now_dt=utc_now_dt,
|
||||
app_tz=APP_TZ,
|
||||
utc_calendar_date_str=utc_calendar_date_str,
|
||||
app_now_str=app_now_str,
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if ok:
|
||||
send_wechat_msg(
|
||||
f"自动划转成功:补足到{round(float(target_amount), 2)}U,实际划转{round(needed, 2)}U "
|
||||
f"{AUTO_TRANSFER_FROM}->{AUTO_TRANSFER_TO}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
else:
|
||||
send_wechat_msg(
|
||||
f"自动划转失败:计划补足到{round(float(target_amount), 2)}U,需划转{round(needed, 2)}U\n原因:{msg}\n"
|
||||
f"账簿日(UTC):{transfer_day}|触发时刻(北京):{app_now_str()}"
|
||||
)
|
||||
|
||||
|
||||
def get_trading_day_reset_open_guard_enabled(conn=None):
|
||||
|
||||
@@ -435,7 +435,7 @@
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
<div class="rule-tip">
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;界面时间为北京;将 {{ auto_transfer_to }} 补足到 {{ auto_transfer_amount }}U,来自 {{ auto_transfer_from }})
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;将 {{ auto_transfer_to }} 调整至 {{ auto_transfer_amount }}U:不足从 {{ auto_transfer_from }} 划入、超出划回 {{ auto_transfer_from }};<strong>持仓中不划转</strong>并微信通知)
|
||||
</div>
|
||||
<form action="/manual_transfer" method="post" class="form-row">
|
||||
<input name="amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额U" required>
|
||||
|
||||
+7
-3
@@ -87,14 +87,18 @@ bash deploy/setup_env.sh
|
||||
|
||||
## 四所 `.env` 自动划转项(已有 .env 时)
|
||||
|
||||
`AUTO_TRANSFER_AMOUNT` 等与 `DAILY_START_CAPITAL` **独立**;四所 `.env.example` 已统一注释。若服务器上已有 `.env`,可合并写入(不覆盖 API 密钥):
|
||||
`AUTO_TRANSFER_AMOUNT` 等为交易账户目标余额(北京时间 8 点自动划入/划出,**持仓中不划转**并微信通知),与 `DAILY_START_CAPITAL` **独立**。若服务器上已有 `.env`,可合并写入(不覆盖 API 密钥):
|
||||
|
||||
```bash
|
||||
python scripts/sync_four_exchange_transfer_env.py
|
||||
# 缺 AUTO_TRANSFER_AMOUNT 时会沿用该文件中的 DAILY_START_CAPITAL
|
||||
# 计仓 + 划转一次同步(缺项补全,不覆盖已有 API 与自定义值)
|
||||
python scripts/sync_four_exchange_env.py
|
||||
# 或仅划转:缺 AUTO_TRANSFER_AMOUNT 时默认 50(否则沿用已有 / DAILY_START_CAPITAL)
|
||||
python scripts/sync_four_exchange_transfer_env.py --set-amount 50 --enable-auto-transfer
|
||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot
|
||||
```
|
||||
|
||||
详见 [docs/auto-transfer-daily.md](../docs/auto-transfer-daily.md)。
|
||||
|
||||
## 四所 `.env` 计仓模式项(已有 .env 时)
|
||||
|
||||
`POSITION_SIZING_MODE` / `FULL_MARGIN_BUFFER_RATIO` 仅能通过 env 切换;切换模式前须**无持仓**:
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# 每日自动划转(四所统一)
|
||||
|
||||
## 行为
|
||||
|
||||
在 `.env` 开启 `AUTO_TRANSFER_ENABLED=true` 后,监控轮询在**北京时间 `AUTO_TRANSFER_BJ_HOUR` 整点所在小时**内(默认 8:00–8:59)执行一次(按 **UTC 自然日** 去重):
|
||||
|
||||
| 交易账户 (`AUTO_TRANSFER_TO`,默认 swap) | 动作 |
|
||||
|------------------------------------------|------|
|
||||
| 余额 **低于** `AUTO_TRANSFER_AMOUNT` | 从 `AUTO_TRANSFER_FROM`(默认 funding)划入差额 |
|
||||
| 余额 **高于** `AUTO_TRANSFER_AMOUNT` | 将多余划回 `AUTO_TRANSFER_FROM` |
|
||||
| 与目标相差 < 0.01U | 跳过,不写划转 |
|
||||
| 存在 **active** 持仓(`order_monitors`) | **不划转**,写账簿 `skipped`,并**企业微信**说明「持仓中,本次资金无划转」 |
|
||||
|
||||
## 配置示例(目标 50U)
|
||||
|
||||
```env
|
||||
AUTO_TRANSFER_ENABLED=true
|
||||
AUTO_TRANSFER_AMOUNT=50
|
||||
AUTO_TRANSFER_FROM=funding
|
||||
AUTO_TRANSFER_TO=swap
|
||||
AUTO_TRANSFER_BJ_HOUR=8
|
||||
```
|
||||
|
||||
API Key 须具备万向划转权限(与手动划转相同)。
|
||||
|
||||
## 部署
|
||||
|
||||
```bash
|
||||
git pull
|
||||
# 四所补全划转项(已有值保留)
|
||||
python scripts/sync_four_exchange_transfer_env.py
|
||||
# 目标 50U 并开启自动划转
|
||||
python scripts/sync_four_exchange_transfer_env.py --set-amount 50 --enable-auto-transfer
|
||||
# 计仓 + 划转一并同步
|
||||
python scripts/sync_four_exchange_env.py --set-transfer-amount 50 --enable-auto-transfer
|
||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot
|
||||
```
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
四所 .env 一次性同步:计仓模式 + 自动划转(调用子脚本,不覆盖已有自定义值)。
|
||||
|
||||
用法(仓库根目录):
|
||||
python scripts/sync_four_exchange_env.py
|
||||
python scripts/sync_four_exchange_env.py --dry-run
|
||||
python scripts/sync_four_exchange_env.py --set-transfer-amount 50 --enable-auto-transfer
|
||||
|
||||
子脚本可单独运行:
|
||||
python scripts/sync_four_exchange_position_sizing_env.py
|
||||
python scripts/sync_four_exchange_transfer_env.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
PY = sys.executable
|
||||
|
||||
|
||||
def _run(script: str, extra: list[str]) -> int:
|
||||
cmd = [PY, str(REPO / "scripts" / script)] + extra
|
||||
print(f"\n>>> {' '.join(cmd)}")
|
||||
return subprocess.call(cmd, cwd=str(REPO))
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="四所 .env 统一同步(计仓 + 划转)")
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
ap.add_argument("--set-mode", choices=("risk", "full_margin"), metavar="MODE")
|
||||
ap.add_argument("--set-transfer-amount", metavar="U")
|
||||
ap.add_argument("--enable-auto-transfer", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
dry = ["--dry-run"] if args.dry_run else []
|
||||
code = 0
|
||||
|
||||
ps_args = list(dry)
|
||||
if args.set_mode:
|
||||
ps_args.extend(["--set-mode", args.set_mode])
|
||||
code |= _run("sync_four_exchange_position_sizing_env.py", ps_args)
|
||||
|
||||
tr_args = list(dry)
|
||||
if args.set_transfer_amount:
|
||||
tr_args.extend(["--set-amount", args.set_transfer_amount])
|
||||
if args.enable_auto_transfer:
|
||||
tr_args.append("--enable-auto-transfer")
|
||||
code |= _run("sync_four_exchange_transfer_env.py", tr_args)
|
||||
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将自动划转 / 币安资金账户相关项写入四所实例 .env(已存在则更新,缺失则追加)。
|
||||
将每日自动划转相关项写入四所实例 .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 复制)。
|
||||
"""
|
||||
@@ -23,8 +25,12 @@ INSTANCES = (
|
||||
"crypto_monitor_gate_bot",
|
||||
)
|
||||
|
||||
# 四所统一(页顶「将 swap 补足到 XU」= AUTO_TRANSFER_AMOUNT,与 DAILY_START_CAPITAL 独立)
|
||||
COMMON_KEYS = {
|
||||
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",
|
||||
@@ -32,6 +38,8 @@ COMMON_KEYS = {
|
||||
"AUTO_TRANSFER_BJ_HOUR": "8",
|
||||
}
|
||||
|
||||
DEFAULT_AMOUNT = "50"
|
||||
|
||||
BINANCE_ONLY = {
|
||||
"BINANCE_FUNDING_INCLUDE_SPOT": "false",
|
||||
}
|
||||
@@ -71,44 +79,132 @@ def _upsert(lines: list[str], key: str, value: str) -> list[str]:
|
||||
return out
|
||||
|
||||
|
||||
def _ensure_transfer_block(lines: list[str], extra: dict[str, str]) -> list[str]:
|
||||
daily = _env_get(lines, "DAILY_START_CAPITAL")
|
||||
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 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)
|
||||
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 sync_one(dir_name: str, dry_run: bool) -> str:
|
||||
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)"
|
||||
lines = _parse_env(env_path)
|
||||
old_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}: 已是最新"
|
||||
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}: 将更新 {len([1 for a,b in zip(lines,new_lines) if a!=b])}+ 行"
|
||||
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")
|
||||
amt = _env_get(new_lines, "AUTO_TRANSFER_AMOUNT")
|
||||
return f"DONE {dir_name}: AUTO_TRANSFER_AMOUNT={amt}"
|
||||
return f"DONE {dir_name}: ENABLED={enabled} AMOUNT={amt}"
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
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))
|
||||
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__":
|
||||
|
||||
Reference in New Issue
Block a user