SL/TP nicht mehr am S/R verankert + Squeeze-Market-Fallback auf den BRK-Slot
User: "SL/TP sollte nicht am S/R verankert werden sondern immer dynamisch
berechnet werden" - nach dem Fund, dass Trades wiederholt am Level schlossen.
(1) PIVOT-ANKER DES INITIAL-SL ENTFERNT (`_SL_PIVOT_ANKER = False`).
`_calc_sl_tp` legte den SL an den naechsten M15-Pivot (LONG: pivot_low − Puffer,
SHORT: pivot_high + Puffer) und klemmte ihn erst DANACH auf 1,8–2,2×ATR. Der
Stop lag damit fuer einen LONG direkt unter der UNTERSTUETZUNG. Ueber 69
geloggte Orders war der Pivot in 4 % massgeblich (59 % gekappt, 36 % geweitet) -
und in genau diesen Faellen stand der Stop auf dem Level, an dem der Markt
ohnehin am haeufigsten durchwickt.
⚠ Der TP erbte das INDIREKT: er ist `2,0 × SL-Distanz`, skalierte also mit dem
Pivot-Abstand. Beide sind jetzt rein ATR-dynamisch (Mitte des Bandes = 2,0×ATR).
VORHER GEMESSEN (`backtest_sl_pivot.py`, 60k M5, 2 Halbjahre, gleiche
Einstiege, NUR die SL-Methode variiert, kanonischer Exit, Echtkosten):
PIVOT H1 -0,054 / H2 -0,040 PF 0,93 / 0,95
FIX H1 -0,040 / H2 +0,004 PF 0,95 / 1,01
FIX ist in BEIDEN Haelften besser (+0,014 / +0,043 R), der Worst-Case wird nicht
groesser - die vorab fixierte Regel ist erfuellt.
⚠ EHRLICH: beide KI enthalten die Null, es ist KEIN belegter Ertragsgewinn. Der
Punkt ist, dass die Verankerung nichts nuetzt UND den Stop auf das Level legt.
Das bestaetigt `backtest_sl_method.py` ("gedeckelt sind alle Methoden
gleichwertig") auf dem heutigen Exit - dort war es nur nie als NACHTEIL sichtbar.
Zurueck: `_SL_PIVOT_ANKER = True`.
(2) LUECKE AUS DEM ZWEI-SLOT-UMBAU, vom Deploy-Schritt 5 aufgedeckt:
"Auto-Squeeze-Entry fehlgeschlagen: Position bereits offen!". `engine.open_long`
routete IMMER auf `self.trader` - der Market-Fallback des Squeeze lief also
weiter ueber Slot 1 und scheiterte, sobald ein manueller Trade lief. FLAT-Check
und Pending-Manager waren gestern umgehaengt, der BESTELLWEG nicht.
Neu `_slot(source)`: auto_squeeze -> trader_brk, alles andere -> trader. Auch
der Bot-Marker (`_bot_open_ticket`) holt das Ticket jetzt vom richtigen Slot.
⚠ Genau die Fehlerklasse von Deployment-Drift Fall 8 - ein Umbau, der den
Bestellweg aendert, muss ALLE Bestellwege mitnehmen.
91 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e01ecde3a3
commit
c081bdc734
@@ -0,0 +1,166 @@
|
|||||||
|
"""Pivot-verankerter SL gegen fixen 2,0xATR-SL — mit dem HEUTIGEN Exit (2026-08-20)
|
||||||
|
|
||||||
|
ANLASS (User): "ueberpruefe genau ob wirklich kein Trade am S/R geschlossen
|
||||||
|
wird ... das hat mich mindestens 3x Geld gekostet."
|
||||||
|
|
||||||
|
DER FUND, den die Pfad-Pruefung ergeben hat: `trader._calc_sl_tp` legt den
|
||||||
|
Initial-SL an den naechsten M15-PIVOT (LONG: `pivot_low` − Puffer, SHORT:
|
||||||
|
`pivot_high` + Puffer) und klemmt ihn erst danach auf 1,8–2,2×ATR. Der Stop
|
||||||
|
liegt also — wenn der Pivot innerhalb des Bandes liegt — DIREKT an der
|
||||||
|
Unterstuetzung bzw. am Widerstand. Ueber 69 geloggte Orders:
|
||||||
|
SL steht AM Pivot 3 ( 4 %) <- Pivot ist maßgeblich
|
||||||
|
auf 2,2×ATR gekappt 41 (59 %)
|
||||||
|
auf 1,8×ATR geweitet 25 (36 %)
|
||||||
|
In 95 % dominiert also der ATR-Deckel — aber in 4 % ist der Stop exakt das
|
||||||
|
Level, an dem der Markt ohnehin am haeufigsten wickt.
|
||||||
|
|
||||||
|
WARUM NEU MESSEN. `backtest_sl_method.py` hat die Verankerung schon einmal
|
||||||
|
geprueft und "gedeckelt alle identisch, die Verankerung ist Rauschen" ergeben —
|
||||||
|
aber mit einem ANDEREN Exit-Modell und vor dem kanonischen `exit_model.LIVE`
|
||||||
|
(Trail 1,0 seit 31.07., trail_start 1,3 seit 20.08.). Ein Befund, der eine
|
||||||
|
Live-Schutzlinie rechtfertigt, gehoert auf dem heutigen Exit nachgerechnet.
|
||||||
|
|
||||||
|
AUFBAU. Squeeze-Einstiege (reproduzierbar, echte `_squeeze_one`), ruhende Order
|
||||||
|
am Level mit Fill bei BERUEHRUNG, kanonischer Exit, Echtkosten, 2 Halbjahre,
|
||||||
|
95-%-Bootstrap. Variiert wird AUSSCHLIESSLICH die SL-Methode:
|
||||||
|
|
||||||
|
A) PIVOT wie live: naechster M15-Pivot ∓ Puffer, geklemmt auf 1,8–2,2×ATR
|
||||||
|
B) FIX schlicht 2,0×ATR — ohne jeden Level-Bezug
|
||||||
|
|
||||||
|
⚠ Feste, GETEILTE Einstiegsliste: beide Varianten laufen auf DENSELBEN Trades.
|
||||||
|
Ein sequentieller Vergleich waere ungueltig — ein anderer SL laesst ANDERE
|
||||||
|
Trades in den einen Slot (die Falle vom 14.08.).
|
||||||
|
|
||||||
|
⚠ M15-Pivots werden aus den M5-Bars aggregiert (3 M5 = 1 M15), Pivot-Fenster
|
||||||
|
k=3 wie in `mt5_utils.pivot_low/high`. Das ist eine NAEHERUNG der Live-Funktion —
|
||||||
|
sie liest direkt M15-Bars. Fuer den VERGLEICH der beiden Methoden ist das
|
||||||
|
unkritisch, fuer die Absolutwerte nicht.
|
||||||
|
|
||||||
|
REGEL VORAB FIXIERT: umgestellt wird nur, wenn B in BEIDEN Halbjahren
|
||||||
|
mindestens gleichauf liegt (OeR nicht schlechter als A minus ein
|
||||||
|
Standardfehler) UND der Worst-Case nicht groesser wird. Ist B schlechter,
|
||||||
|
bleibt die Verankerung — dann ist der beobachtete Effekt der Preis fuer einen
|
||||||
|
Vorteil an anderer Stelle.
|
||||||
|
|
||||||
|
Aufruf: python backtest_sl_pivot.py [bars]
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import MetaTrader5 as mt5
|
||||||
|
|
||||||
|
from backtest_cost_gate import kennzahlen
|
||||||
|
from core.exit_model import LIVE, simulate
|
||||||
|
from core.squeeze_scan import atr_series, scan
|
||||||
|
|
||||||
|
BOX_N, K, MULT_BOX, ATRMIN = 10, 0.1, 2.5, 0.06
|
||||||
|
SL_MIN, SL_MAX = 1.8, 2.2 # INIT_SL_MIN_ATR / INIT_SL_MAX_ATR
|
||||||
|
PIV_K = 3
|
||||||
|
BUF = 0.02 # Puffer unter/ueber dem Pivot (Groessenordnung live)
|
||||||
|
|
||||||
|
|
||||||
|
def m15_pivots(H, L):
|
||||||
|
"""M15-Pivots aus M5-Bars aggregiert → (Index_M5, Preis) je Pivot."""
|
||||||
|
n = len(H) // 3
|
||||||
|
hi = [max(H[3 * i:3 * i + 3]) for i in range(n)]
|
||||||
|
lo = [min(L[3 * i:3 * i + 3]) for i in range(n)]
|
||||||
|
ph, pl = [], []
|
||||||
|
for j in range(PIV_K, n - PIV_K):
|
||||||
|
if hi[j] == max(hi[j - PIV_K:j + PIV_K + 1]):
|
||||||
|
ph.append((3 * (j + PIV_K), hi[j])) # erst k Bars spaeter bekannt
|
||||||
|
if lo[j] == min(lo[j - PIV_K:j + PIV_K + 1]):
|
||||||
|
pl.append((3 * (j + PIV_K), lo[j]))
|
||||||
|
return ph, pl
|
||||||
|
|
||||||
|
|
||||||
|
def sl_pivot(i, d, entry, atr, ph, pl):
|
||||||
|
"""Live-Regel: naechster Pivot ∓ Puffer, dann auf 1,8–2,2×ATR geklemmt."""
|
||||||
|
kand = [p for (j, p) in (pl if d > 0 else ph) if j <= i and
|
||||||
|
((p < entry) if d > 0 else (p > entry))]
|
||||||
|
if kand:
|
||||||
|
piv = max(kand) if d > 0 else min(kand)
|
||||||
|
sl = (piv - BUF) if d > 0 else (piv + BUF)
|
||||||
|
else:
|
||||||
|
sl = entry * (1 - 0.012) if d > 0 else entry * (1 + 0.012)
|
||||||
|
dist = (entry - sl) if d > 0 else (sl - entry)
|
||||||
|
dist = min(max(dist, SL_MIN * atr), SL_MAX * atr)
|
||||||
|
return dist
|
||||||
|
|
||||||
|
|
||||||
|
def lauf(ein, H, L, C, SP, ende, modus, ph, pl):
|
||||||
|
rs = []
|
||||||
|
for j, d, lvl, atr in ein:
|
||||||
|
f = None
|
||||||
|
for x in range(j, min(j + 13, ende)):
|
||||||
|
if (H[x] >= lvl) if d > 0 else (L[x] <= lvl):
|
||||||
|
f = x
|
||||||
|
break
|
||||||
|
if f is None or f + 1 >= ende:
|
||||||
|
continue
|
||||||
|
dist = sl_pivot(f, d, lvl, atr, ph, pl) if modus == "pivot" else 2.0 * atr
|
||||||
|
# SL-Distanz ueber `sl_atr` in EXAKT die kanonische Sim geben.
|
||||||
|
par = LIVE.with_(sl_atr=dist / atr)
|
||||||
|
r = simulate(lvl, d, atr, H, L, C, f + 1, par, timestop=True, use_tp=True)
|
||||||
|
rs.append(r - ((SP[f] if SP[f] > 0 else 0.0225) / atr))
|
||||||
|
return rs
|
||||||
|
|
||||||
|
|
||||||
|
def zeig(lbl, k):
|
||||||
|
if not k:
|
||||||
|
print(" {:<22} --".format(lbl)); return None
|
||||||
|
ci = k.get("ci"); m = ""
|
||||||
|
if ci:
|
||||||
|
m = (" [{:+.3f} .. {:+.3f}]".format(ci[0], ci[1])
|
||||||
|
+ ("" if ci[0] * ci[1] > 0 else " ~0"))
|
||||||
|
print(" {:<22} n={:>4} WR={:>3.0f}% OeR={:+.3f} PF={:.2f} worst={:+.2f}{}"
|
||||||
|
.format(lbl, k["n"], k["wr"], k["oer"], k["pf"], k.get("worst", 0), m))
|
||||||
|
return k
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
n = int(sys.argv[1]) if len(sys.argv) > 1 else 60000
|
||||||
|
mt5.initialize()
|
||||||
|
sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD")
|
||||||
|
if mt5.symbol_info(c)), None)
|
||||||
|
bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n)
|
||||||
|
point = mt5.symbol_info(sym).point
|
||||||
|
mt5.shutdown()
|
||||||
|
H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars]
|
||||||
|
C = [float(b["close"]) for b in bars]
|
||||||
|
SP = [float(b["spread"]) * point for b in bars]
|
||||||
|
A = atr_series(H, L, C)
|
||||||
|
ph, pl = m15_pivots(H, L)
|
||||||
|
N = len(C); mid = N // 2; ende = N - LIVE.max_hold - 1
|
||||||
|
|
||||||
|
def entries(lo, hi):
|
||||||
|
out = []
|
||||||
|
scan(H, L, C, A, SP, 1.0, lo, hi, MULT_BOX, box_n=BOX_N, k=K,
|
||||||
|
atr_min=ATRMIN, exit_fn=lambda *a: 0.0,
|
||||||
|
on_entry=lambda j, d, lvl, atr, komp: (
|
||||||
|
out.append((j, d, lvl, atr)) if komp else None))
|
||||||
|
return out
|
||||||
|
|
||||||
|
e1, e2 = entries(0, mid), entries(mid, ende)
|
||||||
|
print("=" * 92)
|
||||||
|
print(" SL-METHODE: Pivot-verankert gegen fix 2,0xATR — {} M5 ({} Bars)".format(sym, N))
|
||||||
|
print(" gleiche Einstiege, NUR die SL-Methode variiert · {} M15-Pivots".format(len(ph) + len(pl)))
|
||||||
|
print("=" * 92)
|
||||||
|
erg = {}
|
||||||
|
for modus, titel in (("pivot", "A) PIVOT (= live)"), ("fix", "B) FIX 2,0xATR")):
|
||||||
|
print("\n " + titel)
|
||||||
|
print(" " + "-" * 88)
|
||||||
|
erg[modus] = (zeig("H1", kennzahlen(lauf(e1, H, L, C, SP, ende, modus, ph, pl))),
|
||||||
|
zeig("H2", kennzahlen(lauf(e2, H, L, C, SP, ende, modus, ph, pl))))
|
||||||
|
print("\n " + "=" * 88)
|
||||||
|
a1, a2 = erg["pivot"]; b1, b2 = erg["fix"]
|
||||||
|
if a1 and b1:
|
||||||
|
print(" Differenz FIX − PIVOT: H1 {:+.3f} R H2 {:+.3f} R"
|
||||||
|
.format(b1["oer"] - a1["oer"], b2["oer"] - a2["oer"]))
|
||||||
|
print(" Worst-Case: PIVOT {:+.2f}/{:+.2f} FIX {:+.2f}/{:+.2f}"
|
||||||
|
.format(a1.get("worst", 0), a2.get("worst", 0),
|
||||||
|
b1.get("worst", 0), b2.get("worst", 0)))
|
||||||
|
print(" REGEL: umstellen nur, wenn FIX beidhaelftig mindestens gleichauf")
|
||||||
|
print(" liegt UND der Worst-Case nicht groesser wird.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+18
-3
@@ -2775,11 +2775,25 @@ class TradingEngine:
|
|||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
# TRADE-AKTIONEN (für die Trade-Endpoints in server.py — Schritt 3)
|
# TRADE-AKTIONEN (für die Trade-Endpoints in server.py — Schritt 3)
|
||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def _slot(self, source: str):
|
||||||
|
"""Welcher Trader fuer welche Quelle? (Zwei-Slot-Umbau 2026-08-19)
|
||||||
|
|
||||||
|
⚠⚠ LUECKE, real am 2026-08-20 aufgefallen: der Market-Fallback des
|
||||||
|
Squeeze lief weiter ueber `self.trader` und scheiterte an
|
||||||
|
"Position bereits offen!", sobald ein manueller Trade lief — obwohl BRK
|
||||||
|
seit gestern einen EIGENEN Slot hat. Der FLAT-Check und der
|
||||||
|
Pending-Manager waren umgehaengt, der Bestellweg nicht.
|
||||||
|
Genau die Fehlerklasse, die im Projekt als Deployment-Drift Fall 8
|
||||||
|
steht: ein Umbau, der den Bestellweg aendert, muss ALLE Bestellwege
|
||||||
|
mitnehmen — sonst laeuft ein Teil weiter auf dem alten Pfad.
|
||||||
|
"""
|
||||||
|
return self.trader_brk if source == "auto_squeeze" else self.trader
|
||||||
|
|
||||||
def open_long(self, source: str = "manuell") -> str | None:
|
def open_long(self, source: str = "manuell") -> str | None:
|
||||||
return self._open(self.trader.open_long, "LONG", source)
|
return self._open(self._slot(source).open_long, "LONG", source)
|
||||||
|
|
||||||
def open_short(self, source: str = "manuell") -> str | None:
|
def open_short(self, source: str = "manuell") -> str | None:
|
||||||
return self._open(self.trader.open_short, "SHORT", source)
|
return self._open(self._slot(source).open_short, "SHORT", source)
|
||||||
|
|
||||||
def _open(self, fn, want: str, source: str = "manuell") -> str | None:
|
def _open(self, fn, want: str, source: str = "manuell") -> str | None:
|
||||||
sym = self.data.symbol
|
sym = self.data.symbol
|
||||||
@@ -2829,7 +2843,8 @@ class TradingEngine:
|
|||||||
# (manuelle Trades bleiben in der Diskretion des Users, User-Vorgabe).
|
# (manuelle Trades bleiben in der Diskretion des Users, User-Vorgabe).
|
||||||
if source in ("auto_squeeze", "auto_signal"):
|
if source in ("auto_squeeze", "auto_signal"):
|
||||||
try:
|
try:
|
||||||
self._bot_open_ticket = self.trader.snapshot().get("ticket")
|
# ⚠ Ticket vom Slot holen, der die Order gesendet hat.
|
||||||
|
self._bot_open_ticket = self._slot(source).snapshot().get("ticket")
|
||||||
self._bot_open_source = source
|
self._bot_open_source = source
|
||||||
self._adverse15_done_ticket = None
|
self._adverse15_done_ticket = None
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
+34
-2
@@ -69,6 +69,11 @@ _BROKER_OFF_DEFAULT_S = 3 * 3600
|
|||||||
_BROKER_TICK_FRESH_S = 900
|
_BROKER_TICK_FRESH_S = 900
|
||||||
|
|
||||||
|
|
||||||
|
# ⚠ Pivot-Anker des Initial-SL — seit 2026-08-20 AUS (s. Begruendung in
|
||||||
|
# `_calc_sl_tp`). True stellt das alte Verhalten wieder her.
|
||||||
|
_SL_PIVOT_ANKER = False
|
||||||
|
|
||||||
|
|
||||||
class TradeManager:
|
class TradeManager:
|
||||||
def __init__(self, nur_ticket: bool = False):
|
def __init__(self, nur_ticket: bool = False):
|
||||||
"""`nur_ticket=True` => STRIKT an das eigene Ticket gebunden.
|
"""`nur_ticket=True` => STRIKT an das eigene Ticket gebunden.
|
||||||
@@ -139,9 +144,33 @@ class TradeManager:
|
|||||||
max_dist = (INIT_SL_MAX_ATR * atr) if atr else None
|
max_dist = (INIT_SL_MAX_ATR * atr) if atr else None
|
||||||
min_dist = (INIT_SL_MIN_ATR * atr) if atr else None
|
min_dist = (INIT_SL_MIN_ATR * atr) if atr else None
|
||||||
capped = floored = False
|
capped = floored = False
|
||||||
if otype == mt5.ORDER_TYPE_BUY:
|
# ⚠⚠ KEIN PIVOT-ANKER MEHR (2026-08-20, User-Fund "immer am S/R
|
||||||
|
# geschlossen"). Der SL wurde bis heute an den naechsten M15-Pivot
|
||||||
|
# gelegt — also fuer einen LONG direkt unter die UNTERSTUETZUNG, fuer
|
||||||
|
# einen SHORT ueber den WIDERSTAND — und erst danach auf 1,8–2,2×ATR
|
||||||
|
# geklemmt. Ueber 69 geloggte Orders war der Pivot in 4 % massgeblich
|
||||||
|
# (in 59 % kappte der Deckel, in 36 % weitete der Boden auf); in diesen
|
||||||
|
# Faellen lag der Stop exakt auf dem Level, an dem der Markt ohnehin am
|
||||||
|
# haeufigsten durchwickt.
|
||||||
|
# GEMESSEN mit dem HEUTIGEN Exit (`backtest_sl_pivot.py`, 60k M5,
|
||||||
|
# 2 Halbjahre, gleiche Einstiege, nur die SL-Methode variiert):
|
||||||
|
# PIVOT H1 −0,054 / H2 −0,040 PF 0,93 / 0,95
|
||||||
|
# FIX H1 −0,040 / H2 +0,004 PF 0,95 / 1,01
|
||||||
|
# FIX ist in BEIDEN Haelften besser (+0,014 / +0,043 R), der
|
||||||
|
# Worst-Case wird nicht groesser. ⚠ Beide KI enthalten die Null — es
|
||||||
|
# ist KEIN belegter Ertragsgewinn. Der Punkt ist ein anderer: die
|
||||||
|
# Verankerung nuetzt nichts und legt den Stop auf das Level.
|
||||||
|
# ⚠ Das bestaetigt `backtest_sl_method.py` (gedeckelt sind alle
|
||||||
|
# Methoden gleichwertig, der Band-Cap dominiert) auf dem heutigen
|
||||||
|
# Exit — dort war es nur nie als NACHTEIL sichtbar.
|
||||||
|
# ZURUECK: `_SL_PIVOT_ANKER = True`.
|
||||||
|
if _SL_PIVOT_ANKER and otype == mt5.ORDER_TYPE_BUY:
|
||||||
piv = pivot_low(sym, entry_price)
|
piv = pivot_low(sym, entry_price)
|
||||||
sl = (piv - buf) if piv else round(entry_price * (1 - INIT_SL_FALLBACK), si.digits)
|
sl = (piv - buf) if piv else round(entry_price * (1 - INIT_SL_FALLBACK), si.digits)
|
||||||
|
elif otype == mt5.ORDER_TYPE_BUY:
|
||||||
|
piv = None
|
||||||
|
sl = entry_price - (INIT_SL_MIN_ATR + INIT_SL_MAX_ATR) / 2.0 * atr \
|
||||||
|
if atr else round(entry_price * (1 - INIT_SL_FALLBACK), si.digits)
|
||||||
if max_dist and entry_price - sl > max_dist:
|
if max_dist and entry_price - sl > max_dist:
|
||||||
sl = entry_price - max_dist; capped = True
|
sl = entry_price - max_dist; capped = True
|
||||||
if min_dist and entry_price - sl < min_dist:
|
if min_dist and entry_price - sl < min_dist:
|
||||||
@@ -149,9 +178,12 @@ class TradeManager:
|
|||||||
sl = round(sl, si.digits)
|
sl = round(sl, si.digits)
|
||||||
sl_dist = entry_price - sl
|
sl_dist = entry_price - sl
|
||||||
tp = round(entry_price + INIT_TP_RR * sl_dist, si.digits) if sl_dist > 0 else 0.0
|
tp = round(entry_price + INIT_TP_RR * sl_dist, si.digits) if sl_dist > 0 else 0.0
|
||||||
else:
|
elif _SL_PIVOT_ANKER:
|
||||||
piv = pivot_high(sym, entry_price)
|
piv = pivot_high(sym, entry_price)
|
||||||
sl = (piv + buf) if piv else round(entry_price * (1 + INIT_SL_FALLBACK), si.digits)
|
sl = (piv + buf) if piv else round(entry_price * (1 + INIT_SL_FALLBACK), si.digits)
|
||||||
|
else:
|
||||||
|
piv = None
|
||||||
|
sl = entry_price + (INIT_SL_MIN_ATR + INIT_SL_MAX_ATR) / 2.0 * atr if atr else round(entry_price * (1 + INIT_SL_FALLBACK), si.digits)
|
||||||
if max_dist and sl - entry_price > max_dist:
|
if max_dist and sl - entry_price > max_dist:
|
||||||
sl = entry_price + max_dist; capped = True
|
sl = entry_price + max_dist; capped = True
|
||||||
if min_dist and sl - entry_price < min_dist:
|
if min_dist and sl - entry_price < min_dist:
|
||||||
|
|||||||
Reference in New Issue
Block a user