Supervisor S-1: Dossier-Generator + Modul-Skelett mit Dossier-Browser + Journal-Nachverdrahtung

Neues Modul PolyTrader.Modules.Supervisor (IPolyTraderModule, Name=Supervisor, DbPrefix=sup_,
nur Core-Referenz, strikt read-only):
- DossierBuilder (Core/Analytics, pur+getestet): TradeDossier aus Entscheidungen + Order-Events +
  Trades + Log-Zeilen, chronologisch, mit Markdown-Rendering (Tabellen, Pipe-Escaping).
- DossierService (Modul): beschafft Journal/Events/Trade-Log per SignalId + JSONL-Zeilen per CID
  (nur Tagesdateien im Ereignis-Zeitfenster +-1 Tag); RecentSignals-Uebersicht (Journal gruppiert).
- SupervisorMainForm: Dossier-Browser - links juengste Signale, rechts Markdown-Dossier;
  SignalId-Suche; Analyse-Chat (OpenRouter) folgt in S-2. In Launcher/Smoke registriert.

Journal-Nachverdrahtung (S-0-Vervollstaendigung):
- TraderMonitor: Profit-Target erzeugt eigene SignalId -> Leiter + Journal (ProfitTargetTriggered);
  Stale-Cleanup-Cancels als OrderEvents (StaleCleanupCancel).
- StartupOrderReconciliation: K2-Cancels als OrderEvents (StartupReconcileCancel).
- RF: Demo-Einstiege (DemoFilled, eigene SignalId) + Resolution-Closes (SystemResolutionClose)
  im Journal - damit sind ALLE Module im Entscheidungsjournal vertreten.

Tests: +3 DossierBuilder; 4 Service-Builder auf neue Ctors. Build 0 Fehler, 344 Tests gruen,
--smoke-ui: [OK] supervisor.main (alle 5 Views gruen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-17 10:09:02 +02:00
co-authored by Claude Opus 4.8
parent ca00977ea3
commit c75a958e36
18 changed files with 610 additions and 15 deletions
@@ -4,10 +4,12 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PolyTrader.Core.Persistence;
using PolyTrader.Modules.ResolutionFarming.Logic;
using PolyTrader.Modules.ResolutionFarming.Models;
using PolyTrader.Modules.ResolutionFarming.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;
namespace PolyTrader.Modules.ResolutionFarming.Services
@@ -31,10 +33,12 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
private readonly IRfPositionRepository _positionRepo;
private readonly IRfClosedTradeRepository _closedRepo;
private readonly TerminalLogger _logger;
private readonly IDecisionJournal _journal;
public FarmingExecutionService(
TradingState state, IRfSettingsRepository settingsRepo, IRfCandidateRepository candidateRepo,
IRfPositionRepository positionRepo, IRfClosedTradeRepository closedRepo, TerminalLogger logger)
IRfPositionRepository positionRepo, IRfClosedTradeRepository closedRepo, TerminalLogger logger,
IDecisionJournal journal)
{
_state = state;
_settingsRepo = settingsRepo;
@@ -42,6 +46,7 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
_positionRepo = positionRepo;
_closedRepo = closedRepo;
_logger = logger;
_journal = journal;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -119,6 +124,25 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
};
_positionRepo.Upsert(pos);
opened.Add(pos);
// S-0: Entscheidung strukturiert journalisieren (Rejects stehen bereits in rf_candidates).
_journal.Write(new DecisionRecord
{
SignalId = Guid.NewGuid().ToString("N"),
ModuleName = "ResolutionFarming",
AccountId = accountId,
IsDemo = true,
TokenId = c.TokenId,
MarketSlug = c.MarketSlug,
MarketQuestion = c.MarketQuestion,
Side = "BUY",
SignalPrice = c.Ask,
MarketEndDate = c.EndDate,
Decision = TradeDecision.Executed,
Reason = DecisionReason.DemoFilled,
Message = $"RF-Demo-Einstieg {shares:F2} Shares @ {c.Ask:F3} (Score {c.Score:F2})",
ContextJson = System.Text.Json.JsonSerializer.Serialize(new { shares, sizeUsd, score = c.Score, netEdgePct = c.NetEdgePct, cluster = c.ClusterKey })
});
}
return opened;
}
@@ -30,10 +30,12 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
private readonly IMarketResolutionSource _resolution;
private readonly ITradeLogRepository _coreLog;
private readonly TerminalLogger _logger;
private readonly IDecisionJournal _journal;
public FarmingResolutionMonitorService(
TradingState state, IRfPositionRepository positionRepo, IRfClosedTradeRepository closedRepo,
IMarketResolutionSource resolution, ITradeLogRepository coreLog, TerminalLogger logger)
IMarketResolutionSource resolution, ITradeLogRepository coreLog, TerminalLogger logger,
IDecisionJournal journal)
{
_state = state;
_positionRepo = positionRepo;
@@ -41,6 +43,7 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
_resolution = resolution;
_coreLog = coreLog;
_logger = logger;
_journal = journal;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -100,6 +103,22 @@ namespace PolyTrader.Modules.ResolutionFarming.Services
});
_logger.Trade($"🏆 [RF] {pos.MarketQuestion} aufgelöst ({(isWinner ? "Gewinner" : "Verlierer")}) PnL {trade.RealizedPnl:F2} USDC.");
_journal.Write(new DecisionRecord
{
SignalId = Guid.NewGuid().ToString("N"),
ModuleName = "ResolutionFarming",
AccountId = pos.AccountId,
IsDemo = pos.IsDemo,
TokenId = pos.TokenId,
MarketSlug = pos.MarketSlug,
MarketQuestion = pos.MarketQuestion,
Side = "SELL",
SignalPrice = trade.ExitPrice,
Decision = TradeDecision.Executed,
Reason = DecisionReason.SystemResolutionClose,
Message = $"RF-Resolution ({(isWinner ? "Gewinner" : "Verlierer")}), PnL {trade.RealizedPnl:F2}",
ContextJson = System.Text.Json.JsonSerializer.Serialize(new { isWinner, realizedPnl = trade.RealizedPnl, size = pos.Size })
});
result.Add(trade);
}
return result;