Abweichung wird je Instrument bestimmt, Sperren auch kopflos setzbar
Der erste Lauf gegen die Testmaschine zeigte den Fehler sofort: 2 Stueck SAP in DIAG, 2 in UNASSIGNED, der Broker fuehrt 4 - alles richtig, aber zeilenweise verglichen meldete die Ansicht zwei Fehlbestaende. Der Broker kennt unsere Buecher nicht; vergleichbar ist nur die Summe ueber alle. ManagedPosition traegt jetzt OwnTotalQuantity, und Diverges misst daran. Zwei Tests halten beide Richtungen fest: eine aufgeteilte Position ist keine Abweichung, eine echte verschwindet nicht in der Summe. Dazu --lock und --instrument-rule fuer den kopflosen Betrieb. Beide mit Trockenlauf; die Stufe "Locked" schreibt vor dem Setzen aus, dass sie auch das SCHLIESSEN durch Module verhindert - das gehoert dorthin, wo entschieden wird, nicht in den Stoerungsfall. Auf der Testmaschine durchgespielt: GOOG von UNASSIGNED nach MANUAL umgebucht (Menge und Einstand unveraendert), dann auf CloseOnly gesetzt - beides steht in der Uebersicht. 473 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,12 +20,21 @@ namespace IBKRTrader.Core.Positions;
|
||||
/// <param name="Lock">Sperrstufe des Betreibers für dieses Buch und Instrument.</param>
|
||||
/// <param name="Rule">Regel des Instruments (Handelsverbot, Stückgrenze) – gilt über alle Bücher.</param>
|
||||
/// <param name="BrokerQuantity">
|
||||
/// Was der Broker führt. <c>null</c> heißt <b>nicht bekannt</b> und ausdrücklich nicht „null Stück":
|
||||
/// Ohne Verbindung ist jede Aussage über Abweichungen wertlos.
|
||||
/// Was der Broker für dieses Instrument führt – <b>kontoweit</b>, denn der Broker kennt keine
|
||||
/// Bücher. <c>null</c> heißt <b>nicht bekannt</b> und ausdrücklich nicht „null Stück": Ohne
|
||||
/// Verbindung ist jede Aussage über Abweichungen wertlos.
|
||||
///
|
||||
/// <para>Dezimal und nicht ganzzahlig, weil der Broker es so meldet – Bruchstücke gibt es. Sie hier
|
||||
/// zu runden hieße, eine Abweichung wegzurechnen, die es gibt.</para>
|
||||
/// </param>
|
||||
/// <param name="OwnTotalQuantity">
|
||||
/// Unser Bestand desselben Instruments <b>über alle Bücher</b> – die einzige Zahl, die sich mit
|
||||
/// <paramref name="BrokerQuantity"/> vergleichen lässt.
|
||||
///
|
||||
/// <para>Ohne sie meldete jede Aufteilung eine Abweichung: Liegen 2 Stück SAP in DIAG und 2 in
|
||||
/// UNASSIGNED, während der Broker 4 führt, stimmt alles – zeilenweise verglichen sähe es nach zwei
|
||||
/// Fehlbeständen aus. Am 2026-09-06 auf der Test-VM genau so beobachtet.</para>
|
||||
/// </param>
|
||||
public sealed record ManagedPosition(
|
||||
BookId Book,
|
||||
InstrumentRef Instrument,
|
||||
@@ -34,7 +43,8 @@ public sealed record ManagedPosition(
|
||||
decimal BoundCapital,
|
||||
PositionLock Lock,
|
||||
InstrumentRule Rule,
|
||||
decimal? BrokerQuantity)
|
||||
decimal? BrokerQuantity,
|
||||
int OwnTotalQuantity)
|
||||
{
|
||||
/// <summary>Herkunft in einem Wort – das, wonach beim Aufräumen zuerst gefragt wird.</summary>
|
||||
public string Origin => Book.Kind switch
|
||||
@@ -46,10 +56,16 @@ public sealed record ManagedPosition(
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Weicht unser Bestand von dem des Brokers ab? <c>false</c>, solange der Broker nichts gesagt
|
||||
/// hat – eine Abweichung, die nur aus fehlenden Daten besteht, ist keine.
|
||||
/// Weicht unser Bestand von dem des Brokers ab? Verglichen wird der Bestand <b>aller</b> Bücher,
|
||||
/// nicht der dieser Zeile – der Broker kennt unsere Aufteilung nicht.
|
||||
///
|
||||
/// <para><c>false</c>, solange der Broker nichts gesagt hat: Eine Abweichung, die nur aus
|
||||
/// fehlenden Daten besteht, ist keine.</para>
|
||||
/// </summary>
|
||||
public bool Diverges => BrokerQuantity is { } broker && broker != Quantity;
|
||||
public bool Diverges => BrokerQuantity is { } broker && broker != OwnTotalQuantity;
|
||||
|
||||
/// <summary>Teilen sich mehrere Bücher dieses Instrument?</summary>
|
||||
public bool IsSharedAcrossBooks => OwnTotalQuantity != Quantity;
|
||||
|
||||
public bool IsRestricted => Lock != PositionLock.Free || Rule.IsRestrictive;
|
||||
}
|
||||
@@ -142,6 +158,13 @@ public sealed class PositionManager
|
||||
.GroupBy(p => p.Instrument!.Value.Key, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Unser Bestand je Instrument ueber alle Buecher - die einzige Groesse, die sich mit dem
|
||||
// Broker vergleichen laesst.
|
||||
var ownTotals = own
|
||||
.Where(p => p.Quantity != 0)
|
||||
.GroupBy(p => p.Instrument.Key, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var managed = new List<ManagedPosition>(own.Count);
|
||||
foreach (var position in own.Where(p => p.Quantity != 0))
|
||||
{
|
||||
@@ -162,7 +185,10 @@ public sealed class PositionManager
|
||||
BoundCapital: position.BoundCapital,
|
||||
Lock: await _rules.GetLockAsync(book, position.Instrument, ct).ConfigureAwait(false),
|
||||
Rule: await _rules.GetRuleAsync(position.Instrument, ct).ConfigureAwait(false),
|
||||
BrokerQuantity: brokerQuantity));
|
||||
BrokerQuantity: brokerQuantity,
|
||||
OwnTotalQuantity: ownTotals.TryGetValue(position.Instrument.Key, out var total)
|
||||
? total
|
||||
: position.Quantity));
|
||||
}
|
||||
|
||||
// Bestand, den der Broker fuehrt und den wir in keinem Buch haben. Der Abgleich (#306)
|
||||
|
||||
Reference in New Issue
Block a user