using System.Globalization;
using System.Text.Json;
namespace Encelado.Core.Baskets;
///
/// An entry the decider approved whose execution is not finished: leg A is on its way
/// (state PendingA) or filled while leg B is on its way (PendingB). Holds
/// everything needed to finish the basket later — or to undo leg A — without the
/// original decision object, which does not survive a restart.
///
public sealed class PendingEntry
{
public required string BasketId { get; init; }
public required bool BuyCross { get; init; }
public required double EntryZ { get; init; }
public required double UnitsA { get; init; }
public required double UnitsB { get; init; }
public required double TpPips { get; init; }
public required double MaxLossUsd { get; init; }
public double EntryCostPips { get; init; } = double.NaN;
public required double EquityAtEntry { get; init; }
public string Motivazione { get; init; } = string.Empty;
public required DateTime DecidedUtc { get; init; }
public string ClientRefA { get; set; } = string.Empty;
public string ClientRefB { get; set; } = string.Empty;
/// The quotes seen at decision time, for the slippage of each leg.
public double QuoteA { get; init; }
public double QuoteB { get; init; }
/// Leg A as filled, once it is.
public BasketLeg? LegA { get; set; }
public void Write(Utf8JsonWriter w)
{
ArgumentNullException.ThrowIfNull(w);
w.WriteStartObject("pending");
w.WriteString("basketId", BasketId);
w.WriteBoolean("buyCross", BuyCross);
w.WriteNumber("entryZ", EntryZ);
w.WriteNumber("unitsA", UnitsA);
w.WriteNumber("unitsB", UnitsB);
w.WriteNumber("tpPips", TpPips);
w.WriteNumber("maxLossUsd", MaxLossUsd);
w.WriteNumber("entryCostPips", double.IsFinite(EntryCostPips) ? EntryCostPips : 0);
w.WriteNumber("equityAtEntry", EquityAtEntry);
w.WriteString("motivazione", Motivazione);
w.WriteString("decidedUtc", DecidedUtc.ToString("O", CultureInfo.InvariantCulture));
w.WriteString("clientRefA", ClientRefA);
w.WriteString("clientRefB", ClientRefB);
w.WriteNumber("quoteA", QuoteA);
w.WriteNumber("quoteB", QuoteB);
if (LegA is { } leg)
{
BasketLeg.Write(w, "legA", leg);
}
w.WriteEndObject();
}
public static PendingEntry Read(JsonElement e)
{
PendingEntry p = new()
{
BasketId = e.GetProperty("basketId").GetString() ?? string.Empty,
BuyCross = e.GetProperty("buyCross").GetBoolean(),
EntryZ = e.GetProperty("entryZ").GetDouble(),
UnitsA = e.GetProperty("unitsA").GetDouble(),
UnitsB = e.GetProperty("unitsB").GetDouble(),
TpPips = e.GetProperty("tpPips").GetDouble(),
MaxLossUsd = e.GetProperty("maxLossUsd").GetDouble(),
EntryCostPips = e.GetProperty("entryCostPips").GetDouble(),
EquityAtEntry = e.GetProperty("equityAtEntry").GetDouble(),
Motivazione = e.GetProperty("motivazione").GetString() ?? string.Empty,
DecidedUtc = DateTime.Parse(e.GetProperty("decidedUtc").GetString()!, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal),
QuoteA = e.TryGetProperty("quoteA", out JsonElement qa) ? qa.GetDouble() : 0,
QuoteB = e.TryGetProperty("quoteB", out JsonElement qb) ? qb.GetDouble() : 0,
};
p.ClientRefA = e.TryGetProperty("clientRefA", out JsonElement ca) ? ca.GetString() ?? string.Empty : string.Empty;
p.ClientRefB = e.TryGetProperty("clientRefB", out JsonElement cb) ? cb.GetString() ?? string.Empty : string.Empty;
if (e.TryGetProperty("legA", out JsonElement la))
{
p.LegA = BasketLeg.Read(la);
}
return p;
}
}