From 82b8f859d889664a2ca39ea86ca7209dbda8e48d Mon Sep 17 00:00:00 2001 From: Lxy Date: Thu, 21 May 2026 01:05:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=8E=8B=E5=8A=9B?= =?UTF-8?q?=E6=94=AF=E6=92=91=E8=AE=A1=E7=AE=97=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../futures_analysis.cpython-311.pyc | Bin 39472 -> 39467 bytes app/api/futures_analysis.py | 25 +++++++++--------- app/static/futures_analysis.css | 23 ++++++++++++++++ app/static/futures_analysis.html | 7 +++-- app/static/futures_analysis.js | 14 ++++++++-- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/app/api/__pycache__/futures_analysis.cpython-311.pyc b/app/api/__pycache__/futures_analysis.cpython-311.pyc index 8c155c097be6c794dc0e01f2d8c164ccb42604a2..0050bd32a7866133d8a0bc0db1414915fe12f69a 100644 GIT binary patch delta 3918 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 F>wmETBa{FD delta 4015 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 diff --git a/app/api/futures_analysis.py b/app/api/futures_analysis.py index 6e9653a..1de767e 100644 --- a/app/api/futures_analysis.py +++ b/app/api/futures_analysis.py @@ -67,8 +67,8 @@ def get_futures_list(db: Session = Depends(get_db)): "periods": _get_period_trends(all_candles), "successRate": _calc_success_rate(all_candles), "trendScore": _calc_trend_score(all_candles), - "resistance": round(high_price * 1.02, 2), - "support": round(low_price * 0.98, 2), + "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), "open": open_price, "high": high_price, "low": low_price, @@ -120,12 +120,12 @@ def get_futures_detail(symbol: str, db: Session = Depends(get_db)): change = close_price - open_price change_pct = (change / open_price * 100) if open_price > 0 else 0 - resistance1 = round(high_price * 1.01, 2) - resistance2 = round(high_price * 1.03, 2) - resistance3 = round(high_price * 1.05, 2) - support1 = round(low_price * 0.99, 2) - support2 = round(low_price * 0.97, 2) - support3 = round(low_price * 0.95, 2) + # Pivot Point 公式计算关键点位 + pp = (high_price + low_price + close_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_type = "up" if change >= 0 else "down" @@ -145,15 +145,16 @@ def get_futures_detail(symbol: str, db: Session = Depends(get_db)): "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), - "targetPrice": resistance1 if change >= 0 else support1, - "stopLoss": support1 if change >= 0 else resistance1, + "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 "高", "macd": _calc_macd(all_candles), "rsi": _calc_rsi(all_candles), "boll": _calc_boll(all_candles), "kdj": _calc_kdj(all_candles), - "resistances": [resistance1, resistance2, resistance3], - "supports": [support1, support2, support3], + "resistances": [r1, r2], + "supports": [s1, s2], + "pivotPoint": round(pp, 2), "periodConsistency": _get_period_trends(all_candles) } diff --git a/app/static/futures_analysis.css b/app/static/futures_analysis.css index ebe543c..b07bfe5 100644 --- a/app/static/futures_analysis.css +++ b/app/static/futures_analysis.css @@ -1117,6 +1117,24 @@ body { font-variant-numeric: tabular-nums; } +.level-item.pivot-point { + background: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.2); + border-radius: 8px; + padding: 8px 12px; + margin: 4px 0; +} + +.level-item.pivot-point span:first-child { + color: var(--purple); + font-weight: 600; +} + +.level-item.pivot-point span:last-child { + color: var(--purple); + font-size: 14px; +} + .level-divider { height: 1px; background: var(--border-color); @@ -1517,6 +1535,11 @@ body.theme-minimal .level-item { border-radius: 9999px; } +body.theme-minimal .level-item.pivot-point { + background: rgba(124, 58, 237, 0.08); + border-color: rgba(124, 58, 237, 0.2); +} + body.theme-minimal .trend-row { background: var(--bg-card); border-radius: 9999px; diff --git a/app/static/futures_analysis.html b/app/static/futures_analysis.html index d0d1628..4a33e53 100644 --- a/app/static/futures_analysis.html +++ b/app/static/futures_analysis.html @@ -321,14 +321,17 @@ 压力
R1--
R2--
-
R3--
+ +
+
+ 中枢 (PP) + --
支撑
S1--
S2--
-
S3--
diff --git a/app/static/futures_analysis.js b/app/static/futures_analysis.js index ce31b65..877740b 100644 --- a/app/static/futures_analysis.js +++ b/app/static/futures_analysis.js @@ -461,7 +461,7 @@ function updateDetailView(data) { } if (data.resistances) { - for (let i = 0; i < 3; i++) { + for (let i = 0; i < 2; i++) { const el = document.getElementById(`resistance-${i + 1}`); if (el) { el.querySelector('span:last-child').textContent = formatNumber(data.resistances[i]); @@ -469,7 +469,7 @@ function updateDetailView(data) { } } if (data.supports) { - for (let i = 0; i < 3; i++) { + for (let i = 0; i < 2; i++) { const el = document.getElementById(`support-${i + 1}`); if (el) { el.querySelector('span:last-child').textContent = formatNumber(data.supports[i]); @@ -477,6 +477,10 @@ function updateDetailView(data) { } } + if (data.pivotPoint) { + document.getElementById('pivot-point').querySelector('span:last-child').textContent = formatNumber(data.pivotPoint); + } + if (data.periodConsistency) { const container = document.getElementById('period-trends'); const periodNames = { '5': '5分钟', '15': '15分钟', '30': '30分钟', '60': '60分钟' }; @@ -630,6 +634,12 @@ function showHistoryModal(record) { ${formatNumber(v)} `).join('')} + ${record.pivot_point ? ` + + ` : ''} ${(record.support_levels || []).map((v, i) => `