Kultur-Bug behoben + Threema entfernt (INotificationSink)

- TraderMonitorService las API-Preise kulturabhaengig: unter de-DE wurde aus
  "0.53" der Wert 53 (Faktor-100-Fehler im Einstandspreis). Nutzt jetzt den
  bereits vorhandenen invarianten Helper ParseDecimal.
- Gleiche Fehlerklasse in PolymarketClobClient (6x) und MasterTraderAnalyticsJob
  vorsorglich auf InvariantCulture gestellt.
- Neuer Regressionstest ApiNumberParsingTests (10 Faelle unter erzwungener de-DE-Kultur).
- Threema komplett entfernt (Entscheidung Richard): ThreemaService, vendorte
  Bibliothek libs/Threema-MsgApi-Net-Core, ServerSettings-Block, DI-Verdrahtung.
- Ersetzt durch neutrale INotificationSink (No-Throw-Vertrag) + LogNotificationSink
  als Uebergang; RocketChat/Telegram folgen spaeter.
- Entfernt nebenbei libsodium 1.0.16, die einzige Registry-Nutzung im Build,
  den HttpListener-Webhook und System.Web.HttpUtility (alles Linux-Hindernisse).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-06 12:01:13 +02:00
co-authored by Claude Opus 5
parent ab0a494f29
commit fd6d62618f
56 changed files with 794 additions and 3734 deletions
@@ -767,7 +767,10 @@ namespace PolyTraderSharp.Services
var decMatch = System.Text.RegularExpressions.Regex.Match(responseContent, @"maker amount supports a max accuracy of (\d+) decimals, taker amount a max of (\d+) decimals");
if (decMatch.Success && overrideMakerDecimals == null)
{
if (int.TryParse(decMatch.Groups[1].Value, out int newMaker) && int.TryParse(decMatch.Groups[2].Value, out int newTaker))
if (int.TryParse(decMatch.Groups[1].Value, System.Globalization.NumberStyles.Integer,
System.Globalization.CultureInfo.InvariantCulture, out int newMaker)
&& int.TryParse(decMatch.Groups[2].Value, System.Globalization.NumberStyles.Integer,
System.Globalization.CultureInfo.InvariantCulture, out int newTaker))
{
_logger.Info($"🔄 Automatische Anpassung an Dezimalregeln (Maker: {newMaker}, Taker: {newTaker}). Order wird neu berechnet...");
return await PlaceOrderAsync(account, tokenId, sideStr, investAmountUsd, limitPrice, orderType, debugPayloadLog, isNegRisk, actualFeeBps, overrideTickSize, newMaker, newTaker);
@@ -778,7 +781,8 @@ namespace PolyTraderSharp.Services
var match = System.Text.RegularExpressions.Regex.Match(responseContent, @"invalid fee rate \(\d+\), current market's (?:taker|maker) fee: (\d+)");
if (match.Success && actualFeeBps == 0) // Only retry once
{
if (int.TryParse(match.Groups[1].Value, out int newFeeBps))
if (int.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer,
System.Globalization.CultureInfo.InvariantCulture, out int newFeeBps))
{
_logger.Info($"🔄 Automatische Anpassung an Fee Rate ({newFeeBps} bps). Order wird erneut platziert...");
return await PlaceOrderAsync(account, tokenId, sideStr, investAmountUsd, limitPrice, orderType, debugPayloadLog, isNegRisk, newFeeBps, overrideTickSize);
@@ -789,7 +793,8 @@ namespace PolyTraderSharp.Services
var sizeMatch = System.Text.RegularExpressions.Regex.Match(responseContent, @"Size \([\d\.]+\) lower than the minimum: (\d+)");
if (sizeMatch.Success)
{
if (decimal.TryParse(sizeMatch.Groups[1].Value, out decimal minReq))
if (decimal.TryParse(sizeMatch.Groups[1].Value, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out decimal minReq))
{
if (orderType != "MARKET")
{
@@ -812,12 +817,15 @@ namespace PolyTraderSharp.Services
decimal totalBal = 0m, activeOrders = 0m;
if (balMatch1.Success)
{
_ = decimal.TryParse(balMatch1.Groups[1].Value, out totalBal);
_ = decimal.TryParse(balMatch1.Groups[2].Value, out activeOrders);
_ = decimal.TryParse(balMatch1.Groups[1].Value, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out totalBal);
_ = decimal.TryParse(balMatch1.Groups[2].Value, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out activeOrders);
}
else if (balMatch2.Success)
{
_ = decimal.TryParse(balMatch2.Groups[1].Value, out totalBal);
_ = decimal.TryParse(balMatch2.Groups[1].Value, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out totalBal);
activeOrders = 0m;
}