Breakout-Karte vorbereitet (inaktiv bis Neustart) + Bewertung der Margin-Idee
User-Idee: 40 % Margin manuell, 50 % fuer Breakout reservieren, neue Karte. Bewertung: (1) 40 % ist der groesste gemessene Hebel (MaxDD 82,8 %) und ein Schalter ohne Neustart. (2) Reserve ist technisch moeglich (Konto ist RETAIL_HEDGING), aber _check_auto_squeeze feuert 'nur FLAT' und engine.py hat 17 Einzel-Positions-Annahmen -- Kern-Umbau, kein Parameter. Zudem steht die Zuteilung gegen die Messung: autonom -4,71 EUR/Lot gegen Mensch +1,56 EUR/Lot. Live-Befund macht (1) dringlich: freie Margin -6,21 EUR, Margin-Level 99,3 %. Es gibt derzeit nichts zu reservieren. Gebaut (inaktiv): history.fetch_squeeze_since, engine._squeeze_b5_cached (120 s gecacht), Snapshot-Feld squeeze_b5, Karte #card-brk. Eigenes Fenster ab 01.08. -- squeeze_monitor.live rechnet ueber alle 42 Trades, B5 zaehlt 11. Die Karte zeigt beide Zahlen, weil der Monitor 'ontrack' aus der MECHANIK meldet, waehrend live Verhaeltnis 0,14 / PF 0,39 steht. Live aendert sich nichts: Karte ist hidden, renderBrk blendet sie aus solange squeeze_b5 fehlt. Aktiv nach dem naechsten Neustart (wartet auf den Trade). BEINAHE-SCHADEN: ein UnicodeEncodeError liess web/app.js mit 0 Bytes zurueck. node --check bestand trotzdem -- eine leere Datei ist gueltiges JS. Gefangen hat es erst der Groessenvergleich. Neue Regel: erst .tmp schreiben, Groesse pruefen, dann os.replace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3affe51030
commit
30cb77b795
@@ -7434,6 +7434,47 @@ vollständig.
|
|||||||
⚠ **Verifikation der Zustellung steht aus** — sie erfolgt mit dem Report am
|
⚠ **Verifikation der Zustellung steht aus** — sie erfolgt mit dem Report am
|
||||||
nächsten Morgen um 07:30. Die Bausteine sind einzeln live gegengeprüft.
|
nächsten Morgen um 07:30. Die Bausteine sind einzeln live gegengeprüft.
|
||||||
|
|
||||||
|
## ⚙ BREAKOUT-KARTE VORBEREITET — aktiv erst nach dem nächsten Neustart (2026-08-19, v=176)
|
||||||
|
|
||||||
|
User-Idee: (1) manuell fest 40 % Margin, (2) 50 % für Breakout reservieren,
|
||||||
|
(3) neue Dashboard-Karte. **Bewertung:**
|
||||||
|
| Punkt | Urteil |
|
||||||
|
|---|---|
|
||||||
|
| ① 40 % fest | ✅ grösster gemessener Hebel (MaxDD **82,8 %**, schlechtester Trade −381 €). **Ein Schalter, kein Umbau** — Feld „Einsatz %“, ohne Neustart, nur auf neue Positionen. |
|
||||||
|
| ② 50 % Reserve | ⚠ Konto ist **RETAIL_HEDGING**, also technisch möglich — aber: `_check_auto_squeeze` feuert **„nur FLAT“**, dazu **17 Einzel-Positions-Annahmen** in `engine.py` (Trailing, Notfall-Stop, S/R-Close, Time-Stop, Breaker verwalten je EINE Position). Kern-Umbau, kein Parameter. |
|
||||||
|
| ③ Karte | ✅ gebaut, s. u. |
|
||||||
|
|
||||||
|
⚠⚠ **Der Live-Befund macht ① dringlich:** Balance 937,40 € · Equity 877,41 € ·
|
||||||
|
Margin belegt 883,62 € → **freie Margin −6,21 €, Margin-Level 99,3 %**. EINE
|
||||||
|
Position (1,19 Lot) frisst das ganze Konto. **Es gibt derzeit nichts zu
|
||||||
|
reservieren** — die Reserve aus ② entsteht überhaupt erst durch ①.
|
||||||
|
⚠⚠ **Und die Zuteilung steht gegen die Messung:** seit 01.08. je Lot
|
||||||
|
**autonom −4,71 €** (n=11) gegen **Mensch +1,56 €** (n=185). ② gäbe dem
|
||||||
|
gemessen schlechteren Pfad den grösseren Anteil; 40+50 lassen zudem nur 10 %
|
||||||
|
Puffer. **Empfehlung: ① und ③ jetzt, ② erst wenn freie Margin existiert UND
|
||||||
|
der autonome Pfad auf einem NEU vorab fixierten Fenster nicht negativ ist.**
|
||||||
|
|
||||||
|
**Gebaut (inaktiv):** `history.fetch_squeeze_since` · `engine._squeeze_b5_cached`
|
||||||
|
(~120 s gecacht, Muster `_alignment_cached`) · Snapshot-Feld `squeeze_b5` ·
|
||||||
|
Karte `#card-brk` (Zustand · Squeeze jetzt · liegende Orders · **B5-Stand**).
|
||||||
|
⚠ **Eigenes Fenster ab 01.08.** — `squeeze_monitor.live` rechnet über **alle 42**
|
||||||
|
Trades, B5 zählt **11**. Beide ungetrennt zu zeigen wäre dieselbe stille
|
||||||
|
Fenster-Verwechslung wie bei `pbreak_accuracy_v2`.
|
||||||
|
⚠ Die Karte nennt beide Zahlen: der Monitor meldet **`ontrack`** aus der
|
||||||
|
**Mechanik** (ØR +0,163 / PF 1,24), während live **Verhältnis 0,14 / PF 0,39**
|
||||||
|
steht. Die Lücke IST der B4-Befund — eine der beiden allein wäre irreführend.
|
||||||
|
✅ **Live ändert sich nichts:** die Karte ist `hidden` und `renderBrk` blendet
|
||||||
|
sie aus, solange `squeeze_b5` fehlt. Aktiv nach dem nächsten Neustart — der
|
||||||
|
wartet auf User-Wunsch, bis die offene Position zu ist.
|
||||||
|
|
||||||
|
⚠⚠ **BEINAHE-SCHADEN, dokumentiert weil es eine Fehlerklasse ist:** ein
|
||||||
|
`UnicodeEncodeError` (Emoji-Escapes) brach mitten im Schreiben ab und liess
|
||||||
|
**`web/app.js` mit 0 Bytes** zurück — `io.open(p,"w")` kürzt VOR dem Schreiben.
|
||||||
|
⚠ **`node --check` bestand trotzdem: eine LEERE Datei ist gültiges JS.** Die
|
||||||
|
Syntaxprüfung allein hätte den Totalverlust durchgewinkt; gefangen hat ihn erst
|
||||||
|
der Grössenvergleich. **Regel: Datei erst in `.tmp` schreiben, Grösse gegen das
|
||||||
|
Original prüfen, dann `os.replace`** — so ist es jetzt gemacht.
|
||||||
|
|
||||||
## ✅ BRK BLEIBT AN (User 2026-08-19) — und die 13-tägige Flaute war kein Defekt
|
## ✅ BRK BLEIBT AN (User 2026-08-19) — und die 13-tägige Flaute war kein Defekt
|
||||||
|
|
||||||
User: „Breakout-Squeeze soll an bleiben. Rechne und überprüfe erneut.“
|
User: „Breakout-Squeeze soll an bleiben. Rechne und überprüfe erneut.“
|
||||||
|
|||||||
@@ -3248,6 +3248,48 @@ class TradingEngine:
|
|||||||
self._align_ts = now
|
self._align_ts = now
|
||||||
return getattr(self, "_align_cache", None)
|
return getattr(self, "_align_cache", None)
|
||||||
|
|
||||||
|
def _squeeze_b5_cached(self) -> dict | None:
|
||||||
|
"""B5-Stand des Auto-Squeeze fuer die Breakout-Karte (2026-08-19).
|
||||||
|
|
||||||
|
⚠⚠ WARUM EIN EIGENES FELD und nicht `squeeze_monitor.live`: der Monitor
|
||||||
|
rechnet ueber ALLE SQUEEZE_*-Trades (n=42). Die B5-Abbruchregel zaehlt
|
||||||
|
aber ausdruecklich **ab dem 01.08.** (n=11). Beide Zahlen im selben Bild
|
||||||
|
zu zeigen, ohne sie zu trennen, waere genau die Sorte stille
|
||||||
|
Fenster-Verwechslung, an der `pbreak_accuracy_v2` und die
|
||||||
|
Verdict-Faelligkeit schon aufgelaufen sind.
|
||||||
|
|
||||||
|
⚠ Der Monitor meldet zudem `verdict: ontrack` aus der **Mechanik**
|
||||||
|
(Regel neu gerechnet, OeR +0,163 / PF 1,24) — waehrend die LIVE-Bilanz
|
||||||
|
im selben Moment bei Verhaeltnis 0,14 / PF 0,39 steht. Genau diese
|
||||||
|
Luecke ist der B4-Befund; die Karte muss beide Zahlen nebeneinander
|
||||||
|
zeigen, sonst liest sich 'ontrack' wie eine Entwarnung.
|
||||||
|
|
||||||
|
DB-Query ~120 s gecacht (Muster wie `_alignment_cached`)."""
|
||||||
|
now = time.time()
|
||||||
|
if now - getattr(self, "_b5_ts", 0.0) > 120:
|
||||||
|
self._b5_cache = None
|
||||||
|
try:
|
||||||
|
ab = int(datetime(2026, 8, 1, tzinfo=_BERLIN).timestamp())
|
||||||
|
rows = self.history.fetch_squeeze_since(ab)
|
||||||
|
g = [x for x in rows if x > 0]
|
||||||
|
v = [x for x in rows if x <= 0]
|
||||||
|
og = (sum(g) / len(g)) if g else 0.0
|
||||||
|
ov = abs(sum(v) / len(v)) if v else 0.0
|
||||||
|
self._b5_cache = {
|
||||||
|
"n": len(rows), "ziel": 20,
|
||||||
|
"wins": len(g), "losses": len(v),
|
||||||
|
"avg_win": round(og, 2), "avg_loss": round(-ov, 2),
|
||||||
|
# die beiden Groessen, an denen die Regel haengt
|
||||||
|
"ratio": round(og / ov, 2) if ov else None,
|
||||||
|
"pf": round(sum(g) / abs(sum(v)), 2) if v and sum(v) else None,
|
||||||
|
"sum_eur": round(sum(rows), 2),
|
||||||
|
"latte": 1.0,
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
log.debug(f"squeeze_b5: {e}")
|
||||||
|
self._b5_ts = now
|
||||||
|
return getattr(self, "_b5_cache", None)
|
||||||
|
|
||||||
def _hl_ctx_refresh(self):
|
def _hl_ctx_refresh(self):
|
||||||
"""Holt den HL-Kontext im Hintergrund (s. Aufrufstelle im Trend-Loop)."""
|
"""Holt den HL-Kontext im Hintergrund (s. Aufrufstelle im Trend-Loop)."""
|
||||||
try:
|
try:
|
||||||
@@ -4231,6 +4273,10 @@ class TradingEngine:
|
|||||||
# kostet ungenutzt nichts (sie laeuft nur beim Aufruf).
|
# kostet ungenutzt nichts (sie laeuft nur beim Aufruf).
|
||||||
"hl_ctx": getattr(self, "_hl_ctx", None),
|
"hl_ctx": getattr(self, "_hl_ctx", None),
|
||||||
"rec_track": self._rec_track_cached(),
|
"rec_track": self._rec_track_cached(),
|
||||||
|
# B5-Stand des Auto-Squeeze fuer die Breakout-Karte (2026-08-19).
|
||||||
|
# ⚠ Eigenes Fenster (ab 01.08.) — NICHT dasselbe wie
|
||||||
|
# `squeeze_monitor.live` (alle Trades). s. `_squeeze_b5_cached`.
|
||||||
|
"squeeze_b5": self._squeeze_b5_cached(),
|
||||||
# Kennzahlen der MANUELLEN Trades für die gleichnamige Karte
|
# Kennzahlen der MANUELLEN Trades für die gleichnamige Karte
|
||||||
# (2026-08-04). Rein deskriptiv; ~120 s gecacht, damit der heiße
|
# (2026-08-04). Rein deskriptiv; ~120 s gecacht, damit der heiße
|
||||||
# Snapshot-Pfad nicht je Abruf die DB aggregiert.
|
# Snapshot-Pfad nicht je Abruf die DB aggregiert.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -855,6 +855,32 @@ class HistoryLogger:
|
|||||||
"pct": round(100.0 * (r["c"] or 0) / (r["n"] or 1), 1)}
|
"pct": round(100.0 * (r["c"] or 0) / (r["n"] or 1), 1)}
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def fetch_squeeze_since(self, ab: int) -> list:
|
||||||
|
"""P&L aller geschlossenen SQUEEZE_*-Trades ab Epoch `ab` (2026-08-19).
|
||||||
|
|
||||||
|
⚠ Bewusst NUR `setup LIKE 'SQUEEZE%'` — die `setup`-Spalte taugt NICHT
|
||||||
|
als allgemeine Bot/Mensch-Trennung: `engine._open` schreibt die
|
||||||
|
Wellenlage auch bei MANUELLEN Trades mit (`WAVE`, n=124). Nur
|
||||||
|
`SQUEEZE_*` ist autonom eroeffnet. Ein erster Auswertungsversuch am
|
||||||
|
13.08. hat genau das verwechselt und dem Bot 152 Trades zugeschrieben.
|
||||||
|
|
||||||
|
⚠ NICHT je Lot normiert: die B5-Regel ist ausdruecklich auf
|
||||||
|
OeGewinn/OeVerlust in EURO fixiert worden. Sie hier still auf eine
|
||||||
|
andere Einheit umzustellen waere ein nachtraegliches Verschieben der
|
||||||
|
Latte.
|
||||||
|
|
||||||
|
Fail-open (leere Liste) — `core/history.py` hat bewusst keinen Logger.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with self._connect() as c:
|
||||||
|
return [float(r[0]) for r in c.execute(
|
||||||
|
"SELECT pnl FROM trades "
|
||||||
|
" WHERE setup LIKE 'SQUEEZE%' AND exit_time IS NOT NULL "
|
||||||
|
" AND pnl IS NOT NULL AND entry_time > ? "
|
||||||
|
" ORDER BY entry_time", (int(ab),))]
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
def alignment_stats(self, last_n: int = 0) -> dict:
|
def alignment_stats(self, last_n: int = 0) -> dict:
|
||||||
"""Ausrichtungs-Split: liefen die Trades MIT, GEGEN oder OHNE die Empfehlung?
|
"""Ausrichtungs-Split: liefen die Trades MIT, GEGEN oder OHNE die Empfehlung?
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+53
@@ -153,6 +153,58 @@ function beepRecommendation(sig, isNew) {
|
|||||||
// muss jedes Gate EINZELN passieren. Ein Gate, das fällt, lässt sich nicht
|
// muss jedes Gate EINZELN passieren. Ein Gate, das fällt, lässt sich nicht
|
||||||
// durch ein anderes ausgleichen.
|
// durch ein anderes ausgleichen.
|
||||||
// ══════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════
|
||||||
|
// Breakout-Autotrader-Karte (2026-08-19). Nur ANZEIGE aus vorhandenen
|
||||||
|
// Snapshot-Feldern -- hier wird nichts neu gerechnet.
|
||||||
|
// Die Karte bleibt VERSTECKT, solange `squeeze_b5` fehlt (aelterer Server, das
|
||||||
|
// Feld gibt es erst nach einem Neustart). Eine halb gefuellte Karte waere
|
||||||
|
// schlimmer als keine: sie saehe aus wie ein Zustand statt wie eine fehlende
|
||||||
|
// Quelle.
|
||||||
|
function renderBrk(d) {
|
||||||
|
const box = $streng("card-brk");
|
||||||
|
if (!box) return;
|
||||||
|
const b5 = d.squeeze_b5;
|
||||||
|
if (!b5) { box.classList.add("hidden"); return; }
|
||||||
|
box.classList.remove("hidden");
|
||||||
|
|
||||||
|
const an = !!d.auto_squeeze;
|
||||||
|
const nh = (d.squeeze_night_hours || []).map((h) => h + ":00").join(", ");
|
||||||
|
$("brk-state").textContent = (an ? "AN" : "AUS")
|
||||||
|
+ (d.squeeze_pending ? " · ruhende Stop-Order" : " · Market-Fallback")
|
||||||
|
+ (nh ? " · Nachtsperre " + nh : "");
|
||||||
|
$("brk-state").className = an ? "ok" : "warn";
|
||||||
|
|
||||||
|
const sq = (d.wave || {}).squeeze || {};
|
||||||
|
const st = sq.state;
|
||||||
|
$("brk-squeeze").textContent =
|
||||||
|
st === "active" ? "AUSBRUCH " + (sq.dir || "") + " @ " + (sq.level ?? "?")
|
||||||
|
: st === "armed" ? "komprimiert — Ausbruch steht bevor"
|
||||||
|
: "keine Kompression (Box " + (sq.box_atr ?? "?") + "×ATR)";
|
||||||
|
|
||||||
|
const pl = d.pending_levels || [];
|
||||||
|
$("brk-pending").textContent = pl.length
|
||||||
|
? pl.length + " liegend (" + (d.pending_quelle || "?") + ")"
|
||||||
|
: "keine — " + (an ? "Squeeze nicht armiert oder Position offen" : "Trader aus");
|
||||||
|
|
||||||
|
// B5: der Stand der einzigen Regel, die die Autonomie beenden kann.
|
||||||
|
const r = b5.ratio, pf = b5.pf;
|
||||||
|
const erf = r != null && pf != null && r >= b5.latte && pf >= 1;
|
||||||
|
$("brk-b5").textContent = b5.n + "/" + b5.ziel + " Trades · Verhältnis "
|
||||||
|
+ (r == null ? "—" : r.toFixed(2)) + " · PF "
|
||||||
|
+ (pf == null ? "—" : pf.toFixed(2))
|
||||||
|
+ " · " + b5.sum_eur.toFixed(2) + " €";
|
||||||
|
$("brk-b5").className = erf ? "ok" : "bad";
|
||||||
|
|
||||||
|
const el = $("brk-note");
|
||||||
|
if (el) {
|
||||||
|
el.textContent = erf
|
||||||
|
? "Latte (Verhältnis ≥ 1,0 UND PF ≥ 1) erfüllt."
|
||||||
|
: "⚠ Latte verfehlt (Ziel: Verhältnis ≥ 1,0 UND PF ≥ 1). Der Monitor "
|
||||||
|
+ "im Statistik-Tab meldet daneben „ontrack“ — das kommt aus der MECHANIK "
|
||||||
|
+ "(Regel neu gerechnet), nicht aus diesen echten Trades. Die Lücke "
|
||||||
|
+ "zwischen beiden ist der Befund, kein Widerspruch.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderM15(s) {
|
function renderM15(s) {
|
||||||
const st = $("m15-status");
|
const st = $("m15-status");
|
||||||
if (!s) { st.className = "m15-status"; st.textContent = "— keine Daten"; return; }
|
if (!s) { st.className = "m15-status"; st.textContent = "— keine Daten"; return; }
|
||||||
@@ -936,6 +988,7 @@ function render(d) {
|
|||||||
|
|
||||||
// ── Gesamtempfehlung (aggregiert) ───────
|
// ── Gesamtempfehlung (aggregiert) ───────
|
||||||
renderM15(d.m15_setup);
|
renderM15(d.m15_setup);
|
||||||
|
renderBrk(d);
|
||||||
renderKontext(d);
|
renderKontext(d);
|
||||||
renderCircuitBreaker(d.circuit_breaker);
|
renderCircuitBreaker(d.circuit_breaker);
|
||||||
renderPatterns(d.patterns);
|
renderPatterns(d.patterns);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+19
-2
@@ -12,7 +12,7 @@
|
|||||||
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
|
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
|
||||||
und muss offline funktionieren. -->
|
und muss offline funktionieren. -->
|
||||||
<link rel="stylesheet" href="pico.min.css?v=1">
|
<link rel="stylesheet" href="pico.min.css?v=1">
|
||||||
<link rel="stylesheet" href="style.css?v=175">
|
<link rel="stylesheet" href="style.css?v=176">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Modul-Menü (ganz oben) -->
|
<!-- Modul-Menü (ganz oben) -->
|
||||||
@@ -156,6 +156,23 @@
|
|||||||
<div class="reasons" id="wave-reasons" title="Die Begründungen, aus denen sich Richtung und Konfidenz zusammensetzen."></div>
|
<div class="reasons" id="wave-reasons" title="Die Begründungen, aus denen sich Richtung und Konfidenz zusammensetzen."></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Breakout-Autotrader (User-Wunsch 2026-08-19). ⚠ Der autonome Pfad handelt
|
||||||
|
echtes Geld, seine Abbruchregel (B5) stand bisher NUR im taeglichen
|
||||||
|
Report. Diese Karte macht ihn sichtbar — Zustand, liegende Orders und
|
||||||
|
B5-Stand an einer Stelle.
|
||||||
|
⚠⚠ Sie zeigt BEIDE Zahlen getrennt: die B4-'Mechanik' (Regel neu
|
||||||
|
gerechnet, sieht gut aus) und die LIVE-Bilanz (verliert). Genau diese
|
||||||
|
Luecke IST der Befund — eine der beiden allein waere irrefuehrend.
|
||||||
|
Reine ANZEIGE: schaltet nichts, blockiert nichts. -->
|
||||||
|
<section class="card wide hidden" id="card-brk">
|
||||||
|
<h2 title="Autonomer Entry auf den Squeeze-Ausbruch. Einziges 2-Stichproben-validiertes Auto-Setup des Projekts.">🚀 Breakout-Autotrader</h2>
|
||||||
|
<div class="kv"><span>Zustand</span><b id="brk-state">—</b></div>
|
||||||
|
<div class="kv"><span>Squeeze jetzt</span><b id="brk-squeeze">—</b></div>
|
||||||
|
<div class="kv"><span>Liegende Orders</span><b id="brk-pending">—</b></div>
|
||||||
|
<div class="kv" title="B5 = die VORAB fixierte Abbruchregel: nach 20 Trades ab 01.08. muss Verhaeltnis (OeGewinn/OeVerlust) >= 1,0 UND PF >= 1 sein."><span>B5-Regel</span><b id="brk-b5">—</b></div>
|
||||||
|
<div id="brk-note" class="hint"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="card wide" id="card-kontext">
|
<section class="card wide" id="card-kontext">
|
||||||
<h2 title="Drei unabhaengige Kontext-Anzeigen. KEINE Empfehlung und keine Richtungsaussage - die Aggregation zu einer Gesamtempfehlung wurde am 08.08. entfernt.">Marktkontext</h2>
|
<h2 title="Drei unabhaengige Kontext-Anzeigen. KEINE Empfehlung und keine Richtungsaussage - die Aggregation zu einer Gesamtempfehlung wurde am 08.08. entfernt.">Marktkontext</h2>
|
||||||
<!-- Zeitebenen-Ampel (M1/M5/M15/M30/H1) -->
|
<!-- Zeitebenen-Ampel (M1/M5/M15/M30/H1) -->
|
||||||
@@ -370,6 +387,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast hidden"></div>
|
<div id="toast" class="toast hidden"></div>
|
||||||
|
|
||||||
<script src="app.js?v=175"></script>
|
<script src="app.js?v=176"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user