fix: ECR-004 威科夫区间评分硬化与 VP 绘图减负(已审)
评分选 TR、阶段最小跨度、elements_only 门闩、Top-8 VP;无币种独立参数。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+5
-4
@@ -656,18 +656,19 @@ def analyze():
|
||||
else:
|
||||
result['structure_zones'] = []
|
||||
|
||||
# 威科夫分析 —— 按需:include_wyckoff=1
|
||||
# 威科夫分析 —— 按需:include_wyckoff=1,且须有主周期分析(非 elements_only)
|
||||
include_wyckoff_param = request.args.get('include_wyckoff', '')
|
||||
include_wyckoff = str(include_wyckoff_param).lower() in ('1', 'true', 'yes')
|
||||
if include_wyckoff:
|
||||
if include_wyckoff and not elements_only:
|
||||
try:
|
||||
from chanlun.analysis.wyckoff import analyze_wyckoff
|
||||
wyckoff_lookback = int(request.args.get('wyckoff_lookback', 120))
|
||||
wyckoff_bins = int(request.args.get('wyckoff_vp_bins', 50))
|
||||
# ECR-004:默认/上限 24 bins(A+C)
|
||||
wyckoff_bins = int(request.args.get('wyckoff_vp_bins', 24))
|
||||
result['wyckoff'] = analyze_wyckoff(
|
||||
df,
|
||||
lookback=max(40, min(wyckoff_lookback, 500)),
|
||||
vp_bins=max(10, min(wyckoff_bins, 100)),
|
||||
vp_bins=max(10, min(wyckoff_bins, 24)),
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Wyckoff 分析出错: {e}")
|
||||
|
||||
@@ -2222,7 +2222,8 @@ function initTradingView(symbol, timeframe) {
|
||||
if (!isNaN(t0) && !isNaN(t1) && !isNaN(hi) && !isNaN(lo)) {
|
||||
const fill = 'rgba(52, 152, 219, 0.07)';
|
||||
const border = 'rgba(52, 152, 219, 0.75)';
|
||||
const fillLines = 6;
|
||||
// ECR-004:填充线 6→3,减 series
|
||||
const fillLines = 3;
|
||||
const step = (hi - lo) / (fillLines + 1);
|
||||
for (let fi = 1; fi <= fillLines; fi++) {
|
||||
const fy = lo + step * fi;
|
||||
@@ -2320,20 +2321,29 @@ function initTradingView(symbol, timeframe) {
|
||||
const t1 = tr.end_time ? parseTs(tr.end_time) : chartEnd;
|
||||
if (!isNaN(t1)) {
|
||||
const bins = vp.bins || [];
|
||||
// ECR-004 A+C:只画有量 Top-N,避免每 bin 一条 series
|
||||
const TOP_N = 8;
|
||||
const ranked = bins
|
||||
.filter(function(b) { return b && b.volume > 0; })
|
||||
.slice()
|
||||
.sort(function(a, b) { return b.volume - a.volume; })
|
||||
.slice(0, TOP_N);
|
||||
let maxVol = 0;
|
||||
bins.forEach(function(b) { if (b.volume > maxVol) maxVol = b.volume; });
|
||||
const maxWidthSec = Math.max(60, Math.floor((t1 - parseTs(tr.start_time)) * 0.15));
|
||||
bins.forEach(function(b) {
|
||||
ranked.forEach(function(b) { if (b.volume > maxVol) maxVol = b.volume; });
|
||||
const tStart = parseTs(tr.start_time);
|
||||
const maxWidthSec = Math.max(60, Math.floor((t1 - (isNaN(tStart) ? t1 : tStart)) * 0.15));
|
||||
ranked.forEach(function(b) {
|
||||
if (!b.volume || maxVol <= 0) return;
|
||||
const wSec = Math.max(1, Math.floor(maxWidthSec * (b.volume / maxVol)));
|
||||
const alpha = 0.15 + 0.55 * (b.volume / maxVol);
|
||||
const alpha = 0.2 + 0.55 * (b.volume / maxVol);
|
||||
const leftT = Math.max(isNaN(tStart) ? (t1 - wSec) : tStart, t1 - wSec);
|
||||
mainChart.addLineSeries({
|
||||
color: 'rgba(142, 68, 173, ' + alpha.toFixed(2) + ')',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false
|
||||
}).setData([
|
||||
{ time: t1 - wSec, value: b.price },
|
||||
{ time: leftT, value: b.price },
|
||||
{ time: t1, value: b.price }
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -157,3 +157,32 @@ def test_analyze_http_wyckoff_opt_in():
|
||||
w = payload["wyckoff"]
|
||||
for k in WYCKOFF_KEYS:
|
||||
assert k in w, f"missing wyckoff key: {k}"
|
||||
|
||||
|
||||
def test_analyze_http_wyckoff_skipped_when_elements_only():
|
||||
"""elements_only=true 时即使 include_wyckoff=1 也不返回 wyckoff。"""
|
||||
from app import app
|
||||
from services.runtime import add_indicators
|
||||
|
||||
df = add_indicators(make_ohlcv(300))
|
||||
df = df.copy()
|
||||
if "timestamp" not in df.columns:
|
||||
df["timestamp"] = (pd.to_datetime(df["date"]).astype("int64") // 10**6).astype("int64")
|
||||
|
||||
with patch("api.analyze.get_kl_data", return_value=df):
|
||||
client = app.test_client()
|
||||
resp = client.get(
|
||||
"/api/analyze",
|
||||
query_string={
|
||||
"symbol": "BTC/USDT:USDT",
|
||||
"timeframe": "5m",
|
||||
"element_timeframe": "1m",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"elements_only": "true",
|
||||
"include_wyckoff": 1,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200, resp.data[:500]
|
||||
payload = resp.get_json()
|
||||
assert payload is not None
|
||||
assert "wyckoff" not in payload
|
||||
|
||||
Reference in New Issue
Block a user