WebUI-Tests rufen jetzt echte Routen auf
Behebt #568. Keine der Testdateien mit "EndpointsTests" im Namen hat je einen Endpunkt aufgerufen. CopyTradingAndResolutionFarmingEndpointsTests beruehrte gar keinen Produktionscode: Die Tests summierten Listen im Test und verglichen das Ergebnis mit sich selbst. Modules_KnownModules pruefte, ob ein Array seine eigenen Elemente enthaelt. Settings_UpdateRequest pruefte die Portbegrenzung an einer im Test nachgebauten Lambda. Solche Tests bleiben gruen, egal was der Endpunkt tut. Neu startet WebUiTestHost das WebUI in einem Testserver (Microsoft.AspNetCore.TestHost) und spricht es ueber HTTP an. Damit der Test die Kette aus dem Betrieb prueft und keine nachgebaute, liegt sie jetzt in UsePolyTraderWebPipeline(): statische Dateien, Token-Pruefung, alle Routen. ConfigurePolyTraderWeb ruft genau diese Methode. Ersetzt wird nur, was ausserhalb des Prozesses liegt - Kestrel durch den Testserver, MySQL durch den InMemory-Provider. Die acht CopyTrading/ResolutionFarming-Tests pruefen dieselben Absichten wie vorher, jetzt an der Antwort der Route. Neu hinzugekommen sind Tests fuer: - Zugangssperre: ohne Token, mit falschem Token, ohne konfiguriertes Token - die Modulliste - Job-Umschalten samt 404 - die Ablehnung einer leeren Supervisor-Frage Die beiden Tests, die schon echten Code prueften, bleiben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,304 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using PolyTrader.Modules.CopyTrading.Persistence;
|
||||
using PolyTrader.Modules.ResolutionFarming.Models;
|
||||
using PolyTrader.Web.Endpoints;
|
||||
using PolyTrader.Modules.ResolutionFarming.Persistence;
|
||||
using PolyTraderSharp.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace PolyTrader.Tests.WebUi
|
||||
{
|
||||
/// <summary>
|
||||
/// CopyTrading- und ResolutionFarming-Endpunkte ueber echte HTTP-Aufrufe (#568).
|
||||
///
|
||||
/// Die Datei gleichen Namens enthielt vorher acht Tests, die keine einzige Zeile Produktionscode
|
||||
/// aufriefen: Sie summierten Listen im Test und verglichen das Ergebnis mit sich selbst. Die
|
||||
/// Absichten sind dieselben geblieben, geprueft wird jetzt die Antwort der Route.
|
||||
/// </summary>
|
||||
public class CopyTradingAndResolutionFarmingEndpointsTests
|
||||
{
|
||||
[Fact]
|
||||
public void CopyTrading_TrackedTraderInput_MapsCorrectly()
|
||||
{
|
||||
var input = new CopyTradingEndpoints.TrackedTraderInput(
|
||||
Id: 0,
|
||||
DisplayName: "Whale Alpha",
|
||||
WalletAddress: "0x1234567890abcdef1234567890abcdef12345678",
|
||||
Category: "INSIDER",
|
||||
Description: "Top Polymarket Trader",
|
||||
Reasoning: "Consistent winrate above 70%",
|
||||
IsActive: true,
|
||||
IsHidden: false,
|
||||
AutoPauseEnabled: true,
|
||||
MakerEntry: true,
|
||||
AssignedAccountIds: new List<int> { 1, 2 }
|
||||
);
|
||||
private static Task<WebUiTestHost> StartCopyTradingAsync() =>
|
||||
WebUiTestHost.StartAsync(WebUiTestHost.AddCopyTradingStore);
|
||||
|
||||
int id = input.Id <= 0 ? 1 : input.Id;
|
||||
var trader = new TrackedTrader
|
||||
private static Task<WebUiTestHost> StartResolutionFarmingAsync() =>
|
||||
WebUiTestHost.StartAsync(WebUiTestHost.AddResolutionFarmingStore);
|
||||
|
||||
// ---- CopyTrading -----------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public async Task CopyTrading_Trader_anlegen_und_wieder_lesen()
|
||||
{
|
||||
await using var web = await StartCopyTradingAsync();
|
||||
|
||||
var (status, created) = await web.PostJsonAsync("/api/modules/copytrading/traders", new
|
||||
{
|
||||
Id = id,
|
||||
DisplayName = input.DisplayName ?? $"Trader #{id}",
|
||||
WalletAddress = input.WalletAddress?.Trim() ?? string.Empty,
|
||||
Category = string.IsNullOrWhiteSpace(input.Category) ? "NEW_BIG_BET" : input.Category.Trim(),
|
||||
Description = input.Description ?? string.Empty,
|
||||
Reasoning = input.Reasoning ?? string.Empty,
|
||||
IsActive = input.IsActive,
|
||||
IsHidden = input.IsHidden,
|
||||
AutoPauseEnabled = input.AutoPauseEnabled,
|
||||
MakerEntry = input.MakerEntry,
|
||||
AssignedAccountIds = new HashSet<int>(input.AssignedAccountIds ?? new List<int>())
|
||||
};
|
||||
id = 0,
|
||||
displayName = "Whale Alpha",
|
||||
walletAddress = " 0x1234567890abcdef1234567890abcdef12345678 ",
|
||||
category = " ",
|
||||
description = "Top Polymarket Trader",
|
||||
reasoning = "Winrate ueber 70 Prozent",
|
||||
isActive = true,
|
||||
isHidden = false,
|
||||
autoPauseEnabled = true,
|
||||
makerEntry = true,
|
||||
assignedAccountIds = new[] { 1, 2 }
|
||||
});
|
||||
Assert.Equal(200, status);
|
||||
|
||||
Assert.Equal(1, trader.Id);
|
||||
Assert.Equal("Whale Alpha", trader.DisplayName);
|
||||
Assert.Equal("INSIDER", trader.Category);
|
||||
Assert.True(trader.IsActive);
|
||||
Assert.True(trader.MakerEntry);
|
||||
Assert.Equal(2, trader.AssignedAccountIds.Count);
|
||||
Assert.Contains(1, trader.AssignedAccountIds);
|
||||
Assert.Contains(2, trader.AssignedAccountIds);
|
||||
var trader = created.GetProperty("trader");
|
||||
Assert.Equal(1, trader.GetProperty("id").GetInt32());
|
||||
Assert.Equal("NEW_BIG_BET", trader.GetProperty("category").GetString()); // leere Kategorie -> Vorgabe
|
||||
Assert.Equal("0x1234567890abcdef1234567890abcdef12345678", trader.GetProperty("walletAddress").GetString());
|
||||
|
||||
var all = await web.GetJsonAsync("/api/modules/copytrading/traders");
|
||||
var stored = Assert.Single(all.EnumerateArray());
|
||||
Assert.Equal("Whale Alpha", stored.GetProperty("displayName").GetString());
|
||||
Assert.True(stored.GetProperty("makerEntry").GetBoolean());
|
||||
Assert.Equal(new[] { 1, 2 },
|
||||
stored.GetProperty("assignedAccountIds").EnumerateArray().Select(e => e.GetInt32()).OrderBy(x => x).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyTrading_OpenTrades_CalculatesUnrealizedMetricsProperly()
|
||||
public async Task CopyTrading_offene_Trades_rechnen_Buchgewinn_je_Position_und_gesamt()
|
||||
{
|
||||
var positions = new List<Position>
|
||||
await using var web = await StartCopyTradingAsync();
|
||||
|
||||
var acc1 = new AccountState { AccountId = 1, Name = "Konto 1" };
|
||||
acc1.OpenPositions["tok-1"] = new Position
|
||||
{
|
||||
new Position
|
||||
{
|
||||
AccountId = 1,
|
||||
SourceTraderId = 1,
|
||||
SourceTraderName = "Whale 1",
|
||||
MarketQuestion = "Will Trump win in 2024?",
|
||||
Outcome = "Yes",
|
||||
Side = "BUY",
|
||||
EntryPrice = 0.50m,
|
||||
CurrentPrice = 0.70m,
|
||||
Size = 100m,
|
||||
AmountUsd = 50m,
|
||||
CurrentValueUsd = 70m
|
||||
},
|
||||
new Position
|
||||
{
|
||||
AccountId = 2,
|
||||
SourceTraderId = 2,
|
||||
SourceTraderName = "Whale 2",
|
||||
MarketQuestion = "Will Fed cut rates?",
|
||||
Outcome = "Yes",
|
||||
Side = "BUY",
|
||||
EntryPrice = 0.80m,
|
||||
CurrentPrice = 0.60m,
|
||||
Size = 50m,
|
||||
AmountUsd = 40m,
|
||||
CurrentValueUsd = 30m
|
||||
}
|
||||
AccountId = 1, TokenId = "tok-1", SourceTraderId = 1, SourceTraderName = "Whale 1",
|
||||
MarketQuestion = "Will Trump win in 2024?", Outcome = "Yes",
|
||||
EntryPrice = 0.50m, CurrentPrice = 0.70m, Size = 100m, AmountUsd = 50m, CurrentValueUsd = 70m
|
||||
};
|
||||
|
||||
decimal totalAmount = positions.Sum(p => p.AmountUsd);
|
||||
decimal totalUnrealized = positions.Sum(p => p.CurrentValueUsd - p.AmountUsd);
|
||||
|
||||
Assert.Equal(90m, totalAmount);
|
||||
Assert.Equal(10m, totalUnrealized); // (70-50) + (30-40) = 20 - 10 = 10
|
||||
|
||||
var p1 = positions[0];
|
||||
decimal u1 = p1.CurrentValueUsd - p1.AmountUsd;
|
||||
decimal pct1 = p1.AmountUsd > 0 ? 100m * u1 / p1.AmountUsd : 0m;
|
||||
Assert.Equal(20m, u1);
|
||||
Assert.Equal(40m, pct1);
|
||||
|
||||
var p2 = positions[1];
|
||||
decimal u2 = p2.CurrentValueUsd - p2.AmountUsd;
|
||||
decimal pct2 = p2.AmountUsd > 0 ? 100m * u2 / p2.AmountUsd : 0m;
|
||||
Assert.Equal(-10m, u2);
|
||||
Assert.Equal(-25m, pct2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyTrading_ClosedTrades_FiltersCorrectly()
|
||||
{
|
||||
var closed = new List<ClosedTrade>
|
||||
var acc2 = new AccountState { AccountId = 2, Name = "Konto 2" };
|
||||
acc2.OpenPositions["tok-2"] = new Position
|
||||
{
|
||||
new ClosedTrade
|
||||
{
|
||||
TradeId = 1,
|
||||
AccountId = 1,
|
||||
SourceTraderId = 10,
|
||||
MarketQuestion = "Bitcoin reaches 100k?",
|
||||
Outcome = "Yes",
|
||||
RealizedPnl = 35m,
|
||||
ClosedAt = DateTime.UtcNow.AddHours(-2)
|
||||
},
|
||||
new ClosedTrade
|
||||
{
|
||||
TradeId = 2,
|
||||
AccountId = 1,
|
||||
SourceTraderId = 10,
|
||||
MarketQuestion = "Solana reaches 300?",
|
||||
Outcome = "No",
|
||||
RealizedPnl = -12m,
|
||||
ClosedAt = DateTime.UtcNow.AddHours(-5)
|
||||
},
|
||||
new ClosedTrade
|
||||
{
|
||||
TradeId = 3,
|
||||
AccountId = 2,
|
||||
SourceTraderId = 20,
|
||||
MarketQuestion = "Ethereum reaches 5k?",
|
||||
Outcome = "Yes",
|
||||
RealizedPnl = 50m,
|
||||
ClosedAt = DateTime.UtcNow.AddHours(-1)
|
||||
}
|
||||
AccountId = 2, TokenId = "tok-2", SourceTraderId = 2, SourceTraderName = "Whale 2",
|
||||
MarketQuestion = "Will Fed cut rates?", Outcome = "Yes",
|
||||
EntryPrice = 0.80m, CurrentPrice = 0.60m, Size = 50m, AmountUsd = 40m, CurrentValueUsd = 30m
|
||||
};
|
||||
web.State.Accounts[1] = acc1;
|
||||
web.State.Accounts[2] = acc2;
|
||||
|
||||
// Filter by Trader
|
||||
var trader10Trades = closed.Where(t => t.SourceTraderId == 10).ToList();
|
||||
Assert.Equal(2, trader10Trades.Count);
|
||||
var open = await web.GetJsonAsync("/api/modules/copytrading/open-trades");
|
||||
|
||||
// Filter by Result
|
||||
var winners = closed.Where(t => t.RealizedPnl > 0).ToList();
|
||||
Assert.Equal(2, winners.Count);
|
||||
decimal totalWinPnl = winners.Sum(w => w.RealizedPnl);
|
||||
Assert.Equal(85m, totalWinPnl);
|
||||
Assert.Equal(2, open.GetProperty("count").GetInt32());
|
||||
Assert.Equal(90m, open.GetProperty("totalAmount").GetDecimal());
|
||||
Assert.Equal(10m, open.GetProperty("totalUnrealized").GetDecimal()); // (70-50) + (30-40)
|
||||
|
||||
// Search Filter
|
||||
string search = "Bitcoin";
|
||||
var searchResults = closed.Where(t => t.MarketQuestion.Contains(search, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
var matched = Assert.Single(searchResults);
|
||||
Assert.Equal(1, matched.TradeId);
|
||||
var byMarket = open.GetProperty("trades").EnumerateArray()
|
||||
.ToDictionary(t => t.GetProperty("market").GetString()!);
|
||||
Assert.Equal(20m, byMarket["Will Trump win in 2024?"].GetProperty("unrealizedPnl").GetDecimal());
|
||||
Assert.Equal(40m, byMarket["Will Trump win in 2024?"].GetProperty("unrealizedPct").GetDecimal());
|
||||
Assert.Equal(-10m, byMarket["Will Fed cut rates?"].GetProperty("unrealizedPnl").GetDecimal());
|
||||
Assert.Equal(-25m, byMarket["Will Fed cut rates?"].GetProperty("unrealizedPct").GetDecimal());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyTrading_AccountSettings_DefaultValuesAreConsistent()
|
||||
public async Task CopyTrading_geschlossene_Trades_filtern_nach_Trader_Ergebnis_und_Suche()
|
||||
{
|
||||
var settings = new CopyTradingAccountSettings { AccountId = 42 };
|
||||
await using var web = await StartCopyTradingAsync();
|
||||
var log = web.Get<ICopyTradeLogRepository>();
|
||||
log.Insert(new ClosedTrade { TradeId = 1, AccountId = 1, SourceTraderId = 10, TokenId = "a",
|
||||
MarketQuestion = "Bitcoin reaches 100k?", Outcome = "Yes", RealizedPnl = 35m, ClosedAt = DateTime.UtcNow.AddHours(-2) });
|
||||
log.Insert(new ClosedTrade { TradeId = 2, AccountId = 1, SourceTraderId = 10, TokenId = "b",
|
||||
MarketQuestion = "Solana reaches 300?", Outcome = "No", RealizedPnl = -12m, ClosedAt = DateTime.UtcNow.AddHours(-5) });
|
||||
log.Insert(new ClosedTrade { TradeId = 3, AccountId = 2, SourceTraderId = 20, TokenId = "c",
|
||||
MarketQuestion = "Ethereum reaches 5k?", Outcome = "Yes", RealizedPnl = 50m, ClosedAt = DateTime.UtcNow.AddHours(-1) });
|
||||
|
||||
Assert.Equal(42, settings.AccountId);
|
||||
Assert.Equal(5.0m, settings.PerMarketLimit);
|
||||
Assert.Equal(10.0m, settings.PerMasterLimit);
|
||||
Assert.Equal(0.98m, settings.MaxBuyPrice);
|
||||
Assert.Equal(2.0m, settings.MaxPriceDifference);
|
||||
Assert.Equal(15.0m, settings.SellFloorPct);
|
||||
var byTrader = await web.GetJsonAsync("/api/modules/copytrading/closed-trades?traderId=10");
|
||||
Assert.Equal(2, byTrader.GetProperty("totalCount").GetInt32());
|
||||
|
||||
var winners = await web.GetJsonAsync("/api/modules/copytrading/closed-trades?result=Gewinner");
|
||||
Assert.Equal(2, winners.GetProperty("totalCount").GetInt32());
|
||||
Assert.Equal(85m, winners.GetProperty("summary").GetProperty("totalPnl").GetDecimal());
|
||||
|
||||
var search = await web.GetJsonAsync("/api/modules/copytrading/closed-trades?search=bitcoin");
|
||||
var hit = Assert.Single(search.GetProperty("items").EnumerateArray());
|
||||
Assert.Equal(1, hit.GetProperty("tradeId").GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolutionFarming_Candidates_MappingAndEdgeFiltering()
|
||||
public async Task CopyTrading_Kontoeinstellungen_ohne_Eintrag_liefern_die_Vorgaben()
|
||||
{
|
||||
var candidates = new List<RfCandidate>
|
||||
{
|
||||
new RfCandidate
|
||||
{
|
||||
Id = 1,
|
||||
AccountId = 1,
|
||||
MarketQuestion = "Will candidate A win election?",
|
||||
Outcome = "Yes",
|
||||
Category = "Politics",
|
||||
Ask = 0.94m,
|
||||
NetEdgePct = 2.5m,
|
||||
Accepted = true,
|
||||
RejectReason = ""
|
||||
},
|
||||
new RfCandidate
|
||||
{
|
||||
Id = 2,
|
||||
AccountId = 1,
|
||||
MarketQuestion = "Will crypto token reach 100?",
|
||||
Outcome = "Yes",
|
||||
Category = "Crypto",
|
||||
Ask = 0.91m,
|
||||
NetEdgePct = 0.5m,
|
||||
Accepted = false,
|
||||
RejectReason = "Fee too high for edge"
|
||||
}
|
||||
};
|
||||
await using var web = await StartCopyTradingAsync();
|
||||
|
||||
var acceptedOnly = candidates.Where(c => c.Accepted).ToList();
|
||||
var accepted = Assert.Single(acceptedOnly);
|
||||
Assert.Equal("Politics", accepted.Category);
|
||||
Assert.True(accepted.NetEdgePct >= 1.5m);
|
||||
var settings = await web.GetJsonAsync("/api/modules/copytrading/settings/42");
|
||||
|
||||
var rejected = candidates.First(c => !c.Accepted);
|
||||
Assert.Equal("Fee too high for edge", rejected.RejectReason);
|
||||
Assert.Equal(42, settings.GetProperty("accountId").GetInt32());
|
||||
Assert.Equal(5.0m, settings.GetProperty("perMarketLimit").GetDecimal());
|
||||
Assert.Equal(10.0m, settings.GetProperty("perMasterLimit").GetDecimal());
|
||||
Assert.Equal(0.98m, settings.GetProperty("maxBuyPrice").GetDecimal());
|
||||
Assert.Equal(15.0m, settings.GetProperty("sellFloorPct").GetDecimal());
|
||||
}
|
||||
|
||||
// ---- ResolutionFarming -----------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public async Task ResolutionFarming_Kandidaten_liefern_Annahme_und_Ablehnungsgrund()
|
||||
{
|
||||
await using var web = await StartResolutionFarmingAsync();
|
||||
web.State.Accounts[1] = new AccountState { AccountId = 1, Name = "Demo", IsDemo = true };
|
||||
var repo = web.Get<IRfCandidateRepository>();
|
||||
repo.Insert(new RfCandidate { AccountId = 1, TokenId = "t1", ScannedAt = DateTime.UtcNow,
|
||||
MarketQuestion = "Will candidate A win election?", Outcome = "Yes", Category = "Politics",
|
||||
Ask = 0.94m, NetEdgePct = 2.5m, Accepted = true, RejectReason = "" });
|
||||
repo.Insert(new RfCandidate { AccountId = 1, TokenId = "t2", ScannedAt = DateTime.UtcNow,
|
||||
MarketQuestion = "Will crypto token reach 100?", Outcome = "Yes", Category = "Crypto",
|
||||
Ask = 0.91m, NetEdgePct = 0.5m, Accepted = false, RejectReason = "Fee too high for edge" });
|
||||
|
||||
var result = await web.GetJsonAsync("/api/modules/resolutionfarming/candidates?accountId=1");
|
||||
|
||||
Assert.Equal(2, result.GetProperty("count").GetInt32());
|
||||
var items = result.GetProperty("candidates").EnumerateArray().ToList();
|
||||
var accepted = Assert.Single(items, c => c.GetProperty("accepted").GetBoolean());
|
||||
Assert.Equal("Politics", accepted.GetProperty("category").GetString());
|
||||
var rejected = Assert.Single(items, c => !c.GetProperty("accepted").GetBoolean());
|
||||
Assert.Equal("Fee too high for edge", rejected.GetProperty("rejectReason").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolutionFarming_Positions_SummaryMetrics()
|
||||
public async Task ResolutionFarming_Positionen_summieren_den_Einsatz()
|
||||
{
|
||||
var positions = new List<RfPosition>
|
||||
{
|
||||
new RfPosition
|
||||
{
|
||||
AccountId = 1,
|
||||
TokenId = "tok-1",
|
||||
MarketQuestion = "Match 1",
|
||||
EntryPrice = 0.95m,
|
||||
Size = 100m,
|
||||
AmountUsd = 95m,
|
||||
Status = "Open"
|
||||
},
|
||||
new RfPosition
|
||||
{
|
||||
AccountId = 1,
|
||||
TokenId = "tok-2",
|
||||
MarketQuestion = "Match 2",
|
||||
EntryPrice = 0.92m,
|
||||
Size = 50m,
|
||||
AmountUsd = 46m,
|
||||
Status = "Open"
|
||||
}
|
||||
};
|
||||
await using var web = await StartResolutionFarmingAsync();
|
||||
var repo = web.Get<IRfPositionRepository>();
|
||||
repo.Upsert(new RfPosition { AccountId = 1, TokenId = "tok-1", MarketQuestion = "Match 1",
|
||||
EntryPrice = 0.95m, Size = 100m, AmountUsd = 95m, Status = "Open", OpenedAt = DateTime.UtcNow });
|
||||
repo.Upsert(new RfPosition { AccountId = 1, TokenId = "tok-2", MarketQuestion = "Match 2",
|
||||
EntryPrice = 0.92m, Size = 50m, AmountUsd = 46m, Status = "Open", OpenedAt = DateTime.UtcNow });
|
||||
|
||||
decimal totalInvested = positions.Sum(p => p.AmountUsd);
|
||||
Assert.Equal(141m, totalInvested);
|
||||
Assert.Equal(2, positions.Count);
|
||||
var result = await web.GetJsonAsync("/api/modules/resolutionfarming/positions?accountId=1");
|
||||
|
||||
Assert.Equal(2, result.GetProperty("count").GetInt32());
|
||||
Assert.Equal(141m, result.GetProperty("totalAmount").GetDecimal());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolutionFarming_History_CalculatesPnlAndWinRate()
|
||||
public async Task ResolutionFarming_Historie_rechnet_PnL_und_Trefferquote()
|
||||
{
|
||||
var history = new List<RfClosedTrade>
|
||||
{
|
||||
new RfClosedTrade
|
||||
{
|
||||
Id = 101,
|
||||
AccountId = 1,
|
||||
MarketQuestion = "Match A",
|
||||
RealizedPnl = 4.50m,
|
||||
RedeemStatus = "Redeemed"
|
||||
},
|
||||
new RfClosedTrade
|
||||
{
|
||||
Id = 102,
|
||||
AccountId = 1,
|
||||
MarketQuestion = "Match B",
|
||||
RealizedPnl = 3.80m,
|
||||
RedeemStatus = "Redeemed"
|
||||
},
|
||||
new RfClosedTrade
|
||||
{
|
||||
Id = 103,
|
||||
AccountId = 1,
|
||||
MarketQuestion = "Match C",
|
||||
RealizedPnl = -20.00m,
|
||||
RedeemStatus = "None"
|
||||
}
|
||||
};
|
||||
await using var web = await StartResolutionFarmingAsync();
|
||||
var repo = web.Get<IRfClosedTradeRepository>();
|
||||
repo.Insert(new RfClosedTrade { AccountId = 1, TokenId = "a", MarketQuestion = "Match A", RealizedPnl = 4.50m, RedeemStatus = "Redeemed", ClosedAt = DateTime.UtcNow });
|
||||
repo.Insert(new RfClosedTrade { AccountId = 1, TokenId = "b", MarketQuestion = "Match B", RealizedPnl = 3.80m, RedeemStatus = "Redeemed", ClosedAt = DateTime.UtcNow });
|
||||
repo.Insert(new RfClosedTrade { AccountId = 1, TokenId = "c", MarketQuestion = "Match C", RealizedPnl = -20.00m, RedeemStatus = "None", ClosedAt = DateTime.UtcNow });
|
||||
|
||||
decimal totalPnl = history.Sum(t => t.RealizedPnl);
|
||||
int wins = history.Count(t => t.RealizedPnl > 0);
|
||||
decimal winrate = 100m * wins / history.Count;
|
||||
var result = await web.GetJsonAsync("/api/modules/resolutionfarming/history?accountId=1");
|
||||
var summary = result.GetProperty("summary");
|
||||
|
||||
Assert.Equal(-11.70m, totalPnl);
|
||||
Assert.Equal(2, wins);
|
||||
Assert.Equal(66.7m, Math.Round(winrate, 1));
|
||||
Assert.Equal(3, summary.GetProperty("count").GetInt32());
|
||||
Assert.Equal(-11.70m, summary.GetProperty("pnl").GetDecimal());
|
||||
Assert.Equal(2, summary.GetProperty("wins").GetInt32());
|
||||
Assert.Equal(66.7m, Math.Round(summary.GetProperty("winRate").GetDecimal(), 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolutionFarming_Settings_DefaultValuesValid()
|
||||
public async Task ResolutionFarming_Einstellungen_ohne_Eintrag_liefern_die_Vorgaben()
|
||||
{
|
||||
var settings = new RfSettings { AccountId = 5 };
|
||||
await using var web = await StartResolutionFarmingAsync();
|
||||
|
||||
Assert.Equal(5, settings.AccountId);
|
||||
Assert.False(settings.Enabled);
|
||||
Assert.Equal(0.90m, settings.MinPrice);
|
||||
Assert.Equal(0.98m, settings.MaxPrice);
|
||||
Assert.Equal(48, settings.MaxHoursToResolution);
|
||||
Assert.Equal(1.5m, settings.MinEdgePct);
|
||||
Assert.Equal(25m, settings.MaxPerMarketUsd);
|
||||
Assert.Contains("Sports", settings.CategoryWhitelistCsv);
|
||||
var settings = await web.GetJsonAsync("/api/modules/resolutionfarming/settings/5");
|
||||
|
||||
Assert.Equal(5, settings.GetProperty("accountId").GetInt32());
|
||||
Assert.False(settings.GetProperty("enabled").GetBoolean());
|
||||
Assert.Equal(0.90m, settings.GetProperty("minPrice").GetDecimal());
|
||||
Assert.Equal(0.98m, settings.GetProperty("maxPrice").GetDecimal());
|
||||
Assert.Equal(48, settings.GetProperty("maxHoursToResolution").GetInt32());
|
||||
Assert.Contains("Sports", settings.GetProperty("categoryWhitelistCsv").GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user