Add auto break-even toggle and WeCom hub notifications.
Desktop can auto-pull stop to open+/-1 after a 3$ favorable move; hub pushes open/close events and a 23:00 BJT daily summary via webhook. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+103
-1
@@ -157,7 +157,52 @@ def register_routes(app: Flask) -> None:
|
||||
@app.get("/settings")
|
||||
@login_required
|
||||
def settings_page():
|
||||
return render_template("settings.html")
|
||||
from app import wecom as wecom_mod
|
||||
|
||||
wecom_cfg = {"enabled": False, "webhook": "", "daily_hour": Config.WECOM_DAILY_HOUR}
|
||||
if session.get("role") == "admin":
|
||||
with database.get_db() as conn:
|
||||
wecom_cfg = {
|
||||
"enabled": wecom_mod.is_enabled(conn),
|
||||
"webhook": wecom_mod.webhook_url(conn),
|
||||
"daily_hour": Config.WECOM_DAILY_HOUR,
|
||||
"daily_minute": Config.WECOM_DAILY_MINUTE,
|
||||
"daily_last": database.get_setting(conn, wecom_mod.SETTING_DAILY_LAST, ""),
|
||||
}
|
||||
return render_template("settings.html", wecom=wecom_cfg)
|
||||
|
||||
@app.post("/settings/wecom")
|
||||
@admin_required
|
||||
def settings_wecom():
|
||||
from app import wecom as wecom_mod
|
||||
|
||||
enabled = "1" if request.form.get("enabled") == "1" else "0"
|
||||
webhook = (request.form.get("webhook") or "").strip()
|
||||
with database.get_db() as conn:
|
||||
database.set_setting(conn, wecom_mod.SETTING_ENABLED, enabled)
|
||||
database.set_setting(conn, wecom_mod.SETTING_WEBHOOK, webhook)
|
||||
flash("企业微信推送设置已保存", "ok")
|
||||
return redirect(url_for("settings_page"))
|
||||
|
||||
@app.post("/settings/wecom/test")
|
||||
@admin_required
|
||||
def settings_wecom_test():
|
||||
from app import wecom as wecom_mod
|
||||
|
||||
ok, detail = wecom_mod.send_text(
|
||||
"【测试】Gold Monitor 企业微信推送正常。\n开仓/平仓即时通知 · 每日 23:00 日报"
|
||||
)
|
||||
flash("测试发送成功" if ok else f"测试失败:{detail}", "ok" if ok else "error")
|
||||
return redirect(url_for("settings_page"))
|
||||
|
||||
@app.post("/settings/wecom/daily")
|
||||
@admin_required
|
||||
def settings_wecom_daily():
|
||||
from app import wecom as wecom_mod
|
||||
|
||||
ok, detail = wecom_mod.send_daily_reports()
|
||||
flash("日报已推送" if ok else f"日报推送失败:{detail}", "ok" if ok else "error")
|
||||
return redirect(url_for("settings_page"))
|
||||
|
||||
@app.post("/settings/profile")
|
||||
@login_required
|
||||
@@ -654,6 +699,7 @@ def register_routes(app: Flask) -> None:
|
||||
def api_trades_upsert():
|
||||
token = request.headers.get("X-Device-Token") or ""
|
||||
data = request.get_json(silent=True) or {}
|
||||
wecom_job = None # ("open"|"close", kwargs) 提交后再推,避免嵌套事务
|
||||
with database.get_db() as conn:
|
||||
act = database.auth_token(conn, token)
|
||||
if not act:
|
||||
@@ -712,11 +758,24 @@ def register_routes(app: Flask) -> None:
|
||||
(act["account_id"], ticket),
|
||||
).fetchone()
|
||||
|
||||
was_open = bool(
|
||||
existing and existing["close_time"] in (None, "")
|
||||
)
|
||||
is_new = existing is None
|
||||
event_name = str((snapshot or {}).get("event") or "").strip().lower()
|
||||
now_closed = close_time not in (None, "")
|
||||
notify_close_price = close_price
|
||||
acc_row = conn.execute(
|
||||
"SELECT name FROM accounts WHERE id=?", (act["account_id"],)
|
||||
).fetchone()
|
||||
account_name = (acc_row["name"] if acc_row else "") or f"账户#{act['account_id']}"
|
||||
|
||||
if existing:
|
||||
# 浮盈更新不覆盖已有平仓;空备注不覆盖已标注
|
||||
keep_close = existing["close_time"] not in (None, "") and close_time in (None, "")
|
||||
new_close_time = existing["close_time"] if keep_close else close_time
|
||||
new_close_price = existing["close_price"] if keep_close else close_price
|
||||
notify_close_price = new_close_price
|
||||
new_entry = entry_style or (existing["entry_style"] or "")
|
||||
new_note = review_note or (existing["review_note"] or "")
|
||||
new_open_time = open_time if open_time is not None else existing["open_time"]
|
||||
@@ -809,6 +868,49 @@ def register_routes(app: Flask) -> None:
|
||||
""",
|
||||
(dup_snap, act["account_id"], ticket, tid),
|
||||
)
|
||||
|
||||
# 企业微信:仅真实开仓/平仓推送(浮盈、改 SL/TP、对账假平仓不发)
|
||||
if event_name == "open" and is_new:
|
||||
wecom_job = (
|
||||
"open",
|
||||
{
|
||||
"account_name": account_name,
|
||||
"ticket": ticket,
|
||||
"symbol": symbol,
|
||||
"side": side,
|
||||
"volume": volume,
|
||||
"open_price": open_price,
|
||||
"sl": sl,
|
||||
"tp": tp,
|
||||
},
|
||||
)
|
||||
elif event_name in ("close", "history_close") and now_closed and (
|
||||
is_new or was_open
|
||||
):
|
||||
wecom_job = (
|
||||
"close",
|
||||
{
|
||||
"account_name": account_name,
|
||||
"ticket": ticket,
|
||||
"symbol": symbol,
|
||||
"side": side,
|
||||
"volume": volume,
|
||||
"open_price": open_price,
|
||||
"close_price": notify_close_price,
|
||||
"profit": profit,
|
||||
},
|
||||
)
|
||||
if wecom_job:
|
||||
try:
|
||||
from app import wecom as wecom_mod
|
||||
|
||||
kind, kwargs = wecom_job
|
||||
if kind == "open":
|
||||
wecom_mod.notify_trade_open(**kwargs)
|
||||
else:
|
||||
wecom_mod.notify_trade_close(**kwargs)
|
||||
except Exception:
|
||||
pass
|
||||
return jsonify({"ok": True, "id": tid})
|
||||
|
||||
@app.post("/api/v1/trades/reconcile")
|
||||
|
||||
Reference in New Issue
Block a user