Insider-Follow feed: auto-watchlist possible-insider wallets + new-trade alerts

Makes the existing possible_insider trait actionable:
- New AlertType.InsiderActivity (severity 4).
- AlertService.EvaluateInsiderWatchAsync (run from EvaluateAlertsAsync every
  15 min): auto-adds every possible_insider wallet to the watchlist, then fires
  one InsiderActivity alert per new trade one of them places. These wallets
  trade rarely, so a single new trade is the strongest copy signal.
- Dedup + no history spam via WatchlistEntry.LastInsiderAlertAt (migration
  AddWatchlistLastInsiderAlertAt); a freshly auto-added wallet is baselined at
  AddedAt so backfilled trades never alert.
- Repo support: ITraderRepository.GetByTraitAsync, IWatchlistRepository.UpdateAsync.
- UI: distinct 👁 icon for insider alerts (💰 for large positions).
- Tests: auto-add-without-history-alert, alert-on-new-trade-with-dedup.

Migration auto-applies on startup (DependencyInjection.MigrateAsync).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
This commit is contained in:
Richard
2026-07-23 19:46:36 +02:00
parent 295752d778
commit 8b9b34342f
12 changed files with 1507 additions and 2 deletions
@@ -21,6 +21,9 @@ public class WatchlistRepository : IWatchlistRepository
public async Task AddAsync(WatchlistEntry entry, CancellationToken ct = default)
{ _db.WatchlistEntries.Add(entry); await _db.SaveChangesAsync(ct); }
public async Task UpdateAsync(WatchlistEntry entry, CancellationToken ct = default)
{ _db.WatchlistEntries.Update(entry); await _db.SaveChangesAsync(ct); }
public async Task RemoveAsync(int id, CancellationToken ct = default)
{
var e = await _db.WatchlistEntries.FindAsync(new object[] { id }, ct);