fix: deploy scripts/ files properly (correct directory)
This commit is contained in:
@@ -6,14 +6,39 @@
|
||||
|
||||
API: push2his.eastmoney.com 个股资金流日线
|
||||
"""
|
||||
import json, os, sys, time
|
||||
import json, os, sys, time, urllib.request
|
||||
from datetime import datetime
|
||||
from urllib.request import urlopen
|
||||
from urllib.request import urlopen, Request
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from threading import Semaphore
|
||||
|
||||
DATA_DIR = "/home/hmo/web-dashboard/data"
|
||||
DECISIONS_PATH = f"{DATA_DIR}/decisions.json"
|
||||
CACHE_PATH = f"{DATA_DIR}/capital_flow_cache.json"
|
||||
|
||||
UA = "Mozilla/5.0"
|
||||
# 限速器:最多5个并发,每请求后强制间隔0.3s
|
||||
RATE_LIMIT = Semaphore(5)
|
||||
MIN_INTERVAL = 0.3
|
||||
_last_req = 0
|
||||
|
||||
def _rate_limited_request(url):
|
||||
"""带速率限制的HTTP GET,用Semaphore控制并发数"""
|
||||
global _last_req
|
||||
with RATE_LIMIT:
|
||||
elapsed = time.time() - _last_req
|
||||
if elapsed < MIN_INTERVAL:
|
||||
time.sleep(MIN_INTERVAL - elapsed)
|
||||
proxy_handler = urllib.request.ProxyHandler({})
|
||||
opener = urllib.request.build_opener(proxy_handler)
|
||||
req = Request(url, headers={"User-Agent": UA, "Referer": "https://data.eastmoney.com/"})
|
||||
try:
|
||||
resp = opener.open(req, timeout=8)
|
||||
_last_req = time.time()
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
# eastmoney secid: 1=上海 0=深圳
|
||||
def secid(code):
|
||||
code = str(code).strip()
|
||||
@@ -22,30 +47,28 @@ def secid(code):
|
||||
return f"0.{code}"
|
||||
|
||||
def fetch_flow(code, days=5):
|
||||
"""拉取个股近N日资金流"""
|
||||
"""拉取个股近N日资金流(带限速+代理绕过)"""
|
||||
sid = secid(code)
|
||||
url = f"http://push2his.eastmoney.com/api/qt/stock/fflow/daykline/get?secid={sid}&fields1=f1,f2,f3,f7&fields2=f51,f52,f53,f54,f55,f56,f57&lmt={days}"
|
||||
try:
|
||||
resp = urlopen(url, timeout=5)
|
||||
data = json.loads(resp.read().decode("utf-8"))
|
||||
klines = data.get("data", {}).get("klines", [])
|
||||
if not klines:
|
||||
return None
|
||||
result = []
|
||||
for k in klines:
|
||||
p = k.split(",")
|
||||
if len(p) >= 7:
|
||||
result.append({
|
||||
"date": p[0],
|
||||
"main_net": float(p[1]), # 主力净流入(元)
|
||||
"super_large": float(p[2]), # 超大单净流入(元)
|
||||
"large": float(p[3]), # 大单净流入(元)
|
||||
"medium": float(p[4]), # 中单净流入(元)
|
||||
"small": float(p[5]), # 小单净流入(元)
|
||||
})
|
||||
return result
|
||||
except Exception as e:
|
||||
data = _rate_limited_request(url)
|
||||
if not data:
|
||||
return None
|
||||
klines = data.get("data", {}).get("klines", [])
|
||||
if not klines:
|
||||
return None
|
||||
result = []
|
||||
for k in klines:
|
||||
p = k.split(",")
|
||||
if len(p) >= 7:
|
||||
result.append({
|
||||
"date": p[0],
|
||||
"main_net": float(p[1]), # 主力净流入(元)
|
||||
"super_large": float(p[2]), # 超大单净流入(元)
|
||||
"large": float(p[3]), # 大单净流入(元)
|
||||
"medium": float(p[4]), # 中单净流入(元)
|
||||
"small": float(p[5]), # 小单净流入(元)
|
||||
})
|
||||
return result
|
||||
|
||||
def fetch_flow_intraday(code):
|
||||
"""拉取当日分时资金流(用于盘中判断)"""
|
||||
@@ -119,27 +142,40 @@ def main():
|
||||
codes.add(c)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
all_flows = {}
|
||||
|
||||
for code in sorted(codes):
|
||||
|
||||
# 并行抓取:ThreadPoolExecutor + 内置限速器(Semaphore 5 + 0.3s间隔)
|
||||
code_list = sorted(codes)
|
||||
if not code_list:
|
||||
print("[capital_flow] 无代码需要采集")
|
||||
return
|
||||
|
||||
def fetch_one(code):
|
||||
flow = fetch_flow(code, days=5)
|
||||
if flow:
|
||||
analysis = analyze_flow(flow)
|
||||
all_flows[code] = {
|
||||
return (code, {
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
"flow": flow,
|
||||
"analysis": analysis,
|
||||
}
|
||||
time.sleep(0.3) # API限流
|
||||
|
||||
})
|
||||
return (code, None)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=5) as pool:
|
||||
futures = {pool.submit(fetch_one, c): c for c in code_list}
|
||||
for f in as_completed(futures):
|
||||
code, result = f.result()
|
||||
if result:
|
||||
all_flows[code] = result
|
||||
|
||||
# 写缓存
|
||||
cache = {
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
"stocks": all_flows,
|
||||
}
|
||||
json.dump(cache, open(CACHE_PATH, "w"), indent=2, ensure_ascii=False)
|
||||
print(f"[capital_flow] {len(all_flows)}只更新完成")
|
||||
print(f"[capital_flow] {len(all_flows)}/{len(code_list)}只更新完成")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user