CLOB-Sicherheitsnetz: BUY-Risikologik extrahiert + breit getestet; PreRedeemLimit-Skala korrigiert
Ziel (Richard): vor den CLOB-Eingriffen ein umfangreiches Testnetz, damit künftige
Änderungen keine neuen Fehler einschleusen. Reiner, verhaltensneutraler Umbau –
alte Version liegt als Rollback in Git (Commit 38f609e), siehe .agents/rules/clob.md.
- Neue pure Klasse CopyTradingRisk (Logic/): CalculateBuyOrderPrice (HF-fest /
prozentual, gedeckelt durch MaxBuyPrice + harte 0.99), ResolveTimeBucket/
TimeLimitPct/TimeBucketLabel/IsPositionInBucket (Zeitfenster-Exposure),
MaxPerMarket (Markt-Budget inkl. Low-Balance-Bypass-Stufen).
- CopyTradingEngine BUY-Pfad ruft diese Funktionen jetzt statt Inline-Mathematik
(1:1-Semantik, dedupliziert die doppelte Order-Preis-Berechnung).
- CopyTradingRiskTests: 38 Fälle über alle Zweige/Grenzwerte (HF vs. normal,
MaxBuy-/0.99-Deckel, Bucket-Grenzen 6/24/72h, null/expired Expiry,
Balance-Stufen 150/500). Gesamt 124 Tests grün.
Fix: PreRedeemLimit-Skalen-Korrektur (Migration FixPreRedeemLimitScale): Alt-Werte
> 1 (z. B. 99.5) werden /100 (0.995); 0 bleibt deaktiviert. Auf MySQL angewendet.
Build/Smoke grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
38f609ed31
commit
360f264ed8
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PolyTraderSharp.Models;
|
||||
using PolyTrader.Modules.CopyTrading.Logic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
|
||||
@@ -292,26 +293,14 @@ namespace PolyTraderSharp.Services
|
||||
decimal investedInMarket = activePositions.FirstOrDefault(p => p.TokenId == signal.TokenId)?.AmountUsd ?? 0m;
|
||||
|
||||
decimal minTrade = 1.0m;
|
||||
decimal maxAllowed = account.TotalBalance * (settings.PerMarketLimit / 100.0m);
|
||||
|
||||
// Low Balance Bypass (Stufen-System) ALWAYS APPLIES
|
||||
if (account.TotalBalance < 150m) maxAllowed = Math.Min(1.20m, Math.Max(account.AvailableBalance, 0m));
|
||||
else if (account.TotalBalance < 500m) maxAllowed = Math.Min(3.0m, Math.Max(account.AvailableBalance, 0m));
|
||||
|
||||
// Markt-Budget inkl. Low-Balance-Bypass (CopyTradingRisk, unit-getestet)
|
||||
decimal maxAllowed = CopyTradingRisk.MaxPerMarket(account.TotalBalance, account.AvailableBalance, settings.PerMarketLimit);
|
||||
|
||||
if (_copyState.SixSharesMinimum && account.TotalBalance < 500m)
|
||||
{
|
||||
// Adjust maxAllowed to cover at least 6 shares * order limit price.
|
||||
decimal desiredLimitForSix;
|
||||
if (trader != null && trader.Category == "HF")
|
||||
{
|
||||
desiredLimitForSix = signal.Price + 0.005m;
|
||||
}
|
||||
else
|
||||
{
|
||||
desiredLimitForSix = signal.Price * (1.0m + settings.MaxPriceDifference / 100.0m);
|
||||
}
|
||||
decimal orderPriceForSix = Math.Min(desiredLimitForSix, settings.MaxBuyPrice);
|
||||
if (orderPriceForSix > 0.99m) orderPriceForSix = 0.99m;
|
||||
decimal orderPriceForSix = CopyTradingRisk.CalculateBuyOrderPrice(
|
||||
signal.Price, trader != null && trader.Category == "HF", settings.MaxPriceDifference, settings.MaxBuyPrice);
|
||||
decimal costSix = 6m * orderPriceForSix;
|
||||
|
||||
if (costSix > maxAllowed)
|
||||
@@ -335,38 +324,15 @@ namespace PolyTraderSharp.Services
|
||||
return;
|
||||
}
|
||||
|
||||
// Time Limit Restriktion
|
||||
double hoursLeft = signal.EndDate.HasValue ? (signal.EndDate.Value - DateTime.UtcNow).TotalHours : 999999;
|
||||
decimal applicableTimeLimitPct;
|
||||
decimal investedInTimeframe = 0m;
|
||||
string timeframeLabel = "";
|
||||
|
||||
var openVals = activePositions;
|
||||
|
||||
if (hoursLeft < 6)
|
||||
{
|
||||
applicableTimeLimitPct = settings.perMaxTime6h;
|
||||
timeframeLabel = "< 6h";
|
||||
investedInTimeframe = openVals.Where(p => p.ExpiryDate.HasValue && (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours < 6).Sum(p => (decimal)p.AmountUsd);
|
||||
}
|
||||
else if (hoursLeft < 24)
|
||||
{
|
||||
applicableTimeLimitPct = settings.perMaxTime24h;
|
||||
timeframeLabel = "< 24h";
|
||||
investedInTimeframe = openVals.Where(p => p.ExpiryDate.HasValue && (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours >= 6 && (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours < 24).Sum(p => (decimal)p.AmountUsd);
|
||||
}
|
||||
else if (hoursLeft < 72)
|
||||
{
|
||||
applicableTimeLimitPct = settings.perMaxTime72h;
|
||||
timeframeLabel = "< 72h";
|
||||
investedInTimeframe = openVals.Where(p => p.ExpiryDate.HasValue && (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours >= 24 && (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours < 72).Sum(p => (decimal)p.AmountUsd);
|
||||
}
|
||||
else
|
||||
{
|
||||
applicableTimeLimitPct = settings.perMaxTimeNone;
|
||||
timeframeLabel = "> 72h";
|
||||
investedInTimeframe = openVals.Where(p => !p.ExpiryDate.HasValue || (p.ExpiryDate.Value - DateTime.UtcNow).TotalHours >= 72).Sum(p => (decimal)p.AmountUsd);
|
||||
}
|
||||
// Time Limit Restriktion (Zeitfenster-Logik: CopyTradingRisk, unit-getestet)
|
||||
var nowUtc = DateTime.UtcNow;
|
||||
double hoursLeft = signal.EndDate.HasValue ? (signal.EndDate.Value - nowUtc).TotalHours : 999999;
|
||||
var timeBucket = CopyTradingRisk.ResolveTimeBucket(hoursLeft);
|
||||
decimal applicableTimeLimitPct = CopyTradingRisk.TimeLimitPct(timeBucket, settings);
|
||||
string timeframeLabel = CopyTradingRisk.TimeBucketLabel(timeBucket);
|
||||
decimal investedInTimeframe = activePositions
|
||||
.Where(p => CopyTradingRisk.IsPositionInBucket(p.ExpiryDate, timeBucket, nowUtc))
|
||||
.Sum(p => (decimal)p.AmountUsd);
|
||||
|
||||
decimal maxAllowedTimeframe = account.TotalBalance * (applicableTimeLimitPct / 100.0m);
|
||||
|
||||
@@ -402,20 +368,9 @@ namespace PolyTraderSharp.Services
|
||||
return;
|
||||
}
|
||||
|
||||
decimal desiredLimit;
|
||||
if (trader != null && trader.Category == "HF")
|
||||
{
|
||||
// HF Trader: festes 0.5 Cent (0.005) Limit
|
||||
desiredLimit = signal.Price + 0.005m;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normaler Trader: prozentuales Limit aus Slave-Account Settings
|
||||
desiredLimit = signal.Price * (1.0m + settings.MaxPriceDifference / 100.0m);
|
||||
}
|
||||
|
||||
orderPrice = Math.Min(desiredLimit, settings.MaxBuyPrice);
|
||||
if (orderPrice > 0.99m) orderPrice = 0.99m;
|
||||
// Limit-Preis (HF-fest / prozentual, gedeckelt) – CopyTradingRisk, unit-getestet
|
||||
orderPrice = CopyTradingRisk.CalculateBuyOrderPrice(
|
||||
signal.Price, trader != null && trader.Category == "HF", settings.MaxPriceDifference, settings.MaxBuyPrice);
|
||||
|
||||
var exact = PolymarketClobClient.CalculateExactOrderAmounts(maxAmountToBuy, orderPrice, orderPrice, "BUY");
|
||||
if (exact.shares <= 0 || exact.usdc > account.AvailableBalance)
|
||||
|
||||
Reference in New Issue
Block a user