8fd99ee6fa4f817d7220607a71a93f2e6fddfcf0
56
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8fd99ee6fa |
Positionsverwaltung: Herkunft sichtbar, Zuordnung aenderbar, Sperren einstellbar
Erster Teil von #485. Der PositionManager im Core beantwortet drei Fragen, die sich beim Debuggen sofort stellen: Wem gehoert eine Position, was sagt der Broker dazu, und was ist gesperrt. Drei Zugaenge auf denselben Dienst - das Fenster der Shell, --positions und --reassign im kopflosen Betrieb, GET /api/positions. Der Testbetrieb laeuft als systemd-Dienst; ein Werkzeug, das nur in der Oberflaeche existiert, fehlt genau dann, wenn man es braucht. Entscheidungen, die im Code begruendet stehen: - BrokerQuantity ist nullable und heisst "nicht bekannt", nicht "null Stueck". Ohne Verbindung sieht ein leergeraeumtes Konto genauso aus wie eine Stoerung; aus dem einen darf nicht auf das andere geschlossen werden. - Umbuchen aendert die Zuordnung, nie die Menge. Eine Umbuchung, die eine Stueckzahl bewegt, waere ein Handel ohne Order und stuende in keiner Historie. - Gegen eine entgegengesetzte Position wird abgelehnt: Das waere eine Glattstellung und gehoert ueber eine Order gemacht, mit Preis und Historie. - Das Quellbuch ist Pflicht - Schutz gegen die falsche Zeile -, ebenso eine Begruendung, sonst ist der Eingriff spaeter nicht mehr erklaerbar. - Das Cap des Zielbuchs sperrt NICHT. Die Position existiert bereits; sie zu benennen erzeugt kein neues Risiko. Wuerde die Grenze das verhindern, bliebe jede zu grosse Position fuer immer in UNASSIGNED. - Der REST-Endpunkt ist lesend. Der Listener kennt keine Anmeldung; ein schreibender Zugriff waere ein Eingriff in die Buecher, den niemand belegen kann. Beim ersten Testlauf gefunden: (Module, ContractKey) ist der Primaerschluessel von core_position, das Buch laesst sich in der Zeile nicht ueberschreiben. Umgebucht wird deshalb durch Loeschen und Neuanlegen. 471 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c87b0195fd |
Logdateinamen haengen nicht mehr am Kalender der Host-Kultur
Im Logverzeichnis der Test-VM lagen neben 2026-09-03.jsonl auch 1405-06-12.jsonl und 2569-09-03.jsonl - dieselben Tage im Sonnen-Hidschra- und im buddhistischen Kalender. Eine interpolierte Zeichenkette formatiert mit CurrentCulture, und die bringt ihren Kalender mit. Betroffen waren beide Schreibpfade des LoggingService und die Lesestelle im DossierService, die denselben Namen wieder zusammensetzt. CA1305 greift hier nicht: die Regel sieht string.Format und ToString, nicht die interpolierte Form. Die .editorconfig deckt diesen Fall also nicht ab. FormattableString.Invariant an allen drei Stellen, dazu ein Regressionstest ueber fa-IR, th-TH und de-DE - mit Gegenbeweis, dass die naive Form tatsaechlich abweicht, damit der Test nicht eines Tages unbemerkt trivial wird. Ausserdem: Verweise auf die Bugtracker-Nummern richtiggestellt (488/489). 461 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e7e3970dec |
Orders, die beim Broker liegen, gelten nicht mehr als gescheitert
Am geschlossenen Markt gemessen: IBKR nimmt eine Order an und merkt sie fuer die naechste Eroeffnung vor. Dazu schickt es Code 399 - eine Mitteilung, deren Text mit "Warning" beginnt. Der Adapter las jede Meldung aus dem Fehler-Callback als Ablehnung, meldete "Order abgelehnt", und das Kapitalmodell gab die Bindung frei. Auf dem Testkonto lagen dadurch 67 Orders ueber 306 BAS, 140 SAP und 43 ALV beim Broker, vorgemerkt fuer Montag 09:00, ohne eine einzige Reservierung dahinter. OrderResult kannte nur "erfolgreich" und "gescheitert". Der haeufigste Fall passte in keines von beiden. Es gibt jetzt einen dritten Ausgang - Working: abgesendet, Ausgang offen. Die Trennlinie ist, ob BEWIESEN ist, dass beim Broker nichts liegt; ohne Beweis bleibt das Kapital gebunden. Eine Bindung, die zu lange steht, kostet Spielraum und faellt auf. Eine, die zu frueh verschwindet, kostet Geld und faellt nicht auf. - 399/404 sind Ordermeldungen, keine Ablehnungen; nur 201/202/203 u. a. beenden eine Order beweisbar. Ein Verbindungsabbruch beendet unsere Sicht, nicht die Order. - IsDeadStatus ist enger als IsTerminalStatus: "Inactive" ist kein Beweis. - ExecutionService haelt die Bindung bei Working (MarkSubmittedAsync statt CancelReservationAsync) und journalisiert Pending/OrderWorking statt Failed. - Neu lesbar: das Orderbuch des Brokers (reqAllOpenOrders) samt Storno. --open-orders stellt Orderbuch und eigene Bindungen gegenueber, --cancel-order <ID>|all --confirm nimmt zurueck (Sammelstorno nur im Paper-Modus). - Der Wiederanlauf-Abgleich lief bisher gegen eine leere Orderliste und haette ab jetzt jede gesunde Bindung in Quarantaene gestellt. Er sitzt jetzt im ReservationWatchdogWorker, hinter dem Beweis, dass der Broker antwortet. 457 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1dac482cc7 |
DebugTrading: ein Modul, das absichtlich Last erzeugt
Alles, was den Handel absichert - Limits, Deckung, Kapitalpool, Reservierungen, Abgleich, Sperren - war bisher ausschliesslich durch Unit-Tests mit gemockten Gegenstellen belegt. Im Betrieb gab es fast keine Trades. Ein Fehler in der Buchungskette faellt so erst auf, wenn eine echte Strategie scharf geht. Das Modul eroeffnet kleine Positionen, haelt sie eine gewuerfelte Zeit von Minuten bis Tagen und schliesst sie selbst. Es soll keine Rendite erzeugen, sondern Last; die Auswahl ist zufaellig und ausdruecklich nicht renditegetrieben. Planung und Sicherungen sind reine Rechnung mit einer von aussen gereichten Zufallsquelle - genau deshalb ist ein Modul, das absichtlich wuerfelt, ueberhaupt pruefbar. 14 Tests. Die Sicherung, die nicht verhandelbar ist: Der Worker prueft bei JEDEM Lauf, ob der Handelsmodus eindeutig Paper ist, und wirft sonst. Nicht nur beim Start - Einstellungen werden kopiert, wenn eine Instanz umzieht. Geprueft wird gegen den Rohwert UND den geparsten Modus: TradingSettings.ParsedMode faellt bei einem unbekannten Wert auf Paper zurueck, und darauf darf sich diese Pruefung nicht verlassen. Dazu Tagesbudget (in Betriebszeitzone, nicht UTC), Obergrenze offener Positionen, Grenze je Trade und ein Not-Aus, der das Eroeffnen stoppt und offene Positionen weiter betreut. Schliessen hat Vorrang vor Eroeffnen: Sonst koennte eine erreichte Obergrenze verhindern, dass faellige Positionen je geschlossen werden - die Sperre bliebe fuer immer. Beim Bauen hat der Architekturtest aus #474 seinen ersten echten Fang gemacht: Das Modul griff nach IBrokerClient, um Kurse zu holen. Das verstoesst gegen Regel 4. Statt den Test aufzuweichen ist eine lesende Kursquelle entstanden (IQuoteSource) - wer einen Kurs braucht, soll dafuer nicht den Zugang bekommen, mit dem sich Orders platzieren lassen. Auf dem Testserver nachgewiesen: Das Modul laeuft im Takt, plant, wird bei fehlendem Kurs sauber abgelehnt und meldet das - und mit Trading.Mode=Live verweigert es den Dienst, waehrend unabhaengig davon auch der Broker-Adapter den Paper-Port gegen den Live-Modus sperrt. Zwei Schichten, die nichts voneinander wissen. 438 Tests gruen. Arbeit an #468. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
27b594ca8f |
Sperren des Betreibers: Positionen und Instrumente, im Core durchgesetzt
Vier Stufen je Position (frei, nur schliessen, nur aufbauen, gesperrt) und je Instrument ein Handelsverbot samt harter Bestandsobergrenze. Beides gilt fuer Module; der manuelle Handel in TWS bleibt unberuehrt. Die Pruefung sitzt im RiskService, nicht im Modul: Eine Sperre, die ein Modul selbst pruefen muesste, ist keine Sperre, sondern eine Bitte. Sie laeuft NACH der fachlichen Bewertung, weil erst dort feststeht, welcher Teil eines Auftrags eroeffnet und welcher schliesst - genau daran haengen die Stufen. Drei Entscheidungen, die im Betrieb zaehlen: "Gesperrt" verhindert auch den Ausgang. Eine Position, die aus Risikogruenden geschlossen werden muesste, bleibt liegen, bis jemand die Sperre loest. Das ist gewollt und steht als eigener Test da, damit es niemand fuer einen Fehler haelt. Ein Handelsverbot sperrt das Eroeffnen, nicht das Schliessen. Ein nachtraeglich gesetztes Verbot darf keine Position einsperren, die man gerade loswerden will; wer auch das will, nimmt die Positionssperre. Die harte Stueckzahl zaehlt den Bestand ueber ALLE Buecher, MANUAL eingeschlossen, und Short-Positionen mit ihrer Groesse statt ihrem Vorzeichen: "hoechstens 100" meint die Groesse der Verpflichtung. Sie ersetzt das prozentuale Konzentrationslimit nicht - der Prozentsatz waechst mit dem Konto, die Stueckzahl ist eine Aussage ueber ein bestimmtes Papier. Es zieht die engere Grenze. TradingRuleService haelt die Regeln im Speicher, weil sie bei jeder Risikopruefung gebraucht werden, sich aber selten aendern. Laesst sich die Liste NICHT laden, gilt das nicht als "keine Sperren": der Dienst meldet es und sperrt, bis sie wieder lesbar ist. Eine Sperre, die bei einem Datenbankfehler verschwindet, waere keine. Ein unlesbarer Stufenwert wird ebenso als schaerfste Stufe gelesen. Auf dem Testserver angewendet (Tabellen core_position_lock und core_instrument_rule) und der Dienst damit neu gestartet - keine Ladefehler, der Handel bleibt frei. Zur Probe stehen dort die beiden Beispiele des Betreibers: WIRECARD gesperrt, NVDA hoechstens 100 Stueck. 425 Tests gruen, 11 davon neu. Arbeit an #485 und #486; die Oberflaeche fehlt noch, die Durchsetzung steht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0feeeba815 |
Optionskette und Bewertungsgroessen im Core
Der Core kennt jetzt die handelbaren Verfallstage und Strikes eines Basiswerts
(reqSecDefOptParams) und die Bewertungsgroessen eines Kontrakts
(tickOptionComputation). Damit steht der letzte Baustein, den eine
Wheel-Strategie braucht, um ueberhaupt einen Kontrakt auswaehlen zu koennen.
OptionChain ist bewusst nur eine Liste und trifft keine Marktentscheidung. Die
Auswahlhilfen daran sind reine Rechnung - "der Verfall bei etwa 35 Tagen" ist
eine Kalenderfrage, "welches Delta will ich" nicht. Letzteres gehoert ins Modul.
Zwei Eigenheiten der TWS-API sind beim Messen aufgefallen und haben je einen
Fehler gekostet:
1. Greeks kommen NICHT im Snapshot. reqMktData mit snapshot=true liefert Geld-,
Brief- und letzten Kurs, aber keine tickOptionComputation. Es braucht ein Abo,
das beim ersten Modell-Tick wieder abbestellt wird.
2. TWS meldet je Basiswert MEHRERE Serien. Fuer AAPL kamen "AAPL" und "2AAPL" -
letztere eine Sonderserie mit eigenen Ausuebungspreisen. Der erstbeste Satz
war die falsche: Strike 320 in "2AAPL" gibt es nicht, TWS antwortete mit 200
("No security definition has been found"). Die Auswahl nimmt jetzt SMART vor
allen anderen Boersen und darin die Handelsklasse, die dem Symbol entspricht.
Massgeblich ist ausserdem allein das Modell-Feld (13 bzw. 83 bei verzoegerten
Daten). Die Varianten zu Geld-, Brief- und letztem Kurs fehlen bei illiquiden
Serien und lieferten sonst ein Delta aus einem Kurs, den niemand stellt. Fehlende
Werte meldet TWS als double.MaxValue oder -1; sie werden zu null, nicht zu 0 -
ein Delta von 0 hiesse "reagiert nicht auf den Basiswert".
Gemessen gegen das Paper-Gateway:
AAPL (USD, SMART/AAPL, x100): 22 Verfallstage, 123 Strikes
Auswahl wie beim Wheel: AAPL|20261009|P|320 (Kurs 320.01, 34 Tage Laufzeit)
Deckungsbedarf je Kontrakt: 32.000,00 USD
IV 24.5 %, Delta -0.471, Gamma 0.0169, Vega 0.392, Theta -0.123 (verzoegert)
Der Schalter --option-chain SYMBOL [--currency X] faehrt genau diesen Ablauf und
bleibt als Pruefwerkzeug. 414 Tests gruen.
Behebt #299.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b618efa9ce |
Modulkontexte an die maßgebliche Datenbank; Shell-Shutdown weiter untersucht
Zwei Befunde aus dem ersten Dauerbetrieb auf dem Testserver. #487: Der Fix zu #389 hatte nur den Core auf DatabaseEndpoint umgestellt. Alle vier Modulkontexte lasen die Konfiguration weiter selbst und landeten auf dem Platzhalter aus EffectiveConnectionString, sobald die Datenbank nur in settings.json steht - "Access denied for user 'root'@'localhost'", waehrend der Core im selben Prozess sauber arbeitete. Gefunden 30 Sekunden nach dem ersten Start des Dienstes; kein Test und kein --check hatte es gesehen, weil --check nur die Core-Verbindung oeffnet. Die Kontexte loesen DatabaseEndpoint jetzt ueber die IServiceProvider-Ueberladung von AddDbContextFactory auf. Der Platzhalter meldet sich ausserdem nicht mehr als root an, sondern als "KEINE-DATENBANK-KONFIGURIERT" - die alte Meldung sah nach einem Rechteproblem aus, obwohl gar keine Verbindung konfiguriert war. #396 traegt NICHT. Die Pruefung auf der VM zeigt: Das Signal kommt an, der Handler laeuft (Logzeile steht), aber der Prozess bleibt stehen. Zwei Ursachen sind dabei aufgedeckt und behoben worden, eine dritte ist offen: 1. Ueber Avalonia.Application.Current war die Lebensdauer aus dem Signal-Thread nicht erreichbar - der Handler lief ins Leere. Sie wird jetzt im Hauptthread erzeugt (SetupWithLifetime + lifetime.Start) und dem Handler mitgegeben. 2. Die Sicherheitsabfrage der Shell fing das Schliessen ab und wartete auf einen Klick, den bei einem Signal niemand macht. Ein Signal ist keine Frage; der Handler uebergeht die Abfrage jetzt ausdruecklich. 3. OFFEN: Der Dispatcher-Rueckruf wird nicht ausgefuehrt - weder die Erfolgs- noch die Fehlerzeile erscheint, obwohl beide protokolliert wuerden. Der UI-Thread nimmt die Nachricht offenbar nicht an. Die Messpunkte bleiben im Code: ohne sie war von aussen nicht zu unterscheiden, ob das Signal ankommt, ob die Lebensdauer fehlt oder ob der Rueckruf haengt. 404 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3bccd90d97 |
Eigentumsregeln 3 und 4: gebundener Ausfuehrungszugang und eine Grenze mit Zaehnen
Regel 3 verlangt, dass die Buchzuordnung serverseitig gesetzt wird und TradeSignal.SourceModule nicht vertrauenswuerdig ist. Bisher war nur der unbekannte Name abgefangen; ein Modul konnte weiterhin das Kuerzel eines ANDEREN registrierten Moduls eintragen und dessen Cap, dessen Konzentration und dessen GuV belasten. Jedes Modul bekommt jetzt einen an sein Buch gebundenen IModuleExecution-Zugang statt des gemeinsamen IExecutionService. Der Zugang ueberschreibt die Herkunft mit seinem eigenen Buch - was im Signal steht, spielt keine Rolle mehr. Der Zugang steht ausdruecklich NICHT im Container: was dort steht, kann jedes Modul aufloesen, auch den Zugang eines anderen. Der Host uebergibt stattdessen eine Fabrik beim Registrieren (neue Ueberladung von IModule.RegisterServices mit Vorgabe, damit ein nicht handelndes Modul nichts davon wissen muss), und das Modul reicht den erzeugten Zugang direkt an seine Strategie. Regel 4 - Module bekommen den Broker nicht per DI - war eingehalten, aber nicht erzwungen. Jetzt zwei Tests ueber alle vier Modul-Assemblies: der eine prueft per Reflection Konstruktorparameter, Felder und Eigenschaften auf IBrokerClient, IBrokerPortfolioReader und IExecutionService; der andere durchsucht die Modulquellen nach dem zweiten Weg, dem Griff in den Container. Beide Gegenproben mit einem absichtlichen Regelbruch gefahren: jeder Test schlaegt bei genau seinem Weg an. Getrennte Container je Modul waeren der harte Weg, aendern aber die Modularchitektur. Der Fall, um den es geht, ist ein NEUES Modul, das den Core versehentlich umgeht - und den faengt der Test beim Uebersetzen. 404 Tests gruen, auf Windows und auf der Test-VM. Behebt #474, damit ist auch #302 vollstaendig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
49a17c2559 |
Roadmap: Positions- und Instrumentenverwaltung (#485, #486)
Zwei Vorhaben des Betreibers, beide mit demselben Kern: die Durchsetzung gehoert in den Core, die Oberflaeche ist nur die Bedienung. Eine Sperre, die ein Modul selbst pruefen muesste, ist keine Sperre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
40f7bb0d6e |
Bestandsabgleich gegen den Broker: Zuteilung, Verfall, Fremdbestand
Zuteilung und Verfall aendern Positionen ohne eine Order von uns - es gibt kein Ereignis, an das sich ein Abgleich haengen koennte. Nach einem Verfallstag fuehren wir Kontrakte, die es nicht mehr gibt; nach einer Zuteilung liegen Aktien im Konto, die in keinem Buch stehen. Beides verschiebt still die Deckungslage: Ein Covered Call, dessen Aktien in Wahrheit abgerufen wurden, ist ab dann ein nackter Call. PositionReconciler ist reine Rechnung und deutet die Differenz, statt nur Mengen anzugleichen. Verschwindet ein geschriebener Put und tauchen zugleich Aktien des Basiswerts auf, gehoeren diese Aktien dem Buch, das den Put geschrieben hat - landeten sie in UNASSIGNED, verloere ein Wheel-Modul genau den Bestand, aus dem es den naechsten Call schreiben muss. Der Einstand ist dabei der Strike, denn zu genau dem Preis wurde geliefert. Passt die Aktienbewegung nicht zur Groesse der Option, gilt sie NICHT als Zuteilung. Verfall wird nur angenommen, wenn der Verfallstag erreicht ist. Ein Kontrakt, der vorher verschwindet, ist eine Abweichung und wird als solche gemeldet - von Hand geschlossen ist kein Verfall. Die gefaehrlichste Stelle ist die leere Antwort: Die Leseschnittstelle liefert bei gestoerter Verbindung eine leere Liste, die von einem leeren Konto nicht zu unterscheiden ist. Solange wir selbst Positionen fuehren, gilt sie deshalb als Stoerung. Ohne diese Sperre setzte ein Verbindungsabbruch jede Position auf null. Gehandelt wird nie. Der Abgleich bucht ausschliesslich nach. BrokerPosition traegt jetzt das Instrument (IbkrMapping.ToInstrument); ein Kontrakt, den unser Modell nicht kennt - Future, Devise, Fonds -, liefert null und wird uebersprungen statt als Aktie gedeutet. Gemessen auf dem Testserver gegen das Paper-Konto: vier Altlasten erkannt (GOOG, MIGA:EUR, die SAP-ADR aus dem Fehllauf und zwei ungebuchte SAP:EUR), alle an UNASSIGNED gebucht, das Buch DIAG unangetastet. Gegenprobe danach: "Bestand stimmt ueberein (5 Positionen)". 402 Tests gruen. Erledigt den offenen Teil von #300, Kern von #306. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b042396f2d |
Kapitalmodell rechnet Waehrungen um, statt sie zu addieren
Solange jedes Instrument in USD notierte, war die Frage gegenstandslos. Mit waehrungsfaehigen Instrumenten ist sie es nicht mehr: CapitalService summierte BoundCapital ueber alle Positionen unabhaengig von deren Waehrung und verglich das Ergebnis mit NetLiquidation in Kontobasiswaehrung. Dasselbe galt fuer die Konzentration je Basiswert und fuer die offenen Reservierungen, die von einer Groesse in Basiswaehrung abgezogen wurden. Auf dem Testkonto ist die Basis EUR und USD steht bei 0,8611: jede USD-Position ging bisher rund 16 Prozent zu hoch in das verteilbare Kapital ein. Die Richtung des Fehlers wechselt mit dem Kurs - bei einer starken Fremdwaehrung wird die Auslastung unterschaetzt und ein Buch darf mehr aufmachen als es soll. Die Kurse kommen aus dem Ledger des Brokers ($LEDGER:ALL, Tag ExchangeRate), nicht aus einer eigenen Quelle. Das ist Absicht: NetLiquidation und TotalCashValue sind mit genau diesen Kursen gebildet. Mit einem fremden Kurs verglichen wir den Positionswert gegen eine Kontogroesse, die anders gerechnet wurde. Fuer die Buchhaltung gilt das Gegenteil - dort ist der amtliche Tageskurs der richtige, das ist ein anderer Zweck (#321). Fehlt ein Kurs, wird nicht geraten: der Snapshot meldet FxComplete=false, der Positionswert gilt als Untergrenze, und der Kauf wird angehalten. Schliessen bleibt moeglich. Eine Reservierung ohne Kurs wird dagegen roh angesetzt statt uebersprungen - sie zu ignorieren hiesse, gebundenes Geld als frei zu fuehren. Gemessen am Paper-Gateway: "Konto: Umrechnungskurse EUR 1, USD 0.8611 (Basis EUR)". 388 Tests gruen. Behebt #480. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b221e0447b |
Roadmap: Begruendungen zu Waehrungsfaehigkeit, #479 und #480
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
614392afc3 |
Handelshistorie fasst Optionsschluessel; Order-ID landet in der Bindung
Zwei Maengel, sichtbar geworden beim Pruefen der vier Tabellen nach dem Order-Nachweis (#293). core_trade_history.Symbol war varchar(20) und traegt seit der Optionserweiterung den Instrumentenschluessel statt des Ticker-Symbols. "MSFT|20260918|P|302.5" sind 21 Zeichen - gekuerzt auf "MSFT|20260918|P|302." liest sich das als Strike 302. Ein still verfaelschter Eintrag in der Handelshistorie ist schlimmer als gar keiner. Jetzt 64 wie core_position.ContractKey. MarkSubmittedAsync rief niemand auf: im synchronen Pfad gibt es keinen beobachtbaren Zeitpunkt zwischen Absenden und Endstatus. BrokerOrderId blieb dadurch leer, und der Abgleich beim Neustart haette keine Bindung einer Order zuordnen koennen - genau die Aufgabe, fuer die die Spalte da ist. Die ID wird jetzt beim Abschluss festgehalten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b517f7a637 |
OrderRequest entsteht aus dem Instrument, nicht aus Einzelfeldern
Beim ersten erfolgreichen Order-Nachweis (#293) gegen das Paper-Konto lief die Order auf die falsche Notierung: geprueft, bepreist und reserviert wurde SAP:EUR (XETRA, 188,74 EUR), ausgefuehrt wurde die SAP-ADR in New York zu 216,68 USD. Das USD-Guthaben rutschte dadurch von -3.751 auf -4.185 - also genau der Margin-Kredit, den Schicht C verhindern soll, aufgenommen an der Sicherung vorbei. Ursache: ExecutionService baute die OrderRequest aus Einzelfeldern und setzte Currency nicht; die Eigenschaft fiel still auf USD zurueck. Es gab keine Fehlermeldung, und die Buecher wiesen anschliessend eine EUR-Position aus, die es beim Broker nie gab. Behebung nicht durch Nachtragen der Zuweisung, sondern baulich: OrderRequest.For(instrument, ...) uebertraegt die Identitaet vollstaendig. Wer die Felder einzeln setzt, baut ein zweites, halb ausgefuelltes Instrument - das darf im Orderpfad nicht mehr moeglich sein. Zwei Regressionstests, die genau diesen Fall fangen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9e0d3b229a |
Kursabfrage nimmt das Instrument statt nur das Symbol
Ohne Waehrung wird jeder Kontrakt als USD gesucht: ein Wert, der nur in EUR notiert, ist dann "nicht aufloesbar", und die Ursache steht nirgends. Beim Order-Nachweis gegen MIGA:EUR genau so aufgetreten - die Kontraktaufloesung fuer die ORDER war bereits waehrungsfaehig, die fuer den KURS nicht, und damit brach die Kette eine Stufe vorher ab. Dieselbe Aenderung ist die Voraussetzung fuer Optionskurse (#299): ohne Kontraktangaben laesst sich eine Option ueberhaupt nicht abfragen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2a73172e27 |
Instrumente sind waehrungsfaehig
Die Waehrung gehoert zur Identitaet eines Instruments und nicht zu
seiner Beschreibung: derselbe Ticker kann an mehreren Plaetzen in
verschiedenen Waehrungen notieren, und ein Kauf darf nur aus Bargeld
derselben Waehrung finanziert werden (Kapitalmodell §4 Schicht C). Zwei
Notierungen als eine Position zu fuehren, wuerde Bestand und Deckung
vermischen.
USD bleibt im Instrumentenschluessel IMPLIZIT ("AAPL", nicht
"AAPL:USD"). Andernfalls waere jede Bestandszeile ab sofort ein anderes
Instrument als vorher und aus einer Position wuerden zwei. Trennzeichen
ist der Doppelpunkt und nicht der Punkt - Ticker enthalten Punkte
(BRK.B).
Neu: Currencies (kanonische Schreibweise an allen vier Stellen, die sie
gleich haben muessen), Currency an OptionSpec, InstrumentRef,
TradeSignal, OrderRequest und core_position. CapitalService nimmt die
Waehrung ab jetzt aus dem Instrument statt vom Aufrufer - wer sie
uebergeben koennte, koennte die Deckungspruefung gegen den falschen Topf
laufen lassen. --verify-order kennt --currency.
Anlass: das Paper-Konto haelt EUR 91.832 und USD -3.751. Ein
USD-Nachweis waere ein USD-Kredit gewesen und wurde von Schicht C
zu Recht abgelehnt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
bd04642735 |
Bargeld je Waehrung: $LEDGER:ALL statt $LEDGER, und melden was ankam
Beim ersten echten Order-Nachweis gegen das Paper-Konto (#293) lehnte Schicht C jeden Kauf mit "Waehrungsdeckung [USD]: benoetigt 344, verfuegbar 0" ab, obwohl das Konto Bargeld hat. Ursache: das nackte "$LEDGER" liefert nur die Zusammenfassung in Kontobasiswaehrung, und die traegt als Waehrungskennung "BASE" - also genau den Wert, den die Auswertung ueberspringen muss, damit dasselbe Geld nicht zweimal zaehlt. Die Aufschluesselung blieb dadurch leer, und eine leere Aufschluesselung heisst fuer Schicht C bewusst "Deckung unbekannt, also ablehnen". Mit ":ALL" kommen die Bestaende je Waehrung. Dazu eine Logzeile mit dem, was der Broker gemeldet hat. Ohne sie ist eine Ablehnung durch Schicht C von aussen nicht von einem echten Geldmangel zu unterscheiden - man sieht "verfuegbar 0" und weiss nicht, ob kein Geld da ist oder keine Auskunft kam. Genau diese Stunde hat der Fund gekostet. Gemessen am Paper-Konto DUR371528: EUR 91.832,22 / USD -3.751,01. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
084cd7ef77 |
Options-Fundament und Kapitalmodell im Core; Modul OptionsWheel
Das OptionsWheel setzt auf Dingen auf, die es im Core nicht gab. Diese Sitzung baut sie. #298 Optionen als Instrument. InstrumentKind/OptionSpec und ein druckbarer Instrumentenschluessel ("AAPL|20260918|C|150"); TradeSignal und OrderRequest tragen ein optionales OptionSpec, ohne das sich der Aktienpfad unveraendert verhaelt. Position.Notional rechnet ab jetzt mit der Kontraktgroesse - vorher war jede Option um Faktor 100 zu klein bewertet und damit jedes Risikolimit wirkungslos, lautlos und in die gefaehrliche Richtung. Daneben neu BoundCapital: ein Short Put bindet Strike x Kontraktgroesse, nicht die vereinnahmte Praemie. #300 sell-to-open. Jeder Auftrag zerfaellt in einen schliessenden und einen eroeffnenden Teil; der schliessende wird von keinem Limit verhindert - auch nicht vom Kaufstopp, denn ein Rueckkauf baut eine Verpflichtung ab. Deckungspruefung im Core, nicht im Modul: Short Call nur mit freien Aktien, Short Put nur mit reservierbarem Bargeld. #302-#305 Kapital- und Buchmodell. Buecher mit reservierten Namen und Eigentumsregeln, Pool mit Caps und Headroom, Margin-Sperre in vier Schichten, core_reservation mit Lebenszyklus, Frist, Idempotenz ueber (SignalId, ContractKey) und Quarantaene beim Start. #309/#310 Modul OptionsWheel: ow_-Schema mit fuenf Tabellen, Zustandsautomat, StrikeSelector, RollDecider, PremiumMath - vollstaendig ohne Broker testbar. Der Handelsbetrieb bleibt gesperrt, solange #299 und #294 fehlen; das Modul sagt das ueber GetActivationBlocker, statt still nichts zu tun. #293 --verify-order am Daemon: eine Order durch die vollstaendige Kette (Buch, Risiko, Bindung, Order, Fill, Buchung, Abschluss). Ohne --confirm ein Trockenlauf; Abbruch ausserhalb von Paper-Modus UND Paper-Port. Drei Widersprueche in den Konzepten sind dabei entschieden worden: Schicht B heisst ab jetzt "kein UNGEDECKTER Short" (der Grund der Regel war der Kredit, nicht der Short), die Hausreserve wird einmal abgezogen statt zweimal, und der Positionsschluessel ist eine Spalte statt eines Sechser-Verbunds (MySQL verlangt NOT NULL fuer Schluesselspalten). Begruendungen stehen in docs/ROADMAP.md. Enthaelt ausserdem die noch nicht committete Arbeit aus der vorigen Sitzung: die Fehlerbehebungen aus Epic X (WorkerBase-Intervall, BackupWorker, --check gegen die Datenbank, CapitolTrades-429, SIGTERM) samt DatabaseEndpoint, MysqlDumpTool und der Roadmap-Umstellung. Sie lag im Arbeitsbaum und liess sich nicht abtrennen, weil beide Staende dieselben Dateien beruehren. 213 -> 369 Tests, Build ohne Warnung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9f66183f1c |
Eine Roadmap statt sieben Konzepte; Quelldokumente ins Archiv
Der offene Stand lag ueber sieben Konzepte, zwei Referenzdokumente und die
Phasen-Checkliste der Architektur verteilt. Dieselbe Aufgabe stand teils
doppelt unter zwei Namen - die asynchrone Fill-Verfolgung etwa als "bekannte
Grenze" in IBKR-Integration.md und zugleich als W-2 im OptionsWheel-Konzept.
Wer wissen wollte, was als Naechstes ansteht, musste alles neun lesen.
docs/ROADMAP.md fuehrt das zusammen:
- Fuenf Stufen in Abhaengigkeitsreihenfolge, von "Fundament schliessen" bis
zum OptionsWheel, dazu vier laufende Bahnen (Auslieferung, Accounting,
Supervisor, technische Schulden).
- Jede Zeile traegt ihre Herkunft (W-2, P5, Kapitalmodell 5, ...), damit die
Herleitung im Archiv auffindbar bleibt.
- Eigene Abschnitte fuer Zurueckgestelltes und Verworfenes. Zurueckgestellte
Ideen nennen ausdruecklich, WAS sie wieder aktuell macht; verworfene nennen
den Grund, damit sie nicht in sechs Monaten erneut vorgeschlagen werden.
- Erledigtes bleibt als Zeile mit Datum stehen statt zu verschwinden.
Archiv (git erkennt alle sieben als Umbenennung, Historie bleibt):
docs/konzepte/* -> docs/archiv/
docs/Kapital-und-Buchmodell.md -> docs/archiv/
Jedes archivierte Dokument bekommt oben einen Vermerk, warum es erhalten bleibt
und wo der lebende Stand steht. Inhaltlich ist keines veraendert.
Weiter gepflegt werden ARCHITECTURE.md (Aufbau + Phasen-Historie),
IBKR-Integration.md (Adapter-Design und Grenzen) und die TWS-Setup-Checkliste -
das sind Referenzen, keine Planung. Ihre eigenen Offen-Listen verweisen jetzt
mit Roadmap-Kennung dorthin, statt einen zweiten Stand zu fuehren.
Alle 63 relativen Markdown-Links geprueft, keiner tot. Fuenf Pfadverweise in
Code, csproj und systemd-Unit mitgezogen; die Unit zeigte auf die
Portierungsanalyse, die ausdruecklich den Stand VOR dem Umbau beschreibt - jetzt
auf ARCHITECTURE.md. Build 0 Warnungen, 198/198 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e1546bd1b1 |
Fruehjahrsputz: toter Code, ungenutzte Symbole, Dokumentenstand
Alles Entfernte war nachweislich ohne Aufrufer. Build, 198/198 Tests, Smoke-UI
und Daemon-Prueflauf sind vor und nach jedem Schritt gruen.
Code:
- AIModelService: Platzhalter, der immer 0.5 lieferte. Im DI registriert,
aber nie irgendwo injiziert. Ordner Core/AI faellt mit weg.
- CtApiWrapper + CtMeta: JSON-Modelle fuer einen {data,meta}-Umschlag, den
CapitolTrades nicht mehr liefert. Der Scraper deserialisiert seit laengerem
direkt List<CtTrade>.
- IBKRGatewayService: DisconnectAsync, InitBrokerageSessionAsync und
SearchStocksBySymbolAsync. Der Dienst selbst bleibt - er versorgt
Instrument-Sync, Kurshistorie und den Watchdog-Heartbeat.
- Je eine Methode ohne Aufrufer: BudgetService.GetAvailableBudgetAsync,
TradeHistoryService.GetRecentTradesAsync, CongressRepository.
GetAllTradeIdsAsync und .ResetHistoryImportAsync, IbkrMapping.DefaultPortFor,
SecretProtection.IsEncrypted.
- CongressRepository bekam damit einen LoggingService injiziert, den es nicht
mehr benutzt - Abhaengigkeit samt Konstruktorparameter raus.
Ressourcen:
- 17 Symbole der WinForms-Oberflaeche entfernt. Das Wildcard-Muster im csproj
nahm sie in die Binaerdatei auf, ViewIcons.cs bildet aber nur sieben
Schluessel ab. Resources/ enthaelt jetzt genau die sieben.
NuGet-Allowlist:
- Dapper und HtmlAgilityPack sind seit R3 bzw. R1 aus dem Projekt raus,
Microsoft.WindowsDesktop.* seit L5. Muster entfernt.
- MySqlConnector und Newtonsoft.Json stehen NUR transitiv in den
Projektdateien und wurden zuerst mitentfernt - ein Restore in einen leeren
Paket-Ordner scheiterte darauf mit NU1100. Beide wieder aufgenommen, jetzt
mit Begruendung, damit der naechste Aufraeumlauf nicht dieselbe Falle tritt.
Dokumente an den tatsaechlichen Stand angeglichen:
- ARCHITECTURE: R2 fuehrte die Umstellung auf IHostedService als offen, obwohl
R4 sie erledigt hat. L6 und die Deploymentcenter-Phase fehlten ganz.
- DC-Konzept: Schritte 0-8 standen auf "dieser Durchlauf", sind aber umgesetzt.
Jetzt je Schritt der wirkliche Stand - inklusive der beiden Halbfertigen:
Update-PRUEFUNG laeuft, das Anwenden hat keinen Aufrufer; die
Release-Pipeline steht, ist aber nie gelaufen. P5 ist eingetreten.
- Accounting und Supervisor trugen keinen Umsetzungsvermerk, obwohl beide
Module gebaut sind. Vermerk nach dem Muster des Linux-Konzepts ergaenzt,
mit dem, was jeweils offen bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0a72ea7e28 |
Deploymentcenter-Integration (Schritte 0-8 des Konzepts)
Bindet Lizenz, Watchdog, Fehler-Stream, UpdateService und Erstinstallation an
das Deploymentcenter 2.5.1 an. Einbauort ist IBKRTrader.Hosting - den Host
teilen sich Shell und Daemon.
Projekt-Befunde aus dem Konzept vorab bereinigt:
P1 Echte DB-Zugangsdaten als Vorgabewerte in AppSettings -> Platzhalter.
Das alte Passwort steht weiterhin in der Git-Historie und ist als
kompromittiert zu behandeln (Rotation ist Nutzer-Aktion).
P2 AppPaths fiel unter Windows auf /etc/ibkrtrader zurueck, was .NET zu
C:\etc\ibkrtrader aufloest. Jetzt %ProgramData%\IBKRTrader.
P3 Globale Ausnahmebehandler (AppDomain / TaskScheduler) - vorher gab es
keinen Logeintrag, wenn der Prozess unbehandelt wegbrach.
P4 Version einmal zentral in Directory.Build.props statt zweimal hartkodiert.
Bewusste Abweichungen vom DC-Leitfaden, beide fuer ein handelndes System:
- Lizenz-Urteil fuehrt zum Sperrbetrieb (TradingEnabled=false) statt zu
Environment.Exit(1). Keine neuen Einstiege, aber Risiko-, Exit- und
Buchhaltungslogik laufen weiter.
- exitCurrentApp bleibt immer false; der Aufrufer beendet geordnet.
Das SDK haengt als Cross-Repo-ProjectReference am Schwester-Repo
Deploymentcenter (Interim, siehe Konzept 2.2). Damit ist P5 offen: die
Gitea-CI checkt das Schwester-Repo nicht aus und wird rot, bis der Bezug auf
ein NuGet-Paket umgestellt ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
87194bfc48 |
IBKR: Zeitkontext einer Verbindung nachvollziehbar machen
ResolveExecutionTime liefert neben dem UTC-Zeitpunkt jetzt die Herkunft der verwendeten Zeitzone (gemeldet / angenommen / unbekannt / unlesbar). Bisher war im Nachhinein nicht unterscheidbar, ob ein Buchungszeitpunkt von TWS stammte oder eine Annahme war - genau der Fehler, der beim Umzug zwischen EU- und US-Host lautlos entsteht. IbkrConnection schreibt beim Verbinden einmalig Betriebszeitzone, Systemzeitzone und den Versatz zur TWS-Serverzeit ins Log; ab 5 s Abweichung gilt die Uhr des Hosts als verstellt. TWS-Setup-Checkliste um den Linux-Abschnitt ergaenzt (Betrieb und Umgebung unterscheiden sich, das Protokoll nicht). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c176b05ea1 |
L6: IBKRTrader.App.Avalonia -> IBKRTrader.App; letzte WinForms-Spuren raus
Das Suffix ".Avalonia" gab es nur, weil daneben ein WinForms-IBKRTrader.App
stand. Das ist seit L5 weg, also faellt auch das Suffix. Git erkennt alle
Dateien als Umbenennung; Assembly, Wurzel-Namensraum und die
avares://-Ressourcen-URI sind mitgezogen.
Nebeneffekt der Umbenennung: die global::Avalonia-Qualifizierungen entfallen.
Sie waren noetig, weil der Namensraum IBKRTrader.App.Avalonia das
Avalonia-Paket verdeckt hat - ein Ueberbleibsel genau der Namensgebung, die
jetzt weg ist.
Inhaltlich falsch gewordene Aussagen berichtigt - das waren die eigentlichen
Ueberbleibsel, nicht die Kommentare:
- .agents/rules/grundregeln.md schrieb weiterhin "C# .NET 10 WinForms",
RichTextBox-Logging, LauncherForm und PropertyGrid vor. Das ist die Regel,
nach der kuenftig gearbeitet wird - sie haette die Portierung Stueck fuer
Stueck rueckgaengig gemacht. Jetzt: Avalonia, keine Plattform-Suffixe, die
11er-Pinnung mit Begruendung, dazu die beiden Regeln, die uns in L1b am
meisten gekostet haben (UTC persistieren + AppTimeZone statt DateTime.Now;
jede Formatierung mit ausdruecklichem IFormatProvider).
- Core: LogEntry ("wird in RichTextBox geschrieben"), IWorker/WorkerEngine/
WorkerInfo ("DataGridView-Zeile"/"-Binding"), ModuleView ("die
WinForms-Shell castet auf Form").
- Doku: ARCHITECTURE (Modul-Ui-Ordner, "designbare Forms mit Initialize"),
KONZEPT-Modul-Accounting ("UI (WinForms, ein Fenster mit Tabs)").
BEWUSST STEHEN GEBLIEBEN sind die Kommentare, die WinForms nur als
Begruendung nennen - warum LoggingService ein Ereignis hat statt einer
RichTextBox, warum ModuleView Func<object> liefert, warum es benannte
Record-Zeilentypen gibt, warum die Einstellungsmaske aus Attributen entsteht.
Das ist die Herleitung des heutigen Entwurfs; ohne sie sieht spaeter jede
dieser Stellen nach Umstaendlichkeit ohne Grund aus.
KONZEPT-Linux-Portierung.md bekommt einen Statusvermerk: umgesetzt, die
Pfadangaben im Fundstellenverzeichnis beziehen sich auf den alten Aufbau.
Zwei Abweichungen von der Schaetzung sind dort festgehalten - der geringere
Aufwand dank der PolytraderSharp-Vorlage, und dass die dort empfohlene
InvariantGlobalization ein Fehler gewesen waere.
Verifiziert: Build 0 Fehler/0 Warnungen, 193 Tests gruen, Smoke-UI
konstruiert alle 7 Ansichten + Launcher + Dialog, Daemon-Prueflauf OK,
publish -r linux-x64 fuer beide Einstiegspunkte fehlerfrei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
d86a083438 |
L5: WinForms vollstaendig entfernt - das Projekt ist plattformneutral
Die Avalonia-Shell hatte in L4 Funktionsgleichheit erreicht, damit ist die WinForms-Fassung ueberfluessig. Entfernt: IBKRTrader.App.csproj, Program.cs, LauncherForm.cs, UI/ (14 Dateien) und Properties/Resources.* - zusammen rund 1.900 Zeilen. Rueckfallpunkt bleibt der Tag winforms-final. Resources/ mit den 24 PNG-Symbolen bleibt: die Avalonia-Shell bindet sie ueber AvaloniaResource ein und nutzt dieselben Symbolschluessel. Kein Projekt traegt mehr eine Plattformbindung. Verifiziert: alle acht Projekte bauen einzeln fuer linux-x64, und die gesamte Projektmappe baut ohne Fehler und ohne Warnung. Die CI kann damit vereinfacht werden: der Behelf aus L4 - unter Linux die Projekte einzeln statt der Mappe zu bauen, weil IBKRTrader.App dort nicht baubar war - entfaellt. Beide Plattformen bauen und testen jetzt dieselbe Mappe. Doku nachgezogen: README (Architektur-Uebersicht, Start- und Pruefbefehle), ARCHITECTURE (Ziel-Architektur auf den Ist-Stand, neuer Abschnitt L0-L5 in der Phasen-Checkliste, AppTimeZone ergaenzt). Der Hinweis "UI-Contract liegt im Core" war nach L1a falsch - WindowMenu ist dort raus, der Contract ist jetzt toolkit-neutral. Verifiziert: Build 0 Fehler/0 Warnungen, 193 Tests gruen, Smoke-UI konstruiert alle 7 Ansichten + Launcher + Dialog, Daemon-Prueflauf OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3513a0b2d9 |
L4: Modul-Fenster auf Avalonia - Funktionsgleichheit mit der WinForms-Shell
Die drei Modul-Fenster sind portiert; die Avalonia-Shell zeigt jetzt
dieselben sieben Ansichten wie die WinForms-Shell.
- CongressTradingWindow: Kennzahlen als Kacheln, manueller Scrape-Ausloeser,
offene Positionen des Moduls.
- SupervisorWindow: Analyse mit Live-Anzeige der Tool-Aufrufe, Dossier-
Browser mit Splitter, Berichte, Hinweise. Der Hinweistext nennt jetzt die
Betriebszeitzone, weil die Tagesbericht-Stunde in ihr gilt und nicht in
der Zeitzone des Rechners.
- AccountingWindow: gemeinsame Filterleiste, Uebersicht/BWA, Ledger, Steuer
(Platzhalter), Export und Abruf/Status.
Ersetzte WinForms-Bausteine:
- SaveFileDialog -> IStorageProvider.SaveFilePickerAsync (asynchron, anderer
Zuschnitt). Betrifft die drei Exporte des Accounting-Moduls.
- MessageBox bei Fehlern -> Statuszeile im Fenster. Ein modaler Dialog fuer
einen fehlgeschlagenen Ladevorgang war ohnehin zu laut.
- Anonyme Typen an DataGridView.DataSource -> benannte Records. Kompilierte
Bindings pruefen die Spalten dadurch beim Kompilieren; ein Tippfehler in
einer Spaltendefinition (Binding="{Binding Profile}}") ist beim Build
aufgefallen statt im Betrieb.
Accounting-Betraege werden beim Laden gegen InvariantCulture formatiert, das
Delta eines Ingest-Laufs ist nullable behandelt (ohne Saldo-Anker gibt es
keins) - vorher waere dort eine NullReferenceException moeglich gewesen.
CI-Matrix (Gitea Actions, Actions-kompatibel): ubuntu-latest und
windows-latest. Sie ist der Waechter gegen Rueckfaelle - ein neues
DateTime.Now, ein ToString ohne Formatanbieter oder eine WinForms-Referenz
im Core faellt auf dem Entwicklungsrechner nicht auf, hier schon. Der
Linux-Lauf baut bewusst NICHT die ganze Mappe, weil IBKRTrader.App
(net10.0-windows) dort nicht baubar ist; die Aufzaehlung entfaellt, sobald
die WinForms-Shell aus dem Build genommen wird.
Der Smoke-UI-Lauf ist Teil der CI - mit Avalonia braucht er kein
Anzeigegeraet mehr (SetupWithoutStarting). Mit WinForms war das auf einem
Build-Server nicht moeglich.
Verifiziert: Build 0 Fehler/0 Warnungen, 193 Tests gruen, Smoke-UI
konstruiert alle 7 Ansichten + Launcher + Dialog, publish -r linux-x64
liefert 32 MB mit ELF-Launcher. Die WinForms-Shell ist unveraendert
lauffaehig.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
afae3c9c4a |
L3: Avalonia-Shell und Core-Ansichten - die Oberflaeche laeuft auf Linux
Neues Projekt IBKRTrader.App.Avalonia (net10.0, plattformneutral) mit Launcher, Fenster-Menue, Beenden-Abfrage und den vier Core-Ansichten Dashboard, Workers, Logs und Settings. Nutzt denselben AppHostBuilder wie der Daemon; ShellServices ergaenzt nur, was ohne Oberflaeche nicht existiert. Versionen bewusst auf der 11er-Linie: Avalonia 11.3.19, DataGrid 11.3.13. LiveCharts2 2.0.5 ist gegen Avalonia 11 gebaut und bricht unter 12 (Gestures.PinchEvent gibt es dort nicht mehr). Diagramme kommen mit den neuen Modulen - bis LiveCharts2 Avalonia 12 unterstuetzt, darf hier nicht angehoben werden. Erfahrung aus PolytraderSharp, im csproj vermerkt. Ersatz fuer WinForms-Bausteine ohne Gegenstueck: - PropertyGrid -> SettingsModelBuilder erzeugt die Maske aus den bereits vorhandenen Category-/DisplayName-/Description-Attributen von AppSettings. Eine neue Einstellung erscheint damit automatisch, ohne dass jemand die Oberflaeche anfasst - genau der Vorteil des PropertyGrid. Kennwortfelder (DB-Passwort, Flex-Token) werden verdeckt. Gemessen: 11 Abschnitte, 41 Felder. - MessageBox.Show -> ShutdownConfirmWindow (Avalonia bringt keinen Meldungsdialog mit). Schliessen ueber das X zaehlt als Abbruch, damit ein versehentlicher Klick nie den Handelsbetrieb stoppt. - RichTextBox mit SelectionColor -> eingefaerbte Elemente je Logzeile, mit Filter, Auto-Scroll und Zeilenbegrenzung (im Dauerbetrieb waere die Liste sonst unbegrenzt gewachsen). - ToolStrip/StatusStrip -> zentrale Stilklassen in App.axaml (toolbar, statusbar, kpi, section). Die neuen Module setzen darauf auf. Gemeinsame Bausteine bewusst jetzt schon zentral, weil die neuen Module direkt in Avalonia entwickelt werden sollen. Kompilierte Bindings sind aktiv (x:DataType je Datenkontext) - Tippfehler in Bindings fallen damit beim Kompilieren auf statt erst zur Laufzeit. Dafuer brauchte es benannte Record-Zeilentypen statt der anonymen Typen, die DataGridView.DataSource frueher bekommen hat. --smoke-ui laeuft jetzt ueber SetupWithoutStarting, also OHNE Anzeigegeraet und ohne laufende Dienste. Das war mit WinForms nicht moeglich und macht die Konstruktionspruefung erstmals CI-tauglich. Der Host wird dabei bewusst nicht gestartet, sonst liefen Worker und Broker-Verbindungen gegen die echten Endpunkte an. AVLN3001 unterdrueckt: die Fenster haben absichtlich keinen parameterlosen Konstruktor. Einer wuerde sie ohne ihre Dienste konstruierbar machen und genau den Fehler verdecken, den die Konstruktionspruefung finden soll. Verifiziert: Build 0 Fehler/0 Warnungen, 193 Tests gruen, Avalonia-Smoke-UI konstruiert alle 6 Fenster, publish -r linux-x64 liefert 32 MB mit ELF-Launcher samt libSkiaSharp.so/libHarfBuzzSharp.so und ohne Windows-Abhaengigkeiten. Die WinForms-Shell ist unveraendert lauffaehig. Offen fuer L4: die drei Modul-Fenster (ModuleViews ist noch leer). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9c7e19149c |
L2: Kopfloser Linux-Dienst - der Trading-Kern laeuft ohne Oberflaeche
Meilenstein: Trading, Worker, Accounting, Supervisor, REST und MCP laufen
als systemd-Dienst auf Linux. Nur noch IBKRTrader.App ist Windows-gebunden.
Neu IBKRTrader.Hosting: die komplette Host-Zusammenstellung, aus der
WinForms-Program.cs herausgezogen. Bewusst ein eigenes Projekt - laege sie
weiter in der Shell, muesste sie fuer den Daemon dupliziert werden, und
beide wuerden auseinanderlaufen. Ein Modul, das nur in einer Variante
registriert ist, faellt sonst erst im Betrieb auf. RunStartupChecks buendelt
die Startschritte in fester Reihenfolge (Zeitzone vor dem ersten Logeintrag,
Master-Key vor jeder Entschluesselung).
Neu IBKRTrader.Daemon: kopfloser Einstiegspunkt mit SIGTERM-Behandlung,
--check (Trockenlauf: Host bauen, Startpruefungen, keine Boersenverbindung -
fuer Deployment und CI) und --db-version.
AppPaths: Logs, Backups, settings.json und master.key lagen neben der
Binaerdatei. Unter /opt hat der Dienstbenutzer dort keinen Schreibzugriff -
der Dienst waere beim ersten Logeintrag gescheitert. Aufloesung jetzt:
Umgebungsvariable, sonst Binaerverzeichnis WENN beschreibbar (haelt Windows
und die Entwicklung unveraendert), sonst FHS. Der Schreibtest ist ein echter
Schreibversuch - Attributpruefung traegt nicht, weil unter Linux
Besitzer/Gruppe/Modus und unter Windows die ACL entscheiden.
Rechtepruefung fuer master.key/openrouter.key auf Unix: Windows-ACLs
uebertragen sich beim Kopieren auf einen Linux-Host nicht, eine key-Datei mit
644 waere dort fuer jeden lokalen Nutzer lesbar. Nur eine Warnung.
InvariantGlobalization ausdruecklich auf false. Der naheliegende Griff zum
schlanken Image waere hier ein Eigentor gewesen und haette genau die zwei
Punkte aus L1b lautlos rueckgaengig gemacht: ohne ICU faellt die Aufloesung
von Windows-Zeitzonen-IDs aus (Rueckfall auf UTC im Container) und
CultureInfo.GetCultureInfo("de-DE") liefert die invariante Kultur, womit die
PDF-Betraege wieder das Format wechseln.
systemd: Unit mit Type=notify plus UseSystemd() im Host - ohne sd_notify
haette systemctl start bis zum Timeout gehangen. Unit mit Absicherung
(ProtectSystem=strict, ReadWritePaths), deploy/README.md mit Voraussetzungen,
Verzeichnissen, Zeitzonen-Festlegung und dem, was der Daemon NICHT loest
(IB Gateway headless bleibt eine eigene Baustelle).
Verifiziert: 193 Tests gruen (+5), Build 0 Fehler/0 Warnungen, --smoke-ui
konstruiert alle 7 Fenster, --check laeuft durch, und publish -r linux-x64
liefert 11 MB mit ELF-Launcher, ohne eine einzige Windows-Abhaengigkeit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8b9b993d1d |
L1b/2: Kultur und Dateisystem plattformunabhaengig machen
Kultur - Ausgaben und Parsen haengen nicht mehr am Host:
- PdfExporter formatierte Betraege mit ToString("N2") ohne Formatanbieter,
also CurrentCulture. Auf dem deutschen Desktop "1.234,56", in einem
Container mit LANG=C "1,234.56" - dieselbe Zahl, fuer einen Leser eine
andere. Fuer ein ausdruecklich pruefbares Dokument jetzt fest de-DE.
- CapitolTradesScraper.ParseDate nutzte DateOnly.TryParse ohne
Formatanbieter. Das ist nicht theoretisch: gemessen wurde aus
"2026-08-04" unter th-TH das Jahr 1483 (buddhistischer Kalender), unter
fa-IR das Jahr 2647 (persischer Kalender), unter ar-SA schlug das Parsen
ganz fehl. de-DE und en-US kommen mit ISO klar - genau deshalb faellt so
etwas auf dem Entwicklungsrechner nie auf. Jetzt TryParseExact mit
InvariantCulture; ein Formatwechsel der Quelle faellt damit auf, statt
still ein falsches Datum zu erzeugen. Regressionstest ueber vier Kulturen.
- IBKRGatewayService baute den Query-Parameter mit .ToString().ToLower()
(Tuerkisch-I) - jetzt fest "true"/"false".
PDF-Schriften: PDFsharp 6 loest auf Nicht-Windows-Plattformen nichts von
selbst auf, "Segoe UI" gibt es dort nicht - der Export waere zur Laufzeit
gescheitert. Neuer DocumentFontResolver: unter Windows bleibt die Plattform
zustaendig (unveraenderte Optik), auf Linux wird eine freie Systemschrift
gesucht (DejaVu/Liberation/Noto/FreeSans). Bewusst keine Schrift im Repo -
das erspart eine Lizenzfrage; fehlt sie, nennt die Fehlermeldung das zu
installierende Paket.
BackupWorker:
- Suchte "mysqldump.exe" in C:\Program Files\... und splittete PATH mit ';'.
Auf Linux ist das Trennzeichen ':' - der gesamte PATH waere als ein
Eintrag gelesen worden. Jetzt Path.PathSeparator, plattformabhaengige
Suchpfade und zusaetzlich "mariadb-dump" (MariaDB hat mysqldump ab 10.5
umbenannt).
- Das DB-Passwort stand als Kommandozeilenargument im Prozessbaum. Unter
Linux ist /proc/<pid>/cmdline fuer jeden lokalen Nutzer lesbar - das waere
eine neue Offenlegung gewesen, die es unter Windows so nicht gab. Jetzt
ueber MYSQL_PWD, nur an den Kindprozess vererbt. Argumente einzeln statt
als Zeichenkette (kein Quoting-Problem bei Pfaden mit Leerzeichen).
Verifiziert: 188 Tests gruen (+5), Build 0 Fehler/0 Warnungen, Core + 3
Module + Tests bauen fuer linux-x64, --smoke-ui konstruiert alle 7 Fenster.
Offen aus L1b und nach L2 verschoben: IAppPaths (Logs/Backups/settings.json/
master.key liegen neben der Binaerdatei; unter /opt ist das nicht schreibbar).
Gehoert zum Daemon, wo die Pfade tatsaechlich gebraucht werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
d8273c3a1e |
L1b/1: Betriebszeitzone statt Rechnerzeit - EU- und US-Instanzen sauber trennen
Wir betreiben Instanzen in zwei Regionen. Bisher hing jede Ortszeit an der Zeitzone des Rechners (DateTime.Now, DateTimeKind.Local): derselbe Code haette auf einem Windows-Desktop mit Europe/Berlin und in einem Linux-Container mit UTC lautlos unterschiedliche Werte geliefert - ohne Fehler, nur um Stunden verschoben, mitten in Buchungszeitstempeln. AppTimeZone (Core/Time): Betriebszeitzone der Instanz, einmalig aus Trading.ApplicationTimeZoneId gesetzt, IANA- und Windows-Schreibweise tragen beide, unbekannter Wert weicht auf die Systemzone aus und warnt. Wird laut Festlegung vor den ersten Trades gesetzt und danach nie gewechselt - ein Wechsel verschoebe rueckwirkend alle Tagesgrenzen. Persistenz bleibt UTC, damit die Daten beider Instanzen vergleichbar sind. IbkrMapping.ParseExecutionTime verwirft die von TWS gemeldete Zeitzone nicht mehr, sondern rechnet gegen sie nach UTC; ohne Zonenangabe gilt die Betriebszeitzone. Das ist der Kern: eine NYSE-Ausfuehrung darf nicht mit demselben nackten Zeitwert in die Buecher wie eine an der Eurex. Rueckgabe ist jetzt immer Kind=Utc. DailyReportService.NextRun -> NextRunUtc(nowUtc, hour, zone): der Bericht laeuft zu einer festen ORTSZEIT. Sommerzeitumstellung wird behandelt - bei der uebersprungenen Stunde weicht er aus, statt den Tag ausfallen zu lassen. LoggingService fuehrt Anzeigezeit und UTC getrennt: Dateinamen und Anzeige in Ortszeit (Tagesgrenzen gehoeren zur Instanz, der Supervisor liest die JSONL-Dateien ueber diese Namen), das ts-Feld im JSONL in UTC. Beides musste getrennt werden, weil die umgerechnete Ortszeit Kind=Unspecified traegt und ein ToUniversalTime() darauf sie als Zeit des HOSTS gedeutet haette. Verbleibende DateTime.Now in Worker-Zeitplaenen und Statuszeilen ebenfalls auf AppTimeZone.Now umgestellt. Verifiziert: 183 Tests gruen (+20), darunter EU/US-Versatz, Winter-/Sommerzeit, unbekannte Zone und die uebersprungene Stunde bei der Zeitumstellung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
123f38ab6f |
L1a: Core und Module von WinForms entkoppeln - net10.0 statt net10.0-windows
Core, alle drei Module und das Testprojekt tragen keinen UI-Code mehr und bauen fuer linux-x64. Nur noch IBKRTrader.App ist Windows-gebunden. UI-Contract toolkit-neutral (Vorbild: PolytraderSharp): - ModuleView.CreateForm (Func<Form>) -> CreateView (Func<object>) - ModuleView.Icon (System.Drawing.Image) -> IconKey (string). System.Drawing.Common ist seit .NET 7 Windows-only und wirft auf Linux. - WindowMenu.cs war reine WinForms-Umsetzung -> in die Shell verschoben. LoggingService haelt keine RichTextBox mehr, sondern meldet Eintraege ueber event EntryWritten. Einfaerbung und UI-Thread-Wechsel liegen jetzt im LogPanelController der Shell. Nebenbei: ToUpper() -> ToUpperInvariant() (tr-TR haette aus "info" ein "İNFO" gemacht) und \r\n -> Environment.NewLine. Die drei Modul-Fenster liegen jetzt unter UI/Views/Modules/; RegisterUi der Module ist bewusst leer, die Shell registriert sie zentral ueber UI/ModuleViews.cs (nur fuer tatsaechlich geladene Module). ViewIcons loest IconKey gegen die PNG-Ressourcen auf - dieselben Schluessel bekommt spaeter die Avalonia-Shell. UiConstructionTests entfernt: die Konstruktionspruefung deckt --smoke-ui ab, das Testprojekt braucht dafuer keine UI-Referenz mehr. Der Test RegisterUi_RegistersMainView haelt jetzt das Gegenteil fest - das Modul darf nichts registrieren, sonst waere es wieder toolkit-gebunden. Verifiziert: Build 0 Fehler/0 Warnungen, 163 Tests gruen, --smoke-ui konstruiert alle 7 Fenster, und Core + 3 Module + Tests bauen fuer linux-x64. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
18b1059fa9 |
L0: NuGet.config reparieren - Restore gegen leeren Paketordner
Drei Pakete hatten kein passendes packageSourceMapping-Muster; mit
<clear/> und Allowlist bedeutet das NU1100. Auf dem Entwicklungsrechner
unsichtbar, weil alle drei laengst im globalen Cache liegen - ein frischer
Klon (und damit jeder Linux-Host) konnte nicht wiederherstellen.
- PDFsharp* : gar kein Muster vorhanden
- Microsoft.EntityFrameworkCore : der Glob "…EntityFrameworkCore.*" matcht
das Basispaket ohne Suffix nicht
- Microsoft.CodeAnalysis.* : transitiv ueber EntityFrameworkCore.Design
Verifiziert: dotnet restore der Projektmappe gegen einen leeren
--packages-Ordner stellt jetzt alle sechs Projekte wieder her.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
27577ef1b2 |
@ Doku: Analyse Linux-Portierung (Avalonia + net10, Aufwand, Fundstellen)
Bestandsaufnahme gegen
winforms-final
|
||
|
|
b96a20788c |
R10: Lesender Bestandsabgleich (IBrokerPortfolioReader) + Datenlage-Konzepte
Eigener Seam neben IBrokerClient: Wer handelt, braucht ihn nicht; wer die eigene Buchfuehrung gegen den Broker abstimmt, braucht nur ihn. Zuteilung und Verfall aendern Positionen ohne Order von uns - ohne Abgleich laeuft das Managementbuch zwangslaeufig auseinander. - IBrokerPortfolioReader mit GetPositionsAsync/GetExecutionsAsync; implementiert von IbkrBrokerClient und NullBrokerClient (DI registriert beide Rollen auf derselben Instanz). - IbkrConnection: reqAccountUpdates statt reqPositions (nur dieser Weg liefert Marktwert und unrealisierten G/V), reqExecutions inkl. Zuordnung der verspaetet eintreffenden commissionReport-Callbacks ueber die ExecId. - BrokerPosition/BrokerExecution als Broker-Wahrheit neben Position; IbkrMapping: ParseSide, ParseExecutionTime, FormatExecutionFilterTime (UTC wegen TWS-Warnung 2174) - mit Unit-Tests. - Verifiziert gegen Paper-Konto DUR371528: 2 Positionen, 2 Ausfuehrungen inkl. Kommissionen. Doku: Kapital- und Buchmodell (drei Wahrheiten, Kapitalzuteilung), KONZEPT-Datenlage-und-Strategien (gemessen, was die API auf diesem Konto liefert). Options-Wheel: Greeks bei verzoegerten Daten funktionieren (Feld 83); Earnings-Termine sind ueber die TWS API nicht erreichbar (Fehler 10358) - Behelf ueber IV-Filter statt Fremddatenquelle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
80afcd49c1 |
R9: Echter IbkrBrokerClient über die TWS API
Broker-Adapter gegen TWS/IB Gateway, aktivierbar über IBKRSettings.UseTwsApi;
NullBrokerClient bleibt Default. TradingEnabled bleibt als zweite, unabhängige
Sicherung bestehen – ohne ihn platziert der ExecutionService keine Order.
Aufteilung (src/IBKRTrader.Core/Trading/Ibkr/):
- IbkrMapping – reine Abbildung Core <-> TWS (Kontrakt, Order, Kurs, Port-
und Statusregeln), vollständig unit-getestet
- IbkrConnection – Socket-Lebenszyklus, Reader-Thread, reqId-Korrelation über
TaskCompletionSource
- IbkrBrokerClient – implementiert IBrokerClient, übersetzt Fehler in leere
Ergebnisse (Konto 0 lässt die Risikoprüfung alles ablehnen)
Bewusste Entscheidungen:
- Träges Verbinden mit Wiederholung statt Verbindungsaufbau beim Start: TWS ist
nach einem Neustart minutenlang nicht bereit.
- Port wird gegen den Handelsmodus geprüft; Paper-Modus auf Live-Port (oder
umgekehrt) lässt den Broker inaktiv, statt auf dem falschen Konto zu handeln.
- MarketDataType Default 4: Paper-Konten ohne Datenabo bekommen sonst keine Kurse.
- Fehlercode 10167 ist ein Statushinweis (verzögerte Daten folgen), kein Fehler.
Als Fehler behandelt scheiterte jede einzelne Kursabfrage.
Verifiziert gegen Paper-Konto DUR371528: Verbindung, Konto (100.105,50 EUR),
Kurse (AAPL/MSFT/NVDA, verzögert), Fehlerpfade. Orderpfad bis zur Broker-Annahme
per What-If-Order geprüft (Aktie + Option, ohne Ausführung); dabei zugleich die
Optionsberechtigung des Kontos bestätigt. Offen: echte Ausführung (Fill ->
Buchung) und asynchrone Fill-Verfolgung – beides in IBKR-Integration.md notiert.
Doku: TWS-Setup-Checkliste.md (Einstellungen für Neuinstallation) neu,
IBKR-Integration.md / ARCHITECTURE.md / README.md nachgezogen.
154/154 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
2a312ca035 |
R8: Accounting- + Supervisor-Modul + Core-Datenfundament (S-0)
Portierung der beiden fehlenden Grundbausteine aus PolytraderSharp (voller Ausbau). Core S-0 (Datenfundament fuer Analyse/Forensik): - core_decision_journal + core_order_events (+ ReasonCode/Decision/OrderEvent-Enums), IDecisionJournal/IOrderEventLog mit fehlertoleranten EF-Impls (Handel bricht nie). - SignalId-Durchreichung TradeSignal -> ExecutionService -> core_trade_history; ExecutionService schreibt an jeder Verzweigung Journal/Order-Events. - JSONL-Log-Sink (LogJson + Dual-Sink), pure Analytik: RealizedPnlEngine (FIFO), TradeAnalytics, DossierBuilder. Migration AddAnalysisFoundation. Accounting-Modul (acc_): unabhaengiger IBKR-Kontoauszug (Activity Flex Query) hinter Interfaces mit Offline-Null-Stubs -> append-only Ledger + Periodenabrechnung/BWA + FX (USD/EUR) + CSV/PDF (PDFsharp/MigraDoc). Steuerschicht bewusst offen (Platzhalter-Tab). Kein Handel. Migration InitialAccounting. Supervisor-Modul (sup_): read-only OpenRouter-Agent (Function-Calling-Loop) + read-only Tool-Registry (8 Tools) + Profile + Dossier-Browser + Counterfactual-Job (Stub) + Tagesbericht/MCP-Light (opt-in). Migration InitialSupervisor. Verdrahtung: Program.cs (beide Module + Icons), slnx/App/Tests-Referenzen, provision-db.ps1, AppSettings-Sektionen, docs/konzepte, README. Tests: 79 -> 117 gruen (FIFO/KPIs/Dossier/JSONL, Classifier/Engine/FX/Idempotenz, OpenRouter/Registry/Agent/MCP, STA-Konstruktion beider neuen Fenster). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
cbbedb2e0e |
@
Doku: Projekt-README (Architektur, Build/Test, Konfiguration, DB-Setup, Status) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
72e07d0c6b |
@
Doku: IBKR-Integration festgelegt (TWS API / IB Gateway) - docs/IBKR-Integration.md: Entscheidung (TWS API via IB Gateway) + Setup (Ports 4002/4001, IBC/2FA), Regionen (US<->IE gleiche Codebasis), Lib (IB.TWS.CSharpApi), Design des IbkrBrokerClient (async-Wrapper ueber EWrapper/EClient), To-dos bei Gateway-Zugang - ARCHITECTURE.md: Broker-Anbindung als naechsten Meilenstein verankert (blockiert bis Paper-Zugang) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
c5ff4e17aa |
@
DB-Provisioning-Skripte (Drop-Alt-Tabellen + EF-Migrationen anwenden) - scripts/drop-app-tables.sql: entfernt alte (snake_case) core_/ct_-Tabellen + EF-Historie, damit die EF-Migrationen kollisionsfrei angewendet werden koennen - scripts/provision-db.ps1: liest Connection aus appsettings.Local.json, setzt IBKRTRADER_MYSQL, wendet CoreDbContext- + CongressTradingDbContext-Migrationen an (dotnet ef database update) - scripts/README.md: Ablauf + Sicherheitshinweise (Passwort rotieren, Master-Key) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
e6bffdb70d |
@
R7: Dashboard-View + DashboardService (Abschluss Feinschliff) - Core/Trading/DashboardService: aggregiert Positionen/Exposure/Trades via EF (DashboardSnapshot) - UI/Views/DashboardView: Trading-Modus, aggregierte Kennzahlen, geladene Module (+ Aktivierungs-Status) - core.dashboard-View registriert (Order 5) mit dashboard-Icon - Tests: DashboardService (leer + Aggregation, InMemory) -> 58/58 gruen; smoke-ui deckt alle 5 Views ab Kurskorrektur R1-R7 abgeschlossen: IBKRTrader folgt dem PolytraderSharp-Konzept. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
9effc43206 |
@
R6: Security - Master-Key + AES-256-GCM at-rest + TLS-Warnung - Core/Security/SecretProtection: AES-256-GCM at-rest, Master-Key aus env IBKRTRADER_MASTER_KEY bzw. gitignorierte master.key; selbstheilendes enc:v1:-Format; Passthrough ohne Key (mit Warnung) - Core/Security/EncryptedStringConverter (EF-ValueConverter, bereit fuer kuenftige Credentials) - Program: ConfigureSecretProtection (Master-Key laden) + WarnIfDbTlsNotEnforced (SslMode) beim Start - master.key gitignored - Tests: SecretProtection (Round-Trip/Idempotenz/Passthrough/Tamper/Key-Fehler) -> 56/56 gruen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
331623d4f3 |
@
R5: CongressTrading als Strategie (Signal -> ExecutionService) + Positionen im Modul-Fenster
- CongressTradingStrategy: neuer Scrape-Trade -> TradeSignal (buy/sell-Mapping, sonst ignoriert)
-> Core-IExecutionService; CongressScrapeWorker ruft sie je neuem Trade (try/catch-isoliert)
- Modul-Fenster: offene Positionen des Moduls via IPortfolioService.GetPositionsAsync("CT")
- Tests: CongressTradingStrategy (Mapping/Ausfuehrung, gemockter ExecutionService) -> 51/51 gruen
- Handel bleibt sicher aus (TradingEnabled=false + NullBrokerClient), bis Broker+Freigabe
- Build + smoke-ui + App-Start ok
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
|
||
|
|
19dac18050 |
@
R4: Worker von WorkerEngine auf IHostedService (Generic Host) - WorkerBase implementiert IHostedService (StartAsync/StopAsync mit CancellationToken, eigener CTS, Guard auf Info.Active); IWorker auf Metadaten + TriggerAsync reduziert - Worker via AddHostedService registriert (Core + Modul); AppHost.Start() startet sie, AppHost.StopAsync() stoppt sie - WorkerEngine auf leichte Registry reduziert (WorkerInfos fuer UI + TriggerWorkerAsync) - LauncherForm startet/stoppt keine Worker mehr (Host-getrieben) - Hosting.Abstractions im Core; Tests angepasst -> 43/43 gruen; smoke-ui + App-Start ok Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
475c0b41cb |
@
R3 Slice 4: IBKR-Marktdaten auf EF; Dapper vollstaendig entfernt - 3 IBKR-Entities (IBKRInstrument/IBKRMarketBar/IBKRExternalIdentifier) im CoreDbContext (core_ibkr_instruments/_market_data/_external_identifiers); Migration AddIbkr - IBKRMarketDataRepository von Dapper auf EF (IDbContextFactory<CoreDbContext>); Cross-Modul-Query (ct_trade) via Database.SqlQueryRaw - DatabaseService, IBKRMigrations, Dapper-Package entfernt; Laufzeit-Migrationen komplett weg (Schema extern via dotnet ef database update) - Tests: +4 IBKRMarketDataRepository (EF-InMemory) -> 43/43 gruen; Build + smoke-ui + App-Start ok R3 abgeschlossen: Persistenz vollstaendig auf EF Core. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
6c8e36dc3c |
@
R3 Slice 3: CongressTrading-Modul + Worker-Log + core_settings auf EF Core - Modul: CongressTradingDbContext (ct_congressMember/ct_trade) + Design-Time-Factory; CongressRepository von Dapper auf EF; CongressTrade.DetailsFetched ergaenzt; Dapper entfernt - Core: CoreSettingsService (core_settings via EF); WorkerBase-Log auf EF (core_worker_log); 7 Worker-Ctors DatabaseService -> IDbContextFactory<CoreDbContext> - CoreMigrations + CongressMigrations (Dapper) entfernt; core_-Schema nun rein EF - EF-Migration InitialCongressTrading; AddCorePersistence-Fallback fuer leeren Connection-String (App startet ohne DB); Connection aus appsettings.Local.json (gitignored) - Tests: +4 CongressRepository (EF-InMemory) -> 39/39 gruen; Build + smoke-ui + App-Start ok Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
7e3791b82d |
@
R3 Slice 2: Trading-Buchführung auf EF Core (+ InMemory-Tests) - BudgetService, TradeHistoryService, PortfolioService von Dapper/DatabaseService auf IDbContextFactory<CoreDbContext> (EF Core) umgestellt - Positionen/Budget/Trade-Historie ueber CoreDbContext (core_position/core_budget/core_trade_history) - Tests: EF-InMemory-Provider im Testprojekt; 5 PortfolioService-Tests (Buy/Sell/Avg/Exposure/Isolation) - 35/35 Tests + Build + smoke-ui gruen; WorkerBase-Log/Modul/Dapper-Entfernung folgen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
261032f9f9 |
@
R3 Slice 1: EF-Core-Infrastruktur im Core + InitialCore-Migration - Pomelo.EntityFrameworkCore.MySql 8.0.3 + EF Core Design 8.0.11 (Core), NuGet-Allowlist erweitert - Configuration/DatabaseOptions + DatabaseServerVersion (MariaDB 11.8.6 gepinnt) - Persistence/Entities: CorePosition, CoreTrade, CoreBudget, CoreWorkerLog, CoreSetting - Persistence/Ef/CoreDbContext (core_-Tabellen) + Design-Time-Factory (env IBKRTRADER_MYSQL) - DependencyInjection/AddCorePersistence (AddDbContextFactory), in Program verdrahtet (Connection aus appsettings.Local.json) - EF-Migration InitialCore erzeugt (5 core_-Tabellen) - Consumer noch auf Dapper (folgt in Slice 2); Build + 30/30 Tests + smoke-ui gruen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
48c662a699 |
@
R3-Vorbereitung: --db-version-Diagnose + EF-Strategie festgelegt - Program: --db-version gibt die DB-Serverversion aus (fuer EF-ServerVersion-Pin) - Bestaetigt: MariaDB 11.8.6 -> Pin wie PolytraderSharp (MariaDbServerVersion 11.8.6) - docs/ARCHITECTURE.md: R3-Strategie fixiert (EF-Migrationen extern, Verbindung aus appsettings.Local.json) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
218247b5f6 |
@
Launcher: ToolStrip-Fensterleiste + Icons (aus PolytraderSharp) - Fenster-Buttons in einem ToolStrip (Icon ueber Text, 32x32) statt Einzel-Buttons - 24 Icon-PNGs aus PolytraderSharp uebernommen (Resources/) + Properties/Resources.resx (ResXFileRef) + Resources.Designer.cs - Program.AssignViewIcons: Icons je View-ID zugewiesen (Workers=system_time, Logs=error_log, Settings=setting_tools, CongressTrading=cross_reference); Icons auch im Fenster-Menue - Button-Checked spiegelt Offen-Status - Build + 30/30 Tests + smoke-ui gruen; Screenshot geprueft Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
8f44c0fd82 |
@
R2: Generic Host + Modul-Vertrag + Shell-UI (PolytraderSharp-Konzept) - Core/Modularity: neuer IModule (Name, DbPrefix, RegisterServices(services,config), RegisterUi(host,sp), StartAsync/StopAsync, GetActivationBlocker) + ModuleView + IModuleUiHost + WindowMenu. Alte IModule/ModuleRegistry/WindowManager/ModuleFormBase entfernt. - Program.cs: Host.CreateDefaultBuilder + IConfiguration (appsettings.json/.Local.json); Core-Services registriert, Module via RegisterServices, Views via RegisterUi. - UI: ShellUiHost (Einzelinstanz-Fenster + Fenster-Menue), LauncherForm als Shell (Buttons je View), Core-Views Logs/Settings/Workers als eigene Fenster. - CongressTrading auf neuen Vertrag; Worker als IWorker registriert. - --smoke-ui Headless-Test (konstruiert jede View + Launcher). - appsettings.Local.json gitignored. - Tests angepasst -> 30/30 gruen; Build + Smoke-UI + App-Start verifiziert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |
||
|
|
c20538c8d8 |
@
R1: Umbau auf Multi-Projekt-Struktur (Vorbild PolytraderSharp) Kurskorrektur: Vorbild ist PolytraderSharp (C#), nicht die veraltete Python-Version. Reiner Strukturumbau, Verhalten unveraendert. - src/IBKRTrader.Core (classlib): Core-Code + UI-Contract (ModuleFormBase, WindowManager) - src/IBKRTrader.Modules.CongressTrading (classlib, referenziert nur Core) - Root: IBKRTrader.App (WinExe) referenziert Core + Modul - tests/IBKRTrader.Tests: Referenzen auf Core + Modul - Neue .slnx; ungenutztes HtmlAgilityPack entfernt - docs/ARCHITECTURE.md: korrigierter Ziel-Stand + R-Phasenplan - Build + 38/38 Tests gruen; App startet Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @ |