Files
PolyTraderSharp/services/SnapshotService.cs
T
RichardandClaude Opus 4.8 b951b912f4 Phase 6 (Stufe 6): MongoDB vollstaendig entfernt
- Legacy-Form stillgelegt: frm_main(.Designer/.resx) + frm_analytics(.Designer/.resx)
  geloescht, 'Open Legacy'-Menue aus LauncherForm entfernt.
- Program.cs: IMongoDatabase-Registrierung + closed_trades-Cleanup weg;
  Trade-Nummerierung liest jetzt hoechste TradeId aus ICopyTradeLogRepository.
- StartupHydrationService: Mongo-/Bson-Fallback entfernt (Settings kommen aus
  dem Repo; fehlende Accounts erhalten Default-Settings).
- Alle Mongo*Repository (Core 4 + Modul 4) geloescht; DI-Zweige auf EF/MySQL
  reduziert (AddCorePersistence, CopyTradingModule).
- ConfigMigrator: Mongo-Pfad (Run/MigrateSettings) entfernt; nur noch
  RunFromJson + VerifyMySql (kein Mongo-Treiber mehr).
- Modelle: [BsonId] entfernt (EF-Keys via HasKey); ObjectId-Defaults -> Guid.
- LiteDB-Shim (MongoDbLiteDBShim) geloescht; MongoDB.Driver- + LiteDB-Pakete
  aus App/Core entfernt; stale usings + user-sichtbare Strings bereinigt.
- DatabaseOptions/appsettings.json: Mongo-Provider/Connection-Felder entfernt.
- Build gruen; --verify-mysql bestaetigt 3 Accounts/3 Settings/32 Trader/2344 Markets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 18:34:17 +02:00

153 lines
5.7 KiB
C#

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using PolyTraderSharp.Models;
namespace PolyTraderSharp.Services
{
public class SnapshotService : BackgroundService
{
private readonly TradingState _state;
private readonly CopyTradingState _copyState;
private readonly ILogger<SnapshotService> _logger;
private readonly string _snapshotPath = "snapshot.json";
private readonly TimeSpan _interval = TimeSpan.FromSeconds(30);
private readonly JobStatusRow _jobStatus;
public SnapshotService(TradingState state, CopyTradingState copyState, ILogger<SnapshotService> logger, JobManager jobManager)
{
_state = state;
_copyState = copyState;
_logger = logger;
_jobStatus = new JobStatusRow
{
JobName = "State Snapshot",
Description = "Saves active application state (balances, open pos) to snapshot.json.",
StatusText = "Pending Initial Delay..."
};
_jobStatus.ManualTriggerAction = async () =>
{
string oldStatus = _jobStatus.StatusText;
_jobStatus.StatusText = "Running (Manual)...";
await SaveSnapshotAsync();
_jobStatus.StatusText = "Idle";
};
jobManager.RegisterJob(_jobStatus);
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
// Load state on startup
if (File.Exists(_snapshotPath))
{
try
{
string json = await File.ReadAllTextAsync(_snapshotPath, cancellationToken);
var snapshot = JsonConvert.DeserializeObject<StateSnapshot>(json);
if (snapshot != null)
{
_state.LiveTradingMode = snapshot.LiveMode;
_state.DemoTradingMode = snapshot.DemoMode;
_copyState.TotalCopyTrades = snapshot.CopyTrades;
_state.GlobalPnl = snapshot.GlobalPnl;
int restoredPositions = 0;
// Restore OpenPositions to matching accounts
foreach (var kvp in snapshot.OpenPositions)
{
if (_state.Accounts.TryGetValue(kvp.Key, out var acc))
{
foreach (var pos in kvp.Value)
{
acc.OpenPositions.TryAdd(pos.Key, pos.Value);
restoredPositions++;
}
}
}
_logger.LogInformation($"Snapshot loaded. Restored {restoredPositions} positions. Modes: Live={snapshot.LiveMode}, Demo={snapshot.DemoMode}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load snapshot on startup");
}
}
await base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_jobStatus.StatusText = "Idle";
while (!stoppingToken.IsCancellationRequested)
{
if (_jobStatus.IsEnabled)
{
try
{
_jobStatus.StatusText = "Running (Scheduled)...";
await SaveSnapshotAsync();
_jobStatus.LastRun = DateTime.Now;
}
catch (TaskCanceledException)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving TradingState snapshot");
_jobStatus.StatusText = "Error!";
}
finally
{
if (_jobStatus.StatusText != "Error!") _jobStatus.StatusText = "Idle";
}
}
else
{
_jobStatus.StatusText = "Paused";
}
_jobStatus.NextRun = DateTime.Now.Add(_interval);
await Task.Delay(_interval, stoppingToken);
}
}
private async Task SaveSnapshotAsync()
{
var snapshot = new StateSnapshot
{
LiveMode = _state.LiveTradingMode,
DemoMode = _state.DemoTradingMode,
CopyTrades = _copyState.TotalCopyTrades,
GlobalPnl = _state.GlobalPnl,
OpenPositions = _state.Accounts.ToDictionary(
a => a.Key,
a => a.Value.OpenPositions.ToDictionary(p => p.Key, p => p.Value)
)
};
string json = JsonConvert.SerializeObject(snapshot, Formatting.Indented);
await File.WriteAllTextAsync(_snapshotPath, json);
_logger.LogTrace("TradingState snapshot saved.");
}
private class StateSnapshot
{
public TradingMode LiveMode { get; set; }
public TradingMode DemoMode { get; set; }
public int CopyTrades { get; set; }
public decimal GlobalPnl { get; set; }
public Dictionary<int, Dictionary<string, Position>> OpenPositions { get; set; } = new();
}
}
}