Chartmuster grafisch: SVG-Skizze in Karte + Trigger/Ziel-Linien im PWA-Chart

- patterns.py: nur AKTIVE, kursnahe Muster (abgearbeitete/Ziel erreicht raus,
  _MAX_DIST_ATR=5.0 Distanz-Filter)
- get_bars liefert bei M30 die Muster (patterns.snapshot) fürs Chart-Overlay
- app.js: _patternSvg-Skizze je Muster-Karte + 2 stärkste Muster als
  Trigger-/Ziel-Preislinien im M30-Chart; Gitternetz dezenter (#10151c)
- style.css: .pt-svg; index.html v=116->117; CLAUDE.md aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-27 19:13:47 +02:00
co-authored by Claude Opus 4.8
parent 21d855b163
commit ae6bbb2f35
6 changed files with 78 additions and 10 deletions
+37 -2
View File
@@ -225,6 +225,26 @@ function renderCircuitBreaker(cb) {
}
// Chartmuster (visuell) — reine Anzeige. Status: bildet sich / bestätigt / (ungültig raus).
// Kompakte SVG-Skizze der Muster-Form (viewBox 60×32). Rein schematisch, damit die
// Form auf einen Blick erkennbar ist; Farbe = Richtung (grün bull / rot bear / grau).
function _patternSvg(type, col) {
const P = {
double_top: "M2,26 L14,8 L22,16 L34,8 L46,26",
double_bottom: "M2,6 L14,24 L22,16 L34,24 L46,6",
hs: "M2,24 L12,14 L18,20 L28,4 L38,20 L44,14 L54,24",
inv_hs: "M2,8 L12,18 L18,12 L28,28 L38,12 L44,18 L54,8",
cup: "M2,6 C2,30 44,30 44,6 M44,6 L54,12",
inv_cup: "M2,26 C2,2 44,2 44,26 M44,26 L54,20",
tri_asc: "M2,26 L52,26 M2,4 L52,22",
tri_desc: "M2,6 L52,6 M2,28 L52,10",
tri_sym: "M2,4 L52,16 M2,28 L52,16",
};
const d = P[type] || "M2,16 L54,16";
return `<svg class="pt-svg" viewBox="0 0 56 32" width="56" height="32" aria-hidden="true">
<path d="${d}" fill="none" stroke="${col}" stroke-width="2"
stroke-linejoin="round" stroke-linecap="round"/></svg>`;
}
function renderPatterns(pt) {
const el = $("pt-list");
if (!el) return;
@@ -235,6 +255,7 @@ function renderPatterns(pt) {
}
el.innerHTML = items.map(m => {
const dirCls = m.dir === "bull" ? "up" : m.dir === "bear" ? "down" : "dim";
const col = m.dir === "bull" ? "#3fb950" : m.dir === "bear" ? "#f85149" : "#8b949e";
const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
const st = m.status === "confirmed"
? `<span class="pt-badge conf">bestätigt</span>`
@@ -243,7 +264,8 @@ function renderPatterns(pt) {
? `Ziel <b>${fmt(m.target, 2)}</b>` : `Ausbruch offen`;
const q = Math.round((m.quality || 0) * 100);
return `<div class="pt-item ${dirCls}">
<div class="pt-head"><span class="pt-name">${arr} ${m.name}</span>${st}
<div class="pt-head">${_patternSvg(m.type, col)}
<span class="pt-name">${arr} ${m.name}</span>${st}
<span class="pt-q">Güte ${q}%</span></div>
<div class="pt-det">Trigger <b>${fmt(m.trigger, 2)}</b> · ${tgt}
· Höhe ${m.height_atr}×ATR · vor ${m.bars_ago} Bars</div>
@@ -1161,7 +1183,7 @@ function _makeChart(tf) {
const chart = LightweightCharts.createChart(el, {
width: el.clientWidth || 320, height: 200,
layout: { background: { color: "#0d1117" }, textColor: "#8b949e", fontSize: 15 },
grid: { vertLines: { color: "#161b22" }, horzLines: { color: "#161b22" } },
grid: { vertLines: { color: "#10151c" }, horzLines: { color: "#10151c" } },
timeScale: { borderColor: "#30363d", timeVisible: true, secondsVisible: false },
rightPriceScale: { borderColor: "#30363d" },
crosshair: { mode: LightweightCharts.CrosshairMode.Normal },
@@ -1223,6 +1245,19 @@ function renderChart(tf, d) {
(LV.zones || []).forEach(z => {
addLine(z.hi, "#1f6feb", 1, "Zone"); addLine(z.lo, "#1f6feb", 1, "");
});
// Chartmuster-Overlay (nur M30): Trigger = kräftige Linie (grün bull / rot bear),
// Ziel = gepunktete Linie gleicher Farbe. Nur die 2 stärksten, sonst wird's voll.
(d.patterns || []).slice(0, 2).forEach(p => {
const bull = p.dir === "bull";
const col = bull ? "#3fb950" : (p.dir === "bear" ? "#f85149" : "#8b949e");
c.lines.push(c.candle.createPriceLine({
price: p.trigger, color: col, lineWidth: 2, lineStyle: 0,
axisLabelVisible: true, title: (p.status === "confirmed" ? "▶ " : "◌ ") + p.name }));
if (p.target != null)
c.lines.push(c.candle.createPriceLine({
price: p.target, color: col, lineWidth: 1, lineStyle: 1,
axisLabelVisible: true, title: "Ziel" }));
});
c.chart.timeScale().fitContent();
const tr = document.getElementById("ctr-" + tf);
const [txt, cls] = _TREND_TXT[d.trend] || ["—", "flat"];