默认新浪行情,普通用户无需同花顺token

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-15 11:16:51 +08:00
parent fb61153a4d
commit 314d3206c0
5 changed files with 51 additions and 50 deletions
+3 -15
View File
@@ -1,20 +1,8 @@
# 服务配置
HOST=0.0.0.0
PORT=6600
DEBUG=false
# Flask Session 密钥(部署时请改为随机字符串)
SECRET_KEY=change-this-to-a-random-secret-key
# 初始管理员账号(仅首次初始化数据库时使用)
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-me-on-first-login
# 企业微信 Webhook(也可在系统设置页面修改) # 企业微信 Webhook(也可在系统设置页面修改)
WECHAT_WEBHOOK= WECHAT_WEBHOOK=
# 行情数据源: auto(优先同花顺,失败回退新浪)| ths | sina # 行情数据源: sina(默认,免费)| auto(有机构 token 时优先同花顺)| ths
QUOTE_SOURCE=auto QUOTE_SOURCE=sina
# 同花顺 iFinD HTTP refresh_token也可在系统设置页面修改 # 同花顺 iFinD refresh_token仅机构用户,普通用户留空即可
THS_REFRESH_TOKEN= THS_REFRESH_TOKEN=
+14 -5
View File
@@ -22,7 +22,17 @@
| 郑商所 | `SR609``MA606` | 大写品种 + 3 位年月 | | 郑商所 | `SR609``MA606` | 大写品种 + 3 位年月 |
| 中金所 | `IF2606``IH2606` | 大写品种 + 4 位年月 | | 中金所 | `IF2606``IH2606` | 大写品种 + 4 位年月 |
界面展示同花顺代码;行情优先走**同花顺 iFinD HTTP API**,未配置或失败时自动回退新浪 界面展示**同花顺合约代码**ag2608、IF2606),与看盘软件一致;**行情默认走新浪财经**(免费,普通用户无需 token)
## 行情说明
| 项目 | 说明 |
|------|------|
| 合约代码 | 同花顺格式,输入中文自动匹配主力月份 |
| 价格数据 | 新浪财经 API(免费) |
| 同花顺 iFinD | 仅机构/付费数据接口用户可用,**普通期货通用户无 refresh_token** |
因此个人用户使用本系统:**看同花顺代码,价格走新浪**,两者在主力合约价格上基本一致,满足监控需求。
## 快速部署(Ubuntu root + /opt/qihuo ## 快速部署(Ubuntu root + /opt/qihuo
@@ -82,12 +92,11 @@ PORT=6600
SECRET_KEY=随机长字符串 SECRET_KEY=随机长字符串
ADMIN_USERNAME=admin ADMIN_USERNAME=admin
ADMIN_PASSWORD=首次登录密码 ADMIN_PASSWORD=首次登录密码
WECHAT_WEBHOOK=企业微信机器人地址(可选,也可在页面配置 WECHAT_WEBHOOK=企业微信机器人地址(可选)
QUOTE_SOURCE=auto QUOTE_SOURCE=sina
THS_REFRESH_TOKEN=同花顺 refresh_token(可选,也可在页面配置)
``` ```
> 同花顺行情需在 [同花顺数据接口](https://quantapi.10jqka.com.cn/) 申请 iFinD HTTP 权限,在「超级命令 → 工具」获取 `refresh_token`。未配置时自动使用新浪行情 普通用户保持 `QUOTE_SOURCE=sina` 即可,无需配置同花顺 token
> 管理员密码首次从 `.env` 写入数据库并哈希存储,之后请在「系统设置」中修改。 > 管理员密码首次从 `.env` 写入数据库并哈希存储,之后请在「系统设置」中修改。
+2 -10
View File
@@ -15,7 +15,7 @@ from flask import (
from werkzeug.security import check_password_hash, generate_password_hash from werkzeug.security import check_password_hash, generate_password_hash
from symbols import search_symbols, ths_to_codes from symbols import search_symbols, ths_to_codes
from market import get_price as market_get_price, set_ths_refresh_token from market import get_price as market_get_price, set_ths_refresh_token, get_quote_source_label
load_dotenv() load_dotenv()
@@ -521,11 +521,6 @@ def settings():
webhook = request.form.get("wechat_webhook", "").strip() webhook = request.form.get("wechat_webhook", "").strip()
set_setting("wechat_webhook", webhook) set_setting("wechat_webhook", webhook)
flash("企业微信配置已保存") 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": elif action == "password":
old_p = request.form.get("old_password", "") old_p = request.form.get("old_password", "")
new_p = request.form.get("new_password", "") new_p = request.form.get("new_password", "")
@@ -543,15 +538,12 @@ def settings():
return redirect(url_for("settings")) return redirect(url_for("settings"))
webhook = get_setting("wechat_webhook") webhook = get_setting("wechat_webhook")
ths_token = get_setting("ths_refresh_token")
username = get_setting("admin_username") username = get_setting("admin_username")
quote_source = os.getenv("QUOTE_SOURCE", "auto")
return render_template( return render_template(
"settings.html", "settings.html",
webhook=webhook, webhook=webhook,
ths_token=ths_token,
username=username, username=username,
quote_source=quote_source, quote_label=get_quote_source_label(),
) )
# —————————————— 启动 —————————————— # —————————————— 启动 ——————————————
+24 -7
View File
@@ -1,5 +1,6 @@
""" """
行情拉取:优先同花顺 iFinD HTTP API,失败或未配置时回退新浪 行情拉取:默认新浪(免费,普通用户可用)
同花顺 iFinD HTTP 仅面向机构用户,需单独申请 token,可选开启。
""" """
import os import os
import time import time
@@ -27,7 +28,23 @@ _token_cache: dict = {"token": "", "expires": 0.0, "refresh": ""}
def _quote_source() -> str: def _quote_source() -> str:
return os.getenv("QUOTE_SOURCE", "auto").strip().lower() return os.getenv("QUOTE_SOURCE", "sina").strip().lower()
def _has_ths_token() -> bool:
return bool(_get_refresh_token())
def get_quote_source_label() -> str:
"""界面展示用行情源说明。"""
source = _quote_source()
if source == "sina":
return "新浪(免费)"
if source == "ths":
return "同花顺 iFinD" if _has_ths_token() else "同花顺(未配置 token,无法使用)"
if _has_ths_token():
return "同花顺优先,失败回退新浪"
return "新浪(免费)"
def _sina_headers() -> dict: def _sina_headers() -> dict:
@@ -154,23 +171,23 @@ def get_ths_price(ths_full_code: str, refresh_token: str = "") -> Optional[float
def get_price(market_code: str, sina_fallback: str = "") -> Optional[float]: def get_price(market_code: str, sina_fallback: str = "") -> Optional[float]:
""" """
统一取价入口。 统一取价入口。
market_code: 同花顺完整代码 ag2608.SHFE(优先 sina_fallback: 新浪代码 nf_AG2608(普通用户默认使用
sina_fallback: 新浪代码 nf_AG2608(回退 market_code: 同花顺完整代码 ag2608.SHFE(仅机构 token 可用时
""" """
source = _quote_source() source = _quote_source()
if source in ("ths", "auto") and market_code and "." in market_code: # 仅在有 token 且配置为 ths/auto 时才尝试同花顺
use_ths = source == "ths" or (source == "auto" and _has_ths_token())
if use_ths and market_code and "." in market_code:
price = get_ths_price(market_code) price = get_ths_price(market_code)
if price is not None: if price is not None:
return price return price
if source == "ths": if source == "ths":
return None return None
if sina_fallback: if sina_fallback:
return get_sina_price(sina_fallback) return get_sina_price(sina_fallback)
# market_code 本身就是新浪格式
if market_code.startswith("nf_") or market_code.startswith("CFF_RE_"): if market_code.startswith("nf_") or market_code.startswith("CFF_RE_"):
return get_sina_price(market_code) return get_sina_price(market_code)
+6 -11
View File
@@ -4,17 +4,12 @@
<h1 class="page-title">系统设置</h1> <h1 class="page-title">系统设置</h1>
<div class="card"> <div class="card">
<h2>同花顺行情(iFinD HTTP</h2> <h2>行情说明</h2>
<form action="{{ url_for('settings') }}" method="post" class="form-row"> <p style="font-size:.9rem;color:#ccc;line-height:1.6">
<input type="hidden" name="action" value="ths"> 当前行情源:<strong style="color:#4cc2ff">{{ quote_label }}</strong><br>
<input name="ths_refresh_token" type="text" placeholder="refresh_token" value="{{ ths_token }}" style="flex:1;min-width:300px"> 合约代码按<strong>同花顺格式</strong>显示(如 ag2608、IF2606),便于与看盘软件对照;
<button type="submit" class="btn-primary">保存</button> 实际价格通过<strong>新浪财经</strong>免费接口获取,普通用户无需申请 token。<br>
</form> <span style="color:#888;font-size:.85rem">同花顺 iFinD 接口面向机构用户,个人期货通用户一般无法获取 refresh_token,故系统默认不使用。</span>
<p style="font-size:.8rem;color:#888;margin-top:.75rem">
当前行情源:<strong>{{ quote_source }}</strong>(auto=优先同花顺,失败回退新浪)。
在 iFinD 接口包「超级命令 → 工具」查询 refresh_token
或前往 <a href="https://quantapi.10jqka.com.cn/" target="_blank" style="color:#4cc2ff">同花顺数据接口</a> 申请试用。
未配置 token 时自动使用新浪行情。
</p> </p>
</div> </div>