Initial commit: IBKRTrader
.NET WinForms-Anwendung (Core, Modules/CongressTrading, UI). Enthaelt .gitignore und settings.example.json als Konfigurationsvorlage. Echte settings.json mit Zugangsdaten ist bewusst ausgeschlossen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using IBKRTrader.Core.Database;
|
||||
using IBKRTrader.Core.Logging;
|
||||
using IBKRTrader.Core.Settings;
|
||||
using IBKRTrader.Modules.CongressTrading.Database;
|
||||
using IBKRTrader.Modules.CongressTrading.Scraper;
|
||||
using IBKRTrader.Core.Workers;
|
||||
|
||||
namespace IBKRTrader.Modules.CongressTrading.Workers;
|
||||
|
||||
/// <summary>
|
||||
/// Periodischer Scrape-Worker: alle 30 Minuten.
|
||||
/// Lädt nur Seite 1 (neueste 96 Trades) und prüft auf neue Einträge.
|
||||
/// Alle Daten (Datum, Typ, Value, Ticker, Mitglied) kommen direkt aus dem RSC-JSON.
|
||||
/// </summary>
|
||||
public class CongressScrapeWorker : WorkerBase
|
||||
{
|
||||
private readonly CapitolTradesScraper _scraper;
|
||||
private readonly CongressRepository _repo;
|
||||
|
||||
public override string Name => "CT-ScrapeWorker";
|
||||
public override string Module => "CT";
|
||||
public override WorkerType Type => WorkerType.Worker;
|
||||
|
||||
protected override TimeSpan? Interval => TimeSpan.FromMinutes(30);
|
||||
|
||||
public CongressScrapeWorker(
|
||||
LoggingService logger,
|
||||
DatabaseService db,
|
||||
CapitolTradesScraper scraper,
|
||||
CongressRepository repo,
|
||||
SettingsService settings)
|
||||
: base(logger, db)
|
||||
{
|
||||
_scraper = scraper;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken ct)
|
||||
{
|
||||
Logger.Info(Module, "Suche neue Kongress-Trades (Seite 1)...");
|
||||
|
||||
var knownMembers = await _repo.GetAllMemberBioIdsAsync();
|
||||
var (trades, _) = await _scraper.ScrapeTradesPageAsync(1, ct);
|
||||
|
||||
if (trades.Count == 0)
|
||||
{
|
||||
Logger.Warn(Module, "Scrape: Keine Trades auf Seite 1 gefunden.");
|
||||
Info.Info = $"Letzter Lauf {DateTime.Now:HH:mm}: 0 Trades";
|
||||
return;
|
||||
}
|
||||
|
||||
// Neue Kongressmitglieder
|
||||
var newMembers = 0;
|
||||
foreach (var trade in trades.Where(t => t.MemberSnapshot != null))
|
||||
{
|
||||
var snapshot = trade.MemberSnapshot!;
|
||||
if (knownMembers.Contains(snapshot.BioId)) continue;
|
||||
|
||||
await _repo.UpsertMemberAsync(snapshot);
|
||||
knownMembers.Add(snapshot.BioId);
|
||||
Logger.Info(Module, $"Neues Mitglied: {snapshot.Name} ({snapshot.BioId})");
|
||||
newMembers++;
|
||||
}
|
||||
|
||||
// Neue Trades
|
||||
var newTrades = 0;
|
||||
foreach (var trade in trades)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (await _repo.TradeExistsAsync(trade.TradeId)) continue;
|
||||
|
||||
await _repo.InsertTradeAsync(trade, detailsFetched: true);
|
||||
Logger.Info(Module,
|
||||
$" + {trade.IssuerName} ({trade.TradeType}) [{trade.Ticker}] von {trade.MemberBioId}");
|
||||
newTrades++;
|
||||
}
|
||||
|
||||
var summary = $"{newTrades} neue Trades, {newMembers} neue Mitglieder";
|
||||
Logger.Info(Module, $"Scrape abgeschlossen: {summary}");
|
||||
Info.Info = $"Letzter Lauf {DateTime.Now:HH:mm}: {summary}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user