namespace IBKRTrader.Core.Trading;
/// Kauf oder Verkauf.
public enum TradeSide { Buy, Sell }
/// Order-Typ.
public enum OrderType { Market, Limit }
/// Handelsmodus – Paper-Account (Test) oder Live.
public enum TradingMode { Paper, Live }
///
/// Signal, das ein Modul an den übergibt.
/// Das Modul liefert nur die Absicht – Sizing, Risiko und Ausführung macht der Core.
///
public sealed record TradeSignal
{
/// Ticker-Symbol (z. B. "AAPL").
public required string Symbol { get; init; }
/// Kauf oder Verkauf.
public required TradeSide Side { get; init; }
/// Kürzel des auslösenden Moduls (z. B. "CT").
public required string SourceModule { get; init; }
/// Begründung des Signals (für Logging/Buchführung).
public string Reason { get; init; } = "";
/// Optionaler Limit-Preis. null = Market-Order.
public decimal? LimitPrice { get; init; }
/// Optionaler Nominalwert-Wunsch; sonst greift das Risiko-Sizing.
public decimal? SuggestedNotional { get; init; }
}
/// Konkrete Order-Anforderung an den Broker.
public sealed record OrderRequest
{
public required string Symbol { get; init; }
public required TradeSide Side { get; init; }
public required int Quantity { get; init; }
public required OrderType Type { get; init; }
public decimal? LimitPrice { get; init; }
}
/// Ergebnis einer Order-Platzierung.
public sealed record OrderResult
{
public bool Success { get; init; }
public string? OrderId { get; init; }
public int FilledQuantity { get; init; }
public decimal AvgFillPrice { get; init; }
public string? Error { get; init; }
public static OrderResult Filled(string orderId, int qty, decimal price) =>
new() { Success = true, OrderId = orderId, FilledQuantity = qty, AvgFillPrice = price };
public static OrderResult Fail(string error) =>
new() { Success = false, Error = error };
}
/// Momentaufnahme eines Kurses.
public sealed record Quote(string Symbol, decimal Last, decimal Bid, decimal Ask);
/// Kontostand-Momentaufnahme des Brokers.
public sealed record AccountState(decimal NetLiquidation, decimal AvailableFunds);
/// Offene Position eines Moduls.
public sealed record Position(string Module, string Symbol, int Quantity, decimal AvgPrice)
{
public decimal Notional => Quantity * AvgPrice;
}
/// Kontext für die Risikobewertung eines Signals.
public sealed record RiskContext
{
/// Aktueller Kurs des Symbols.
public required decimal Price { get; init; }
/// Netto-Liquidationswert des Kontos.
public required decimal NetLiquidation { get; init; }
/// Aktuell vom Modul gehaltener Nominalwert (Summe offener Positionen).
public decimal ModuleExposure { get; init; }
/// Bereits gehaltene Stückzahl für das Signal-Symbol.
public int ExistingQuantity { get; init; }
}
/// Aus den Settings abgeleitete Risiko-Parameter.
public sealed record RiskParameters(
decimal MaxTradePercent,
decimal MaxPositionPercentPerModule,
decimal MaxSlippagePercent);
/// Entscheidung der Risikoprüfung.
public sealed record RiskDecision
{
public bool Approved { get; init; }
public int Quantity { get; init; }
public string Reason { get; init; } = "";
public static RiskDecision Reject(string reason) =>
new() { Approved = false, Quantity = 0, Reason = reason };
public static RiskDecision Approve(int quantity, string reason = "OK") =>
new() { Approved = true, Quantity = quantity, Reason = reason };
}
/// Ergebnis einer Signal-Ausführung durch den .
public sealed record ExecutionResult
{
/// "EXECUTE", "SKIP" oder "ERROR".
public required string Action { get; init; }
public string Reason { get; init; } = "";
public OrderResult? Order { get; init; }
public bool Executed => Action == "EXECUTE";
public static ExecutionResult Skip(string reason) => new() { Action = "SKIP", Reason = reason };
public static ExecutionResult Error(string reason, OrderResult? order = null) => new() { Action = "ERROR", Reason = reason, Order = order };
public static ExecutionResult Execute(OrderResult order) => new() { Action = "EXECUTE", Reason = "OK", Order = order };
}