From 46edd861b5a076607646ad4cf44a7288816c6153 Mon Sep 17 00:00:00 2001 From: Lxy Date: Tue, 26 May 2026 00:13:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B=E4=BF=AE=E5=A4=8D=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=93=81=E7=A7=8D=E5=90=88=E7=BA=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__pycache__/main.cpython-311.pyc | Bin 6748 -> 8047 bytes app/api/__pycache__/config.cpython-311.pyc | Bin 14858 -> 14954 bytes .../futures_analysis.cpython-311.pyc | Bin 51871 -> 52187 bytes app/api/config.py | 18 ++-- app/api/futures_analysis.py | 40 +++++--- .../__pycache__/cache.cpython-311.pyc | Bin 14225 -> 14225 bytes app/static/index.html | 91 +++++++++++++++--- config/symbols_config.json | 9 +- data/buffer.db | Bin 2052096 -> 2174976 bytes data/futures_analysis.db | Bin 499712 -> 946176 bytes 10 files changed, 122 insertions(+), 36 deletions(-) diff --git a/app/__pycache__/main.cpython-311.pyc b/app/__pycache__/main.cpython-311.pyc index b9577d7149bd2670d142b86eec9b93f1ec9433cc..deb04199c96f9403007421bab5a352fa47e6804f 100644 GIT binary patch delta 3484 zcmb7GeQX>@72mJBTi=(@cfJpw&-U4IVxOJ!N78^(e}u%9rIZAU9jLleI5xLqd!5}o zcXkh`Wy@*nkfa4#Ger$l2p6d?1q%=rDxXDA1meGQWvE$!6#j5YBm}OiA`v9Sn?2vv zJ{u9{cHYgr_j~i+ym|9>|9IcuNBl4Qd~OEX)yB}w?*q&J823ZRa_6xzU6e$YQCP*P z)#z@?ZI4}=NB2rzdn{@`-7op|TB+8a*JuH~PO8&`QqZ2ewU8cMUWqK)Gq2X5$ECR5 zC^hN{DWNw>O?tD`thY!lw%n&B^;W4>Z;*|h$O>#=zlCam1)CF$1 zl%Nt)>QUEPcvS#R=sTL-sC(rodC^%vfZR%CidCXEh0$7U)d5iGrDi+fK07IJ+u$|LBI~sIJBddnUj?$w67iB z>%Q&m4lsVNz{Yz8Chis3bg#hX{|PKb{zz4XI`&2A{z^2fQddds!wy!kT~+IL@52t2 zvFgz(Y6|@|dG-fU~?~s)ojqF`LgB$O?=jMa3v|PVzg)F{dTS1#_CXoKJ9ZfjsT}9A|pTZ=L6PmoAb9*STW~Un+15 z9Js|Rc7a>q>GhoF_TK#kp@x~~S9~-zBlE)E2(iq(u;Qm8xV>Ti{c(W&OY9Db)o8PY z{8@|-fJM7(E;wxKZY`*X7M%ahy5d=)emUj^@=sSwlWX4f0CRD4K?I(2UYw~f2Oh>@ zazY$z^};Ap=DxrvJ}TKY_mgEYy35)9j@Oz!Wjw>c3IVf^oUCba)jiC7f_a`Ly{^Nd z;J(crr<-#sbrVb~L|&_TJeguG?*uXoHJ>||&&nE|(}T$v2!C45A~N7kzB=vhX4#YE zInT|yxaHLHQ&R|Af|{GmV=o!@#uuRsGM4yp(4mKI)ylK8J#tRg9y3%Uqg=vGRPLo* zMKd2TCSwYaIBLq^wpTmJj9ROBzx#rfgg`FnwVe zH9(T(hUqKG5;b)a5?;>XL6|mDR6ks^+wi->yeBda&$SPei@uJ=KMCFqA^et*d{aoi zG+7jq>q2@%NEe0lEppX&{QS0)adZ`h)b3|fNCI&8vsn|qp19Hc`Q|HUwmIGr+7#>G z5>s!Asc*Kei|Gw9T@=%|L^pZC-$8!ii?cni{>|6QCK|x6Uv`E^gUlPj$Y>}3tFF_H zV~xzO8zW==q^0(=>>#;W`;>Us*$d~W=Ez9h)Fbq2{^ct_+4|X+@4WbfJ74?8oZFW5 zOq;s)3Xf?lEZ#7&<({~3`oh`K%mZh~G5rd_DN5|zZBoV*ik zXc}Bk46et9Hey5Tp5YD8a8Vex{hQ-^3o6|w`C_0mec+O=f?8VX0@%w@n-5WvBH_B% z`wsFUJXtUc7#W#LmfIVD7_=4$A55_+k`8`7b&#rGRx{arZc?2pnS2Cvo+R%DQw}>& zZol^3t=E_CT>HY-cfPvy?DE$2?^W^yKL`*g z7kmLj)xh*qwfdw4oX?y15=fqbZqNb;H^s?#6DwlTlUnzrHazqe*iKtwCZo%0E|Zxf zSL@r^50P)xpSmAALCK0v!dpn5ReN+*Q8e@!8Ka(T9vj$2rJ=GEFs7QFgg29cMbaFO zF4lm&4$w7d>~WBi z5GAz0W4b+jf)ZMlDd7QI&T*y@&#a(d-_F2%- z2=+V)YBU3kivqb8ITL9wG6y{550N90p(1m@Lu#X4>?Y}t){|3F=kX$Qbd!0o`2Rd4 zAAR&t7aP9m+GgPIeew>^aN!NMew(9giSV&EJ|;4p<688I==In-8{A-n+Z@LZY%@S8 zI#l6!%edxsHnzdWC~kz}MkubK!ts*d#M)Ue`D^Sj`A6*EN8Dw-(Bp}v1l8)IT3uAj OUuL*TJU$}O8~Pvam^2*# delta 2641 zcma)8X>1!+5Z-%z-$&vj_vM^Tdbd#2CQT1QTO|>#EJ3vKZd^ONc6e)}QWDaZs6`w? z(G#MGqUKjn)d*Ge7ZPeI@Do9P@FQBTAJD=-f)GNggkat#PHX1}dEOb%oA=GUd2eRc z{{6uBJ6s<+oi+-_Ey17t#yRh*Vph5Nw!T4{l4)5`Et)lHHDyt?Y4)Vulr5@5b0(dp zY*k&FJL%RuNsl?Vsb0;O^lAR2Ukl7Hgl4tsWVIGaM$A=*8r5RSm=;gQX^No~I$J|_ zCj(}+3JpAvtuqzC7J+V-CInqynk=%g0<<<{=aOrY0S(aFSohvQonBWcjf*q@fYwH` zNnvHTB2F-}XO1i1DA^2Qh_YAq$$q7IDX_?aCD6KMi_)^tis!|^9>ppL)3h8iIY>+4 zMIN9)Yr~9!SK7+AUPusQ?6$gG+4d4tt~zhW*XURTyoHvkD^CMkc7au}KLVlIfx4BB za<5<&Ew_@ZmSW}iL{s3WhxloYS6J7~Ror%R8(@1$q)kdF+qV&C?NVL&Hm1{cV0f#* zs;vU6w+f8>FL3SP^%Vzi*mUsi8<$j3Gjh`=?2Zj=bA{iQP1v0!>}+cVwrvx3m(nOF zCe3n25okq5-JRSGY2TgQvyq8*#i#fo?H%Ry35~{#n3U)&wgKAq;GJ6!-umd*Px1ZB zAK(Av_I>=2f6_)9;-I7#4xT!Oj|&a^Xu~m>QI%mupUUTSCBYbil1pcDioqR{V9pxc z(UT*i26yV@@Tg%OI(cMl= zYLI;-d+-mo4!^6=Qu07i;L}}b!%=%J?ZNx)9e!`6zd{E&v>V&8O1}GPq1`B9Cmi#}!emu%qFYrlz{3oTR>_ zXY?_797RBj{A9^NmSm|QjYG~*l&E|}1(O#uDMjy2NvX36ijjp-*@6ywxu_}##qm$B z`dNcdsd+H76WBa$6ehB{BXMuKLUE7`MjkhuN=_PA6?v>6>61EY0KIMjR<}M- zBSBi!%mmb#ltVjVN_^>e;KlqKd@fLbSt#Cq1;65MqgU`f_jWwyY4o=*bBV{F6|U>{ ztDbt=;{@Z+dk;nj8mKQDLIZp7XWn!4PQ2ImI)9$+DQ56Je;NmaZToJphy-G=dI1@> zkp zV;dPU&7TISgwKQ$V0tNZ-BW3bd&2?#833j6@o<7C0X)R#!g9UozG4sItRks}vuFfR z8vZ97nKkirPYM(pgZx6VK!3V`y-R+;8Pbc_Sg1JuL@@-~$~VE=x9Z>paGvjx^C=yj z00cC;s6kD_6&ReUC7Z4t`cYK7%)8=HK3_m*$U2Zts_Fu@Do}yW6;F7@*RU#b4q=sA zW4wTx@mE!S^j`dL)l;~?IzkKh`ReLf5zGwl1iXu+sZ2NAr1=8U&2*to;+#}HB7aj1 z8>!h;J~xp`KUSHf2n>-dfd@oRkH{+kkq$?s?U9eLZo){Ln5Askp+_>QejJMQ+FOa1 z6e9}b;Yd%-5fBYto0KyMzVgLf0Syurn^p?wD83Pi4Uz;Sl0Y*n<}Dav5xOjoat4cv zxr#!0Xn8`*lMXw8&Vd#ABcgxJVte$EC%R0PJvbF@@w6{fWe>g{ZO1=GtzFAh=L$8j zytN01V=pw;(ZQ?YDg|GU*dHv#1iz&Ns|-;O;+wHAARl+*O|%XF5^u&$H9xmlD2Bfh fz7&4Ve=#-}TV;3}vI&gXx=IQU?y5b&k$U_Oukk`N diff --git a/app/api/__pycache__/config.cpython-311.pyc b/app/api/__pycache__/config.cpython-311.pyc index 4b1535ffa901b13e6d0018cc128d11fa1963bc8f..1e93bb5f32b7a4e59d6968a6c7f980a59fa9d31c 100644 GIT binary patch delta 2530 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 K_ZitW@BaZJGKum4 delta 7543 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 HfBgJE{A>)9 diff --git a/app/api/config.py b/app/api/config.py index 4c349b6..27dec30 100644 --- a/app/api/config.py +++ b/app/api/config.py @@ -7,7 +7,7 @@ import shutil from pathlib import Path from typing import Optional -from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Body +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Body, Request from fastapi.responses import JSONResponse from sqlalchemy.orm import Session from pydantic import BaseModel @@ -48,12 +48,12 @@ def get_config(): @router.post("/upload") -def upload_config( +async def upload_config( file: Optional[UploadFile] = File(None), - json_config: Optional[dict] = Body(None, embed=False), + request: Request = None, ): """ - 上传品种配置文件(JSON格式)。 + 上传品种配置文件(JSON格式)。 格式示例: { "futures": {"沪银": "AG2606", "沪金": "AU2606"}, @@ -64,12 +64,14 @@ def upload_config( try: if file: - content = file.file.read() + content = await file.read() data = json.loads(content) - elif json_config: - data = json_config else: - raise HTTPException(status_code=400, detail="请提供配置文件或JSON数据") + # 直接从请求体读取 JSON + body = await request.body() + if not body: + raise HTTPException(status_code=400, detail="请提供配置文件或JSON数据") + data = json.loads(body) if not isinstance(data, dict): raise HTTPException(status_code=400, detail="配置文件必须是 JSON 对象") diff --git a/app/api/futures_analysis.py b/app/api/futures_analysis.py index 24d4fc0..72db2e8 100644 --- a/app/api/futures_analysis.py +++ b/app/api/futures_analysis.py @@ -44,6 +44,9 @@ def get_futures_list(db: Session = Depends(get_db)): for name, symbol_code in futures_config.items(): cached = get_cached_data(db, symbol_code, "futures") if cached and cached.get("timeframes"): + # 优先使用缓存中的 current_price 字段 + current_price = cached.get("current_price") + all_candles = [] for period, candles in cached.get("timeframes", {}).items(): all_candles.extend(candles) @@ -55,22 +58,26 @@ def get_futures_list(db: Session = Depends(get_db)): high_price = float(latest_candle.get("high", 0)) low_price = float(latest_candle.get("low", 0)) - change = close_price - open_price + # 如果 current_price 为空,则使用收盘价 + if not current_price: + current_price = close_price + + change = current_price - open_price change_pct = (change / open_price * 100) if open_price > 0 else 0 futures_data.append({ "symbol": symbol_code, "name": name, - "price": close_price, + "price": current_price, "change": round(change, 2), "changePct": round(change_pct, 2), - "suggestion": _get_suggestion(close_price, open_price, change_pct), + "suggestion": _get_suggestion(current_price, open_price, change_pct), "suggestionType": "up" if change >= 0 else "down", "periods": _get_period_trends(all_candles), "successRate": _calc_success_rate(all_candles), "trendScore": _calc_trend_score(all_candles), - "resistance": round(2 * ((high_price + low_price + close_price) / 3) - low_price, 2), - "support": round(2 * ((high_price + low_price + close_price) / 3) - high_price, 2), + "resistance": round(2 * ((high_price + low_price + current_price) / 3) - low_price, 2), + "support": round(2 * ((high_price + low_price + current_price) / 3) - high_price, 2), "open": open_price, "high": high_price, "low": low_price, @@ -106,37 +113,46 @@ def get_futures_detail(symbol: str, db: Session = Depends(get_db)): if not cached: raise HTTPException(status_code=404, detail=f"未找到 {symbol} 的缓存数据") + # 优先使用缓存中的 current_price 字段 + current_price = cached.get("current_price") + + # 从所有K线数据中获取最新的K线用于计算指标 all_candles = [] for period, candles in cached.get("timeframes", {}).items(): all_candles.extend(candles) if not all_candles: raise HTTPException(status_code=404, detail=f"未找到 {symbol} 的K线数据") - + + # 如果 current_price 为空,则从K线数据中获取最新收盘价 + if not current_price: + current_price = float(all_candles[-1].get("close", 0)) + + # 使用最新一条K线数据计算指标 latest_candle = all_candles[-1] open_price = float(latest_candle.get("open", 0)) - close_price = float(latest_candle.get("close", 0)) + close_price = float(latest_candle.get("close", current_price)) high_price = float(latest_candle.get("high", 0)) low_price = float(latest_candle.get("low", 0)) - change = close_price - open_price + change = current_price - open_price change_pct = (change / open_price * 100) if open_price > 0 else 0 # Pivot Point 公式计算关键点位 - pp = (high_price + low_price + close_price) / 3 + pp = (high_price + low_price + current_price) / 3 r1 = round(2 * pp - low_price, 2) r2 = round(pp + (high_price - low_price), 2) s1 = round(2 * pp - high_price, 2) s2 = round(pp - (high_price - low_price), 2) - suggestion = _get_suggestion(close_price, open_price, change_pct) + suggestion = _get_suggestion(current_price, open_price, change_pct) suggestion_type = "up" if change >= 0 else "down" trend_score = _calc_trend_score(all_candles) data = { "symbol": symbol, "name": _get_futures_name(symbol), - "price": close_price, + "price": current_price, "change": round(change, 2), "changePct": round(change_pct, 2), "suggestion": suggestion, @@ -146,7 +162,7 @@ def get_futures_detail(symbol: str, db: Session = Depends(get_db)): "high": high_price, "low": low_price, "volume": sum(float(c.get("volume", 0)) for c in all_candles), - "entryPrice": round(close_price * 0.995, 2) if change >= 0 else round(close_price * 1.005, 2), + "entryPrice": round(current_price * 0.995, 2) if change >= 0 else round(current_price * 1.005, 2), "targetPrice": r1 if change >= 0 else s1, "stopLoss": s1 if change >= 0 else r1, "riskLevel": "低" if trend_score >= 80 else "中" if trend_score >= 60 else "高", diff --git a/app/services/__pycache__/cache.cpython-311.pyc b/app/services/__pycache__/cache.cpython-311.pyc index 1eccd6d84dcc236255034358f3a8ff93895b4068..5333b966f4bc8f61e3535cd23878cd3abda5bf1e 100644 GIT binary patch delta 23 dcmbQ3KQW(gIWI340}$NI70G(6w2`mb8~{{62OIzZ delta 23 dcmbQ3KQW(gIWI340}$9Y31%s)Y~*V;2LMV}1=Iil diff --git a/app/static/index.html b/app/static/index.html index 9ac262e..6c155fb 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -1176,6 +1176,42 @@ + + +