Sono stati aggiunti tutti i file principali di Bootstrap 5.3.3, inclusi CSS, JavaScript (bundle, ESM, UMD, minificati), versioni RTL, utility, reboot, griglia e relative mappe delle sorgenti. Questi file abilitano un sistema di design moderno, responsive e accessibile, con supporto per layout LTR e RTL, debugging avanzato tramite source map e tutte le funzionalità di Bootstrap per lo sviluppo dell’interfaccia utente. Nessuna modifica ai file esistenti.
210 lines
8.1 KiB
C#
210 lines
8.1 KiB
C#
using TradingBot.Models;
|
|
|
|
namespace TradingBot.Services;
|
|
|
|
public class SimulatedMarketDataService : IMarketDataService
|
|
{
|
|
private readonly Dictionary<string, SimulatedAsset> _assets = new();
|
|
private readonly Random _random = new();
|
|
private readonly Timer _updateTimer;
|
|
private readonly object _lock = new();
|
|
|
|
public event Action? OnPriceUpdated;
|
|
|
|
public SimulatedMarketDataService()
|
|
{
|
|
InitializeAssets();
|
|
_updateTimer = new Timer(UpdatePrices, null, TimeSpan.Zero, TimeSpan.FromSeconds(2));
|
|
}
|
|
|
|
private void InitializeAssets()
|
|
{
|
|
var assets = new[]
|
|
{
|
|
new { Symbol = "BTC", Name = "Bitcoin", BasePrice = 45000m, Volatility = 0.02m, TrendBias = 0.0002m },
|
|
new { Symbol = "ETH", Name = "Ethereum", BasePrice = 2500m, Volatility = 0.025m, TrendBias = 0.0003m },
|
|
new { Symbol = "BNB", Name = "Binance Coin", BasePrice = 350m, Volatility = 0.03m, TrendBias = 0.0001m },
|
|
new { Symbol = "SOL", Name = "Solana", BasePrice = 100m, Volatility = 0.035m, TrendBias = 0.0004m },
|
|
new { Symbol = "ADA", Name = "Cardano", BasePrice = 0.45m, Volatility = 0.028m, TrendBias = 0.0002m },
|
|
new { Symbol = "XRP", Name = "Ripple", BasePrice = 0.65m, Volatility = 0.032m, TrendBias = 0.0001m },
|
|
new { Symbol = "DOT", Name = "Polkadot", BasePrice = 6.5m, Volatility = 0.03m, TrendBias = 0.0003m },
|
|
new { Symbol = "AVAX", Name = "Avalanche", BasePrice = 35m, Volatility = 0.038m, TrendBias = 0.0005m },
|
|
new { Symbol = "MATIC", Name = "Polygon", BasePrice = 0.85m, Volatility = 0.033m, TrendBias = 0.0002m },
|
|
new { Symbol = "LINK", Name = "Chainlink", BasePrice = 15m, Volatility = 0.029m, TrendBias = 0.0003m },
|
|
new { Symbol = "UNI", Name = "Uniswap", BasePrice = 6.5m, Volatility = 0.031m, TrendBias = 0.0001m },
|
|
new { Symbol = "ATOM", Name = "Cosmos", BasePrice = 10m, Volatility = 0.03m, TrendBias = 0.0004m },
|
|
new { Symbol = "LTC", Name = "Litecoin", BasePrice = 75m, Volatility = 0.025m, TrendBias = 0.0001m },
|
|
new { Symbol = "ALGO", Name = "Algorand", BasePrice = 0.25m, Volatility = 0.032m, TrendBias = 0.0003m },
|
|
new { Symbol = "VET", Name = "VeChain", BasePrice = 0.03m, Volatility = 0.035m, TrendBias = 0.0002m }
|
|
};
|
|
|
|
foreach (var asset in assets)
|
|
{
|
|
_assets[asset.Symbol] = new SimulatedAsset
|
|
{
|
|
Symbol = asset.Symbol,
|
|
Name = asset.Name,
|
|
CurrentPrice = asset.BasePrice,
|
|
BasePrice = asset.BasePrice,
|
|
Volatility = asset.Volatility,
|
|
TrendBias = asset.TrendBias,
|
|
LastUpdate = DateTime.UtcNow
|
|
};
|
|
}
|
|
}
|
|
|
|
private void UpdatePrices(object? state)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
|
|
foreach (var asset in _assets.Values)
|
|
{
|
|
// Calculate time-based factors
|
|
var timeSinceStart = (now - asset.LastUpdate).TotalSeconds;
|
|
|
|
// Generate random walk with trend
|
|
var randomChange = (_random.NextDouble() - 0.5) * 2 * (double)asset.Volatility;
|
|
var trendComponent = (double)asset.TrendBias;
|
|
|
|
// Add market cycles (sine wave for realistic market behavior)
|
|
var cycleComponent = Math.Sin((double)asset.PriceUpdateCount / 100.0) * 0.001;
|
|
|
|
// Combine all factors
|
|
var totalChange = randomChange + trendComponent + cycleComponent;
|
|
|
|
// Update price
|
|
var newPrice = asset.CurrentPrice * (1 + (decimal)totalChange);
|
|
|
|
// Keep price within reasonable bounds (50% to 200% of base price)
|
|
newPrice = Math.Max(asset.BasePrice * 0.5m, Math.Min(asset.BasePrice * 2.0m, newPrice));
|
|
|
|
// Calculate change and volume
|
|
var priceChange = newPrice - asset.CurrentPrice;
|
|
var changePercentage = asset.CurrentPrice > 0 ? (priceChange / asset.CurrentPrice) * 100 : 0;
|
|
|
|
// Simulate volume based on volatility and price change
|
|
var baseVolume = asset.BasePrice * 1000000m;
|
|
var volumeVariation = (decimal)(_random.NextDouble() * 0.5 + 0.75); // 75% to 125%
|
|
var volumeFromVolatility = Math.Abs(changePercentage) * 100000m;
|
|
|
|
asset.CurrentPrice = newPrice;
|
|
asset.Change24h = changePercentage;
|
|
asset.Volume24h = (baseVolume + volumeFromVolatility) * volumeVariation;
|
|
asset.LastUpdate = now;
|
|
asset.PriceUpdateCount++;
|
|
|
|
// Add to history
|
|
asset.PriceHistory.Add(new MarketPrice
|
|
{
|
|
Symbol = asset.Symbol,
|
|
Price = newPrice,
|
|
Change24h = changePercentage,
|
|
Volume24h = asset.Volume24h,
|
|
Timestamp = now
|
|
});
|
|
|
|
// Keep history limited to last 500 points
|
|
if (asset.PriceHistory.Count > 500)
|
|
{
|
|
asset.PriceHistory.RemoveAt(0);
|
|
}
|
|
}
|
|
|
|
OnPriceUpdated?.Invoke();
|
|
}
|
|
}
|
|
|
|
public Task<List<MarketPrice>> GetMarketPricesAsync(List<string> symbols)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
var prices = new List<MarketPrice>();
|
|
|
|
foreach (var symbol in symbols)
|
|
{
|
|
if (_assets.TryGetValue(symbol, out var asset))
|
|
{
|
|
prices.Add(new MarketPrice
|
|
{
|
|
Symbol = asset.Symbol,
|
|
Price = asset.CurrentPrice,
|
|
Change24h = asset.Change24h,
|
|
Volume24h = asset.Volume24h,
|
|
Timestamp = asset.LastUpdate
|
|
});
|
|
}
|
|
}
|
|
|
|
return Task.FromResult(prices);
|
|
}
|
|
}
|
|
|
|
public Task<MarketPrice?> GetPriceAsync(string symbol)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_assets.TryGetValue(symbol, out var asset))
|
|
{
|
|
return Task.FromResult<MarketPrice?>(new MarketPrice
|
|
{
|
|
Symbol = asset.Symbol,
|
|
Price = asset.CurrentPrice,
|
|
Change24h = asset.Change24h,
|
|
Volume24h = asset.Volume24h,
|
|
Timestamp = asset.LastUpdate
|
|
});
|
|
}
|
|
|
|
return Task.FromResult<MarketPrice?>(null);
|
|
}
|
|
}
|
|
|
|
public List<MarketPrice> GetPriceHistory(string symbol, int count = 100)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_assets.TryGetValue(symbol, out var asset))
|
|
{
|
|
return asset.PriceHistory
|
|
.Skip(Math.Max(0, asset.PriceHistory.Count - count))
|
|
.ToList();
|
|
}
|
|
|
|
return new List<MarketPrice>();
|
|
}
|
|
}
|
|
|
|
public List<string> GetAvailableSymbols()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
return _assets.Keys.OrderBy(s => s).ToList();
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, string> GetAssetNames()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
return _assets.ToDictionary(a => a.Key, a => a.Value.Name);
|
|
}
|
|
}
|
|
|
|
private class SimulatedAsset
|
|
{
|
|
public string Symbol { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public decimal CurrentPrice { get; set; }
|
|
public decimal BasePrice { get; set; }
|
|
public decimal Change24h { get; set; }
|
|
public decimal Volume24h { get; set; }
|
|
public decimal Volatility { get; set; }
|
|
public decimal TrendBias { get; set; }
|
|
public DateTime LastUpdate { get; set; }
|
|
public int PriceUpdateCount { get; set; }
|
|
public List<MarketPrice> PriceHistory { get; set; } = new();
|
|
}
|
|
}
|