using System.Text.Json;
using TradingBot.Models;
namespace TradingBot.Services;
///
/// Service for persisting trade history and active positions to disk
///
public class TradeHistoryService
{
private readonly string _dataDirectory;
private readonly string _tradesFilePath;
private readonly string _activePositionsFilePath;
private readonly ILogger _logger;
private readonly JsonSerializerOptions _jsonOptions;
public TradeHistoryService(ILogger logger)
{
_logger = logger;
_dataDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data");
_tradesFilePath = Path.Combine(_dataDirectory, "trade-history.json");
_activePositionsFilePath = Path.Combine(_dataDirectory, "active-positions.json");
_jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
EnsureDataDirectoryExists();
}
private void EnsureDataDirectoryExists()
{
if (!Directory.Exists(_dataDirectory))
{
Directory.CreateDirectory(_dataDirectory);
_logger.LogInformation("Created data directory: {Directory}", _dataDirectory);
}
}
///
/// Save complete trade history to disk
///
public async Task SaveTradeHistoryAsync(List trades)
{
try
{
var json = JsonSerializer.Serialize(trades, _jsonOptions);
await File.WriteAllTextAsync(_tradesFilePath, json);
_logger.LogInformation("Saved {Count} trades to history", trades.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save trade history");
}
}
///
/// Load trade history from disk
///
public async Task> LoadTradeHistoryAsync()
{
try
{
if (!File.Exists(_tradesFilePath))
{
_logger.LogInformation("No trade history file found, starting fresh");
return new List();
}
var json = await File.ReadAllTextAsync(_tradesFilePath);
var trades = JsonSerializer.Deserialize>(json, _jsonOptions);
_logger.LogInformation("Loaded {Count} trades from history", trades?.Count ?? 0);
return trades ?? new List();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load trade history, starting fresh");
return new List();
}
}
///
/// Save active positions (open trades) to disk
///
public async Task SaveActivePositionsAsync(Dictionary activePositions)
{
try
{
var json = JsonSerializer.Serialize(activePositions, _jsonOptions);
await File.WriteAllTextAsync(_activePositionsFilePath, json);
_logger.LogInformation("Saved {Count} active positions", activePositions.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save active positions");
}
}
///
/// Load active positions from disk
///
public async Task> LoadActivePositionsAsync()
{
try
{
if (!File.Exists(_activePositionsFilePath))
{
_logger.LogInformation("No active positions file found");
return new Dictionary();
}
var json = await File.ReadAllTextAsync(_activePositionsFilePath);
var positions = JsonSerializer.Deserialize>(json, _jsonOptions);
_logger.LogInformation("Loaded {Count} active positions", positions?.Count ?? 0);
return positions ?? new Dictionary();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load active positions");
return new Dictionary();
}
}
///
/// Clear all persisted data
///
public void ClearAll()
{
try
{
if (File.Exists(_tradesFilePath))
File.Delete(_tradesFilePath);
if (File.Exists(_activePositionsFilePath))
File.Delete(_activePositionsFilePath);
_logger.LogInformation("Cleared all persisted trade data");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to clear persisted data");
}
}
///
/// Get total file size of persisted data
///
public long GetDataSize()
{
long size = 0;
if (File.Exists(_tradesFilePath))
size += new FileInfo(_tradesFilePath).Length;
if (File.Exists(_activePositionsFilePath))
size += new FileInfo(_activePositionsFilePath).Length;
return size;
}
}