Konfiguration im Dashboard änderbar machen
Neuer Abschnitt "Konfiguration" im Dashboard, aus dem pydantic-Schema erzeugt:
76 Felder in 13 Bereichen mit Kurzbeschreibung, typgerechtem Eingabefeld und den
Grenzen aus dem Modell. 70 davon sind änderbar, 45 greifen sofort, 31 sind als
neustartpflichtig markiert. Dazu GET und POST /control/config sowie
/control/config/reset.
Overlay statt Direktschreiben
- config.yaml ist im Container read-only eingehängt. Änderungen landen deshalb in
/data/config.overrides.yaml und werden beim Start über die Basiskonfiguration
gelegt. Rangfolge: YAML, dann ${ENV}, dann TRADEMIND__-Variablen, dann Overlay.
- Gespeichert wird nur, was vom Basiswert abweicht. Ein auf den Ausgangswert
zurückgestelltes Feld verschwindet wieder aus dem Overlay, damit spätere
Änderungen an config.yaml dort erneut durchschlagen.
- Beschädigte oder ungültige Overlays werden protokolliert und ignoriert, statt
den Start zu blockieren.
Übernehmen zur Laufzeit
- apply_config hängt die Laufzeitobjekte auf die neuen Teilkonfigurationen um
(Risiko, Strategie, Regelwerk, Lernmodell, Paper-Broker, Notifier, Log-Level).
- Was nur beim Aufbau ausgewertet wird - Börsenclient, Symbole, Timeframe,
Startkapital, Datenbank, Socket - meldet der Store als neustartpflichtig.
Nebenbei behoben: Der Handels-Loop las Abfrageintervall und Historienlänge nur
einmal vor der Schleife. Eine Änderung an poll_interval_seconds hätte nie
gegriffen; beide werden jetzt je Durchlauf frisch gelesen.
Drei Ausnahmen von "komplett", bewusst gesetzt
- exchange.api_key/api_secret/password/uid werden maskiert angezeigt und nicht
entgegengenommen. Sonst könnte jeder mit Zugriff auf den Port die
Börsenschlüssel auslesen oder austauschen.
- mode lässt sich zwischen paper und backtest umstellen, nicht auf live.
- live_confirmation ist nicht schreibbar.
Zusammen verhindern sie, dass sich der Bot über das Netz schrittweise auf
Echtgeldhandel umstellen lässt.
222 Tests (36 neue), ruff sauber. Darunter Prüfungen, dass Geheimnisse in keiner
Antwort auftauchen und dass Tippfehler in RESTART_REQUIRED oder NON_WRITABLE
auffallen. Im Browser durchgeklickt: Änderung sofort wirksam, neustartpflichtige
Felder korrekt gemeldet und nach Neustart aktiv, Zurücksetzen einzeln und
komplett, alle Schutzregeln mit HTTP 400 samt lesbarer Begründung.
This commit is contained in:
@@ -15,6 +15,49 @@ _ENV_PATTERN = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}")
|
||||
|
||||
LIVE_CONFIRMATION_PHRASE = "I_UNDERSTAND_THE_RISK"
|
||||
|
||||
# Felder, die niemals über das Netz ausgeliefert oder entgegengenommen werden. Sie werden
|
||||
# im Dashboard maskiert angezeigt und bleiben ausschließlich über die Umgebung setzbar.
|
||||
SECRET_FIELDS: frozenset[str] = frozenset(
|
||||
{"exchange.api_key", "exchange.api_secret", "exchange.password", "exchange.uid"}
|
||||
)
|
||||
|
||||
# Felder, deren Änderung erst nach einem Neustart greift: Sie werden beim Aufbau der
|
||||
# Laufzeitobjekte einmalig ausgewertet (Börsenclient, Broker, Datenbank, Socket …).
|
||||
RESTART_REQUIRED: frozenset[str] = frozenset(
|
||||
{
|
||||
"mode",
|
||||
"live_confirmation",
|
||||
"market.symbols",
|
||||
"market.timeframe",
|
||||
"market.history_bars",
|
||||
"paper.starting_balance",
|
||||
"paper.quote_currency",
|
||||
# Wirkt erst beim nächsten Start – der aktuelle Handelszustand bleibt, wie er ist.
|
||||
"trading.autostart",
|
||||
"strategy.name",
|
||||
"strategy.learner.enabled",
|
||||
"strategy.learner.model_path",
|
||||
"strategy.learner.replay_size",
|
||||
"storage.database_path",
|
||||
"storage.overrides_path",
|
||||
"server.enabled",
|
||||
"server.host",
|
||||
"server.port",
|
||||
"server.enable_metrics",
|
||||
}
|
||||
)
|
||||
|
||||
# Ganze Abschnitte, die nur beim Start ausgewertet werden.
|
||||
RESTART_REQUIRED_PREFIXES: tuple[str, ...] = ("exchange.", "backtest.")
|
||||
|
||||
|
||||
def requires_restart(path: str) -> bool:
|
||||
return path in RESTART_REQUIRED or path.startswith(RESTART_REQUIRED_PREFIXES)
|
||||
|
||||
|
||||
def is_secret(path: str) -> bool:
|
||||
return path in SECRET_FIELDS
|
||||
|
||||
|
||||
class Mode(str, Enum):
|
||||
PAPER = "paper"
|
||||
@@ -160,6 +203,9 @@ class TradingConfig(_Base):
|
||||
|
||||
class StorageConfig(_Base):
|
||||
database_path: str = "/data/trademind.sqlite3"
|
||||
# Im Dashboard geänderte Werte. Liegt bewusst im beschreibbaren Datenvolume, weil
|
||||
# die Konfigurationsdatei üblicherweise read-only eingehängt ist.
|
||||
overrides_path: str = "/data/config.overrides.yaml"
|
||||
|
||||
|
||||
class ServerConfig(_Base):
|
||||
|
||||
Reference in New Issue
Block a user