Ruhende Orders: aufraeumen statt liegenlassen (User-Vorgabe)
User: "was sind ruhende orders. Loesche die wenn der trade nicht eroeffnet werden kann." Anlass: MT5 zeichnete SL/TP-Linien, obwohl keine Position lief. Das waren die liegenden Stop-Orders des Breakout-Traders - MT5 stellt jede Pending-Order samt mitgeschicktem SL selbst dar. Der Bot exportiert NIE SL/TP ins Chart (0 Treffer im .mq5, keine SL-Zeile in der CSV). Kein Anzeigefehler. ZWEI ECHTE VERWAISUNGS-PFADE GEFUNDEN. _manage_squeeze_pending stieg VOR dem Aufraeum-Block aus, wenn squeeze_pending_entry=false oder kein Symbol da war. Die Orders sind ORDER_TIME_GTC - sie verfallen NICHT. Eine so verwaiste Order haengt beim Broker, wird von niemandem mehr bewertet und kann Stunden spaeter fuellen: ohne Dedup, ohne Guard-Pruefung und (bis heute) ohne Trailing. Neu engine._pendings_abraeumen(sym, grund) - beide Pfade raeumen jetzt ab. DIE EIGENTLICHE VORGABE: ein halbes Setup wird aufgeloest. Scheitert eine Seite mit Lot-Fehler (freie Margin reicht nicht fuers Mindestlot), wird auch die Gegenseite storniert - der gemessene Squeeze ist ein Ausbruch in BEIDE Richtungen; laege nur eine Seite, handelte der Bot ihn nur noch in eine. Ausdruecklich NICHT bei "Kurs hat das Level bereits passiert": das ist voruebergehend und betrifft nur die eine Seite. Der Lot-Fehler betrifft das KONTO und damit beide. Erkannt ueber die Konstante mt5_utils.LOT_ZU_KLEIN, nicht ueber ein deutsches Wort im Fliesstext - eine Erkennung, die an einer Formulierung haengt, hoert still auf zu funktionieren und faellt dabei auf die stille Seite. ⚠⚠ DIE LOG-DROSSEL WAR AM SELBEN TAG ZUM ZWEITEN MAL AUSGEHEBELT - durch meine eigene Verbesserung. Vormittags lag der Merker in EINER Variable fuer beide Seiten (behoben). Dann bekam die Meldung ueber lot_grund ihre Zahlen, und die freie Margin aendert sich mit jedem Tick: der Text war jedes Mal neu, der Vergleich traf immer zu, wieder 2 Zeilen pro Sekunde. Gedrosselt wird jetzt auf die URSACHE, nicht auf ihre Formulierung (_err_key: alle Zahlen -> #). Gemessen: 7 Zeilen in 90 s statt ~180, und die verbliebenen sind echte Wechsel zwischen den beiden Ursachen. Lehre: eine Drossel, die einen TEXT vergleicht, ist so stabil wie der Text. Live verifiziert: 0 liegende Orders, keine verwaisten Reste, Startup-Schonfrist raeumt korrekt ab, Orders kommen danach zurueck sobald die Margin reicht. Pipeline gruen (A-G), 97 Tests, deployt mit --feld squeeze_block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3fb9c3fd2e
commit
fd2ca46dc0
@@ -9342,6 +9342,57 @@ mit Begründung im Code, damit es nicht als Versehen gelesen wird.
|
|||||||
**Pipeline jetzt: A · A2 · A3 · B · B2 · C · C2 · D · E · F · G** — alle grün,
|
**Pipeline jetzt: A · A2 · A3 · B · B2 · C · C2 · D · E · F · G** — alle grün,
|
||||||
97 Tests, deployt und verifiziert.
|
97 Tests, deployt und verifiziert.
|
||||||
|
|
||||||
|
## ⚠⚠ RUHENDE ORDERS: AUFRÄUMEN STATT LIEGENLASSEN (User-Vorgabe 2026-08-20)
|
||||||
|
|
||||||
|
User: *„was sind ruhende Orders. Lösche die, wenn der Trade nicht eröffnet
|
||||||
|
werden kann."* — Anlass war, dass MT5 SL/TP-Linien zeichnete, obwohl keine
|
||||||
|
Position lief. **Das waren die liegenden Stop-Orders des Breakout-Traders**;
|
||||||
|
MT5 stellt jede Pending-Order samt ihrem mitgeschickten SL selbst dar. ✅ Der
|
||||||
|
Bot exportiert **nie** SL/TP ins Chart (0 Treffer im `.mq5`, keine `SL;`-Zeile
|
||||||
|
in der CSV) — kein Anzeigefehler, sondern eine Broker-Funktion.
|
||||||
|
|
||||||
|
⚠⚠ **DIE PRÜFUNG DER FRAGE FÖRDERTE ZWEI ECHTE VERWAISUNGS-PFADE ZUTAGE.**
|
||||||
|
`_manage_squeeze_pending` stieg **VOR** dem Aufräum-Block aus, wenn
|
||||||
|
`squeeze_pending_entry=false` **oder** kein Symbol da war (MT5-Aussetzer). Die
|
||||||
|
Orders sind `ORDER_TIME_GTC` — **sie verfallen nicht**. Eine so verwaiste Order
|
||||||
|
hängt beim Broker, wird von niemandem mehr bewertet und kann Stunden später
|
||||||
|
füllen: **ohne Dedup, ohne Guard-Prüfung** und (bis zum Fix vom selben Tag)
|
||||||
|
**ohne Trailing**.
|
||||||
|
✅ Neu `engine._pendings_abraeumen(sym, grund)` — beide Pfade räumen jetzt ab,
|
||||||
|
statt nur zurückzukehren.
|
||||||
|
|
||||||
|
✅ **UND DIE EIGENTLICHE VORGABE UMGESETZT: ein halbes Setup wird aufgelöst.**
|
||||||
|
Scheitert eine Seite mit **Lot-Fehler** (die freie Margin reicht nicht einmal
|
||||||
|
fürs Mindestlot), wird auch die **Gegenseite** storniert. Grund: der gemessene
|
||||||
|
Squeeze ist ein Ausbruch aus der Box in **beide** Richtungen; läge nur eine
|
||||||
|
Seite, handelte der Bot ihn nur noch in **eine** — das ist nicht das Setup, das
|
||||||
|
validiert wurde.
|
||||||
|
⚠ **Ausdrücklich NICHT bei „Kurs hat das Level bereits passiert"**: das ist
|
||||||
|
vorübergehend und betrifft naturgemäß nur die eine Seite (der Kurs steht gerade
|
||||||
|
an der Box-Kante); die Gegenseite bleibt ein gültiger Einstieg. Der Lot-Fehler
|
||||||
|
dagegen betrifft das **Konto** und damit beide Seiten.
|
||||||
|
⚠ Erkannt über die **Konstante `mt5_utils.LOT_ZU_KLEIN`**, nicht über ein
|
||||||
|
deutsches Wort im Fließtext: eine Erkennung, die an einer Formulierung hängt,
|
||||||
|
hört still auf zu funktionieren, sobald jemand den Satz umschreibt — und fällt
|
||||||
|
dabei auf die **stille** Seite (kein Aufräumen), nicht auf die laute.
|
||||||
|
|
||||||
|
⚠⚠⚠ **DIE LOG-DROSSEL WAR AM SELBEN TAG ZUM ZWEITEN MAL AUSGEHEBELT — durch
|
||||||
|
meine eigene Verbesserung.** Vormittags lag der Merker in EINER Variable für
|
||||||
|
beide Order-Seiten (behoben). Dann bekam die Meldung über `lot_grund` ihre
|
||||||
|
Zahlen — und die **freie Margin ändert sich mit jedem Tick**. Der Text war also
|
||||||
|
jedes Mal neu, der Vergleich `!= err` traf immer zu: wieder **2 Zeilen pro
|
||||||
|
Sekunde**.
|
||||||
|
✅ Gedrosselt wird jetzt auf die **URSACHE**, nicht auf ihre Formulierung
|
||||||
|
(`_err_key`: alle Zahlen → `#`). Gemessen: **7 Zeilen in 90 s** statt ~180 —
|
||||||
|
und die verbliebenen sind echte Wechsel zwischen den beiden Ursachen, kein Lärm.
|
||||||
|
⚠ **Lehre: eine Drossel, die einen TEXT vergleicht, ist so stabil wie der Text.**
|
||||||
|
Sobald eine Meldung Messwerte enthält, muss auf einen normalisierten Schlüssel
|
||||||
|
gedrosselt werden.
|
||||||
|
|
||||||
|
✅ Live verifiziert nach dem Deploy: **0 liegende Orders**, keine verwaisten
|
||||||
|
Reste, Startup-Schonfrist räumt korrekt ab und die Orders kommen danach zurück,
|
||||||
|
sobald die Margin reicht.
|
||||||
|
|
||||||
## ⚠⚠⚠ P(break) IST LIVE **INVERTIERT** — und es steuert echtes Geld (2026-08-19)
|
## ⚠⚠⚠ P(break) IST LIVE **INVERTIERT** — und es steuert echtes Geld (2026-08-19)
|
||||||
|
|
||||||
Die fällige Messung ist entscheidbar geworden: **n=530 entkoppelt** gegen die
|
Die fällige Messung ist entscheidbar geworden: **n=530 entkoppelt** gegen die
|
||||||
|
|||||||
+78
-5
@@ -13,6 +13,7 @@ das MT5-Terminal. Es sollte immer nur EINE Engine-Instanz gegen das Terminal
|
|||||||
laufen.
|
laufen.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import re
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -30,6 +31,7 @@ from core.config import (
|
|||||||
# und die `[zones]`-Sektion in der ini erhalten — reaktivierbar, falls je gewollt.
|
# und die `[zones]`-Sektion in der ini erhalten — reaktivierbar, falls je gewollt.
|
||||||
from core.logger import get_logger
|
from core.logger import get_logger
|
||||||
from core.history import HistoryLogger
|
from core.history import HistoryLogger
|
||||||
|
from core.mt5_utils import LOT_ZU_KLEIN
|
||||||
from core.mt5data import MT5Data
|
from core.mt5data import MT5Data
|
||||||
from core.trader import TradeManager
|
from core.trader import TradeManager
|
||||||
from core.trailing import TrailingManager
|
from core.trailing import TrailingManager
|
||||||
@@ -202,6 +204,18 @@ def send_telegram(text, *a, **kw):
|
|||||||
return _send_telegram_roh(text, *a, **kw)
|
return _send_telegram_roh(text, *a, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
def _err_key(msg: str) -> str:
|
||||||
|
"""Fehlermeldung auf ihre URSACHE reduzieren — Zahlen raus.
|
||||||
|
|
||||||
|
⚠ `lot_grund` nennt die freie Margin, und die aendert sich im Sekundentakt.
|
||||||
|
Ohne diese Normalisierung ist jede Drossel, die den TEXT vergleicht,
|
||||||
|
wirkungslos. Aus
|
||||||
|
"[MINDESTLOT] Einsatz 8 % von 39.33 EUR freier Margin = 3.15 EUR, ..."
|
||||||
|
wird "[MINDESTLOT] Einsatz # % von #", und das bleibt stabil.
|
||||||
|
"""
|
||||||
|
return re.sub(r"[0-9]+(?:[.,][0-9]+)?", "#", msg or "")[:60]
|
||||||
|
|
||||||
|
|
||||||
class TradingEngine:
|
class TradingEngine:
|
||||||
"""Headless-Pendant zu OilWidget: hält die core-Objekte, fährt die
|
"""Headless-Pendant zu OilWidget: hält die core-Objekte, fährt die
|
||||||
Refresh-Loops als Threads und liefert einen aggregierten Snapshot."""
|
Refresh-Loops als Threads und liefert einen aggregierten Snapshot."""
|
||||||
@@ -1738,10 +1752,19 @@ class TradingEngine:
|
|||||||
füllt eine, existiert eine Position → `soll` wird False und die Gegenseite
|
füllt eine, existiert eine Position → `soll` wird False und die Gegenseite
|
||||||
wird im selben Tick storniert. Das ersetzt ein echtes OCO, das MT5 nicht hat.
|
wird im selben Tick storniert. Das ersetzt ein echtes OCO, das MT5 nicht hat.
|
||||||
"""
|
"""
|
||||||
if not self._squeeze_pending:
|
|
||||||
return
|
|
||||||
sym = self.data.symbol or ""
|
sym = self.data.symbol or ""
|
||||||
if not sym:
|
# ⚠⚠ AUFRAEUMEN GEHT VOR (User-Vorgabe 2026-08-20: "loesche die, wenn
|
||||||
|
# der Trade nicht eroeffnet werden kann"). Die beiden frueheren
|
||||||
|
# Fruehausstiege liessen liegende Orders ZURUECK:
|
||||||
|
# · `squeeze_pending_entry=false` waehrend Orders liegen
|
||||||
|
# · kein Symbol (MT5-Aussetzer)
|
||||||
|
# Sie sind `ORDER_TIME_GTC` — sie verfallen NICHT. Eine so verwaiste
|
||||||
|
# Order haengt beim Broker, wird von niemandem mehr bewertet und kann
|
||||||
|
# Stunden spaeter fuellen: ohne Dedup, ohne Guard-Pruefung und (bis zum
|
||||||
|
# Fix von heute) ohne Trailing. Deshalb wird jetzt in beiden Faellen
|
||||||
|
# ABGERAEUMT statt nur zurueckgekehrt.
|
||||||
|
if not self._squeeze_pending or not sym:
|
||||||
|
self._pendings_abraeumen(sym, "Feature aus" if sym else "kein Symbol")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
# ⚠ NUR der eigene Slot entscheidet — vorher stornierte JEDE
|
# ⚠ NUR der eigene Slot entscheidet — vorher stornierte JEDE
|
||||||
@@ -1782,6 +1805,13 @@ class TradingEngine:
|
|||||||
else:
|
else:
|
||||||
self.trader.cancel_pending(o.ticket)
|
self.trader.cancel_pending(o.ticket)
|
||||||
self._pending_tickets.pop(int(o.ticket), None)
|
self._pending_tickets.pop(int(o.ticket), None)
|
||||||
|
# ⚠⚠ LOT-FEHLER = das Setup ist NICHT handelbar (User-Vorgabe
|
||||||
|
# 2026-08-20). Er heisst: die freie Margin reicht nicht einmal
|
||||||
|
# fuer das Mindestlot. Steht dann noch die GEGENSEITE aus einem
|
||||||
|
# frueheren Tick, laege ein halbes Setup am Markt — der Bot
|
||||||
|
# handelte den Ausbruch nur noch in EINE Richtung, waehrend die
|
||||||
|
# Messung beide unterstellt. Das wird unten aufgeloest.
|
||||||
|
lot_fehler = False
|
||||||
for otype, preis in ziel.items():
|
for otype, preis in ziel.items():
|
||||||
if otype in vorhanden:
|
if otype in vorhanden:
|
||||||
continue
|
continue
|
||||||
@@ -1800,7 +1830,15 @@ class TradingEngine:
|
|||||||
log.info(f"🎯 Pending-Entry ({quelle}) gesetzt: "
|
log.info(f"🎯 Pending-Entry ({quelle}) gesetzt: "
|
||||||
f"{'BUY_STOP' if otype == mt5.ORDER_TYPE_BUY_STOP else 'SELL_STOP'}"
|
f"{'BUY_STOP' if otype == mt5.ORDER_TYPE_BUY_STOP else 'SELL_STOP'}"
|
||||||
f" @ {preis:.3f} (T={t})")
|
f" @ {preis:.3f} (T={t})")
|
||||||
elif err and self._squeeze_pending_err.get(otype) != err:
|
# ⚠⚠ GEDROSSELT UEBER EINEN SCHLUESSEL, NICHT UEBER DEN TEXT
|
||||||
|
# (zweiter Fix am selben Tag). Heute Vormittag lag der Merker
|
||||||
|
# in EINER Variable fuer beide Seiten — behoben. Dann bekam
|
||||||
|
# die Meldung mit `lot_grund` ihre Zahlen, und die freie Margin
|
||||||
|
# aendert sich mit JEDEM Tick: der Text war jedes Mal neu, die
|
||||||
|
# Drossel damit erneut wirkungslos (2 Zeilen/Sekunde).
|
||||||
|
# Gedrosselt wird jetzt auf die URSACHE, nicht auf ihre
|
||||||
|
# Formulierung: alles bis zur ersten Zahl.
|
||||||
|
elif err and self._squeeze_pending_err.get(otype) != _err_key(err):
|
||||||
# ⚠ Nur bei ÄNDERUNG loggen: „Kurs hat das Level bereits
|
# ⚠ Nur bei ÄNDERUNG loggen: „Kurs hat das Level bereits
|
||||||
# passiert" tritt im 1-s-Takt auf, sobald der Kurs am Level
|
# passiert" tritt im 1-s-Takt auf, sobald der Kurs am Level
|
||||||
# klebt.
|
# klebt.
|
||||||
@@ -1812,14 +1850,49 @@ class TradingEngine:
|
|||||||
# Real am 20.08.: 269 Zeilen an einem Tag, 2 je Sekunde.
|
# Real am 20.08.: 269 Zeilen an einem Tag, 2 je Sekunde.
|
||||||
# Eine Drossel, die nie drosselt, ist schlimmer als keine:
|
# Eine Drossel, die nie drosselt, ist schlimmer als keine:
|
||||||
# sie verdeckt im Log genau die Meldung, die zaehlt.
|
# sie verdeckt im Log genau die Meldung, die zaehlt.
|
||||||
self._squeeze_pending_err[otype] = err
|
self._squeeze_pending_err[otype] = _err_key(err)
|
||||||
log.info(f"Pending-Entry ({quelle}) nicht platzierbar "
|
log.info(f"Pending-Entry ({quelle}) nicht platzierbar "
|
||||||
f"({preis:.3f}): {err}")
|
f"({preis:.3f}): {err}")
|
||||||
|
if not t and err and LOT_ZU_KLEIN in err:
|
||||||
|
lot_fehler = True
|
||||||
|
# ⚠ Nur beim LOT-Fehler abraeumen — NICHT bei "Kurs hat das Level
|
||||||
|
# bereits passiert". Der zweite ist voruebergehend und betrifft
|
||||||
|
# naturgemaess nur die eine Seite (der Kurs steht gerade an der
|
||||||
|
# Box-Kante); die Gegenseite bleibt ein gueltiger Einstieg. Der
|
||||||
|
# Lot-Fehler dagegen betrifft das KONTO und damit beide Seiten.
|
||||||
|
if lot_fehler and vorhanden:
|
||||||
|
self._pendings_abraeumen(sym, "Einsatz reicht nicht fuer das "
|
||||||
|
"Mindestlot — halbes Setup aufgeloest")
|
||||||
self._pending_levels = ziel
|
self._pending_levels = ziel
|
||||||
self._pending_quelle = quelle
|
self._pending_quelle = quelle
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"_manage_squeeze_pending: {e}", exc_info=True)
|
log.error(f"_manage_squeeze_pending: {e}", exc_info=True)
|
||||||
|
|
||||||
|
def _pendings_abraeumen(self, sym: str, grund: str) -> int:
|
||||||
|
"""Alle EIGENEN liegenden Orders stornieren. Rueckgabe: wie viele.
|
||||||
|
|
||||||
|
⚠ Fail-safe und still, wenn nichts liegt — die Methode laeuft im
|
||||||
|
1-s-Takt. Geloggt wird nur, wenn wirklich storniert wurde.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not sym:
|
||||||
|
sym = self.data.symbol or ""
|
||||||
|
if not sym:
|
||||||
|
return 0
|
||||||
|
weg = 0
|
||||||
|
for o in (self.trader.pending_orders(sym) or ()):
|
||||||
|
if not self.trader.cancel_pending(o.ticket):
|
||||||
|
self._pending_tickets.pop(int(o.ticket), None)
|
||||||
|
weg += 1
|
||||||
|
if weg:
|
||||||
|
self._pending_levels = None
|
||||||
|
self._pending_quelle = ""
|
||||||
|
log.info(f"⏸ {weg} liegende Order(s) storniert — {grund}")
|
||||||
|
return weg
|
||||||
|
except Exception as e:
|
||||||
|
log.debug(f"_pendings_abraeumen: {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
def _check_auto_squeeze(self):
|
def _check_auto_squeeze(self):
|
||||||
"""Autonomer Entry auf den gemessen-validierten Volatilitäts-Squeeze-Ausbruch
|
"""Autonomer Entry auf den gemessen-validierten Volatilitäts-Squeeze-Ausbruch
|
||||||
(`wave.squeeze.state == 'active'`), NUR wenn flat. Feuert **1× je Ausbruch**
|
(`wave.squeeze.state == 'active'`), NUR wenn flat. Feuert **1× je Ausbruch**
|
||||||
|
|||||||
+14
-5
@@ -117,6 +117,15 @@ def calc_lots(sym: str, price: float, otype: int,
|
|||||||
return min(lots, si.volume_max)
|
return min(lots, si.volume_max)
|
||||||
|
|
||||||
|
|
||||||
|
# ⚠⚠ MASCHINENLESBARER Marker fuer "Einsatz reicht nicht fuer das Mindestlot".
|
||||||
|
# Der Aufrufer (`_manage_squeeze_pending`) raeumt daraufhin das halbe Setup ab.
|
||||||
|
# Bewusst eine KONSTANTE und kein Wort im Fliesstext: eine Erkennung, die an
|
||||||
|
# einer deutschen Formulierung haengt, hoert still auf zu funktionieren, sobald
|
||||||
|
# jemand den Satz umschreibt — und faellt dabei auf die stille Seite (kein
|
||||||
|
# Abraeumen), nicht auf die laute.
|
||||||
|
LOT_ZU_KLEIN = "MINDESTLOT"
|
||||||
|
|
||||||
|
|
||||||
def lot_grund(sym: str, price: float, otype: int,
|
def lot_grund(sym: str, price: float, otype: int,
|
||||||
buffer_pct: float | None = None) -> str:
|
buffer_pct: float | None = None) -> str:
|
||||||
"""Warum liefert `calc_lots` 0? — als lesbarer Satz statt „Lot-Fehler".
|
"""Warum liefert `calc_lots` 0? — als lesbarer Satz statt „Lot-Fehler".
|
||||||
@@ -137,14 +146,14 @@ def lot_grund(sym: str, price: float, otype: int,
|
|||||||
return "Lot-Fehler (Margin je Lot nicht berechenbar)"
|
return "Lot-Fehler (Margin je Lot nicht berechenbar)"
|
||||||
_man = get_manual_margin()
|
_man = get_manual_margin()
|
||||||
if _man > 0:
|
if _man > 0:
|
||||||
return (f"Einsatz {_man:.2f} EUR reicht nicht fuer das Mindestlot "
|
return (f"[{LOT_ZU_KLEIN}] Einsatz {_man:.2f} EUR reicht nicht fuer das "
|
||||||
f"{si.volume_min} (braucht {si.volume_min * mpl:.2f} EUR)")
|
f"Mindestlot {si.volume_min} (braucht {si.volume_min * mpl:.2f} EUR)")
|
||||||
buf = (max(0.01, min(0.99, buffer_pct / 100.0))
|
buf = (max(0.01, min(0.99, buffer_pct / 100.0))
|
||||||
if buffer_pct and buffer_pct > 0 else get_margin_buffer())
|
if buffer_pct and buffer_pct > 0 else get_margin_buffer())
|
||||||
verf = acc.margin_free * buf
|
verf = acc.margin_free * buf
|
||||||
return (f"Einsatz {buf * 100:.0f} % von {acc.margin_free:.2f} EUR freier "
|
return (f"[{LOT_ZU_KLEIN}] Einsatz {buf * 100:.0f} % von {acc.margin_free:.2f} "
|
||||||
f"Margin = {verf:.2f} EUR, Mindestlot {si.volume_min} braucht aber "
|
f"EUR freier Margin = {verf:.2f} EUR, Mindestlot {si.volume_min} "
|
||||||
f"{si.volume_min * mpl:.2f} EUR")
|
f"braucht aber {si.volume_min * mpl:.2f} EUR")
|
||||||
|
|
||||||
|
|
||||||
def calc_lots_risk(sym: str, price: float, otype: int,
|
def calc_lots_risk(sym: str, price: float, otype: int,
|
||||||
|
|||||||
Reference in New Issue
Block a user