部署改回/opt;接入同花顺iFinD HTTP行情,新浪作回退

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-15 11:14:10 +08:00
parent bd7f0da1ca
commit fb61153a4d
11 changed files with 403 additions and 158 deletions
+58 -23
View File
@@ -14,7 +14,8 @@ from flask import (
)
from werkzeug.security import check_password_hash, generate_password_hash
from symbols import search_symbols, get_price, ths_to_sina_code
from symbols import search_symbols, ths_to_codes
from market import get_price as market_get_price, set_ths_refresh_token
load_dotenv()
@@ -83,8 +84,9 @@ def init_db():
"ALTER TABLE key_monitors ADD COLUMN lower_triggered INTEGER DEFAULT 0",
"ALTER TABLE trade_records ADD COLUMN symbol_name TEXT",
"ALTER TABLE order_plans ADD COLUMN sina_code TEXT",
"ALTER TABLE key_monitors ADD COLUMN sina_code TEXT",
"ALTER TABLE trade_records ADD COLUMN sina_code TEXT",
"ALTER TABLE order_plans ADD COLUMN market_code TEXT",
"ALTER TABLE key_monitors ADD COLUMN market_code TEXT",
"ALTER TABLE trade_records ADD COLUMN market_code TEXT",
]
for sql in migrations:
try:
@@ -103,9 +105,19 @@ def init_db():
if not get_setting("wechat_webhook") and os.getenv("WECHAT_WEBHOOK"):
set_setting("wechat_webhook", os.getenv("WECHAT_WEBHOOK"))
if not get_setting("ths_refresh_token") and os.getenv("THS_REFRESH_TOKEN"):
set_setting("ths_refresh_token", os.getenv("THS_REFRESH_TOKEN"))
init_db()
def sync_ths_token():
set_ths_refresh_token(get_setting("ths_refresh_token"))
sync_ths_token()
# —————————————— 推送 ——————————————
def send_wechat_msg(content: str):
@@ -121,20 +133,25 @@ def send_wechat_msg(content: str):
# —————————————— 行情 ——————————————
def resolve_sina_code(ths_code: str, sina_code: str = "") -> Optional[str]:
"""同花顺代码 -> 新浪行情代码;兼容旧数据中的新浪格式"""
if sina_code:
return sina_code
def resolve_market_codes(ths_code: str, market_code: str = "", sina_code: str = "") -> tuple[str, str]:
"""返回 (market_code, sina_code) 用于行情拉取"""
if market_code:
return market_code, sina_code
if sina_code and "." in sina_code:
return sina_code, ""
codes = ths_to_codes(ths_code)
if codes:
return codes["market_code"], codes["sina_code"]
if ths_code.startswith("nf_") or ths_code.startswith("CFF_RE_"):
return ths_code
return ths_to_sina_code(ths_code)
return ths_code, ths_code
return "", sina_code or ""
def fetch_price(ths_code: str, sina_code: str = "") -> Optional[float]:
code = resolve_sina_code(ths_code, sina_code)
if not code:
def fetch_price(ths_code: str, market_code: str = "", sina_code: str = "") -> Optional[float]:
mc, sc = resolve_market_codes(ths_code, market_code, sina_code)
if not mc and not sc:
return None
return get_price(code)
return market_get_price(mc, sc)
# —————————————— 监控逻辑 ——————————————
@@ -147,7 +164,8 @@ def check_order_plans():
for r in rows:
sym = r["symbol"]
sina = r["sina_code"] if "sina_code" in r.keys() else ""
p = fetch_price(sym, sina)
market = r["market_code"] if "market_code" in r.keys() else ""
p = fetch_price(sym, market, sina)
if not p:
continue
@@ -229,8 +247,9 @@ def check_key_monitors():
name = r["symbol_name"] or sym
pid = r["id"]
sina = r["sina_code"] if "sina_code" in r.keys() else ""
market = r["market_code"] if "market_code" in r.keys() else ""
p = fetch_price(sym, sina)
p = fetch_price(sym, market, sina)
if not p:
continue
@@ -337,20 +356,22 @@ def add_plan():
direction = d.get("direction")
symbol = d.get("symbol", "").strip()
symbol_name = d.get("symbol_name", "").strip()
market_code = d.get("market_code", "").strip()
sina_code = d.get("sina_code", "").strip()
if not direction:
flash("请选择多空方向")
return redirect(url_for("plans"))
if not symbol or not sina_code:
if not symbol or not market_code:
flash("请从下拉列表选择品种(同花顺合约代码)")
return redirect(url_for("plans"))
conn = get_db()
conn.execute(
"""INSERT INTO order_plans
(symbol, symbol_name, sina_code, direction, zone_upper, zone_lower, stop_loss, take_profit)
(symbol, symbol_name, market_code, sina_code, direction,
zone_upper, zone_lower, stop_loss, take_profit)
VALUES (?,?,?,?,?,?,?,?)""",
(
symbol, symbol_name, sina_code, direction,
symbol, symbol_name, market_code, sina_code, direction,
float(d["zone_upper"]), float(d["zone_lower"]),
float(d["stop_loss"]), float(d["take_profit"]),
),
@@ -388,19 +409,20 @@ def add_key():
direction = d.get("direction")
symbol = d.get("symbol", "").strip()
symbol_name = d.get("symbol_name", "").strip()
market_code = d.get("market_code", "").strip()
sina_code = d.get("sina_code", "").strip()
if not direction:
flash("请选择多空方向")
return redirect(url_for("keys"))
if not symbol or not sina_code:
if not symbol or not market_code:
flash("请从下拉列表选择品种(同花顺合约代码)")
return redirect(url_for("keys"))
conn = get_db()
conn.execute(
"""INSERT INTO key_monitors
(symbol, symbol_name, sina_code, monitor_type, direction, upper, lower)
VALUES (?,?,?,?,?,?,?)""",
(symbol, symbol_name, sina_code, d["type"], direction, float(d["upper"]), float(d["lower"])),
(symbol, symbol_name, market_code, sina_code, monitor_type, direction, upper, lower)
VALUES (?,?,?,?,?,?,?,?)""",
(symbol, symbol_name, market_code, sina_code, d["type"], direction, float(d["upper"]), float(d["lower"])),
)
conn.commit()
conn.close()
@@ -499,6 +521,11 @@ def settings():
webhook = request.form.get("wechat_webhook", "").strip()
set_setting("wechat_webhook", webhook)
flash("企业微信配置已保存")
elif action == "ths":
token = request.form.get("ths_refresh_token", "").strip()
set_setting("ths_refresh_token", token)
sync_ths_token()
flash("同花顺行情配置已保存")
elif action == "password":
old_p = request.form.get("old_password", "")
new_p = request.form.get("new_password", "")
@@ -516,8 +543,16 @@ def settings():
return redirect(url_for("settings"))
webhook = get_setting("wechat_webhook")
ths_token = get_setting("ths_refresh_token")
username = get_setting("admin_username")
return render_template("settings.html", webhook=webhook, username=username)
quote_source = os.getenv("QUOTE_SOURCE", "auto")
return render_template(
"settings.html",
webhook=webhook,
ths_token=ths_token,
username=username,
quote_source=quote_source,
)
# —————————————— 启动 ——————————————