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>
This commit is contained in:
@@ -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()}"
|
||||
)
|
||||
Reference in New Issue
Block a user