- GetNavHistoryAsync现在会自动检查是否有历史数据 - 无历史数据时自动调用BackfillNavHistoryInternalAsync - 拆分内部回填方法,避免重复验证权限
172 lines
6.5 KiB
C#
Executable File
172 lines
6.5 KiB
C#
Executable File
using AssetManager.Models.DTOs;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AssetManager.Infrastructure.Services;
|
|
|
|
/// <summary>
|
|
/// Mock 市场数据服务(用于开发测试)
|
|
/// </summary>
|
|
public class MockMarketDataService : IMarketDataService
|
|
{
|
|
private readonly ILogger<MockMarketDataService> _logger;
|
|
|
|
public MockMarketDataService(ILogger<MockMarketDataService> logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<MarketPriceResponse> GetStockPriceAsync(string symbol)
|
|
{
|
|
_logger.LogInformation("Mock 获取股票价格: {Symbol}", symbol);
|
|
|
|
// 先拿历史数据,取最后一根 K 线收盘价作为 PreviousClose
|
|
var historicalData = await GetStockHistoricalDataAsync(symbol, "1d", 2);
|
|
decimal previousClose = historicalData.Count >= 1 ? historicalData[^1].Close : 0;
|
|
|
|
// Mock 价格:基于标的代码生成一个稳定的价格
|
|
decimal basePrice = symbol.GetHashCode() % 1000 + 50;
|
|
return new MarketPriceResponse
|
|
{
|
|
Symbol = symbol,
|
|
Price = basePrice,
|
|
PreviousClose = previousClose,
|
|
Timestamp = DateTime.UtcNow,
|
|
AssetType = "Stock"
|
|
};
|
|
}
|
|
|
|
public async Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol)
|
|
{
|
|
_logger.LogInformation("Mock 获取加密货币价格: {Symbol}", symbol);
|
|
|
|
// 先拿历史数据,取最后一根 K 线收盘价作为 PreviousClose
|
|
var historicalData = await GetCryptoHistoricalDataAsync(symbol, "1d", 2);
|
|
decimal previousClose = historicalData.Count >= 1 ? historicalData[^1].Close : 0;
|
|
|
|
decimal basePrice = symbol.GetHashCode() % 50000 + 10000;
|
|
return new MarketPriceResponse
|
|
{
|
|
Symbol = symbol,
|
|
Price = basePrice,
|
|
PreviousClose = previousClose,
|
|
Timestamp = DateTime.UtcNow,
|
|
AssetType = "Crypto"
|
|
};
|
|
}
|
|
|
|
public Task<List<MarketDataResponse>> GetStockHistoricalDataAsync(string symbol, string timeframe, int limit)
|
|
{
|
|
_logger.LogInformation("Mock 获取股票历史数据: {Symbol}, {Timeframe}, {Limit}", symbol, timeframe, limit);
|
|
return Task.FromResult(GenerateMockData(symbol, "Stock", timeframe, limit));
|
|
}
|
|
|
|
public Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit)
|
|
{
|
|
_logger.LogInformation("Mock 获取加密货币历史数据: {Symbol}, {Timeframe}, {Limit}", symbol, timeframe, limit);
|
|
return Task.FromResult(GenerateMockData(symbol, "Crypto", timeframe, limit));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成 Mock K 线数据(模拟一个上升趋势 + 随机波动)
|
|
/// </summary>
|
|
private List<MarketDataResponse> GenerateMockData(string symbol, string assetType, string timeframe, int limit)
|
|
{
|
|
var data = new List<MarketDataResponse>();
|
|
var random = new Random(symbol.GetHashCode()); // 基于 symbol 的稳定随机数
|
|
|
|
// 基础价格:基于 symbol 生成
|
|
decimal basePrice = symbol.GetHashCode() % 500 + 100;
|
|
DateTime currentTime = DateTime.UtcNow;
|
|
|
|
// 根据 timeframe 转换为时间增量
|
|
TimeSpan increment = timeframe.ToLower() switch
|
|
{
|
|
"1min" => TimeSpan.FromMinutes(1),
|
|
"5min" => TimeSpan.FromMinutes(5),
|
|
"15min" => TimeSpan.FromMinutes(15),
|
|
"1h" => TimeSpan.FromHours(1),
|
|
"1d" => TimeSpan.FromDays(1),
|
|
"1w" => TimeSpan.FromDays(7),
|
|
"1m" => TimeSpan.FromDays(30),
|
|
_ => TimeSpan.FromDays(1)
|
|
};
|
|
|
|
// 倒序生成(从过去到现在)
|
|
for (int i = limit - 1; i >= 0; i--)
|
|
{
|
|
DateTime timestamp = currentTime - increment * i;
|
|
|
|
// 模拟上升趋势 + 随机波动
|
|
decimal trend = (limit - i) * 0.1m; // 上升趋势
|
|
decimal noise = (decimal)(random.NextDouble() - 0.5) * 2; // ±1 的随机波动
|
|
decimal close = basePrice + trend + noise;
|
|
|
|
// 生成 OHLC
|
|
decimal open = close + (decimal)(random.NextDouble() - 0.5) * 1;
|
|
decimal high = Math.Max(open, close) + (decimal)random.NextDouble() * 0.5m;
|
|
decimal low = Math.Min(open, close) - (decimal)random.NextDouble() * 0.5m;
|
|
decimal volume = (decimal)(random.NextDouble() * 1000000 + 100000);
|
|
|
|
data.Add(new MarketDataResponse
|
|
{
|
|
Symbol = symbol,
|
|
Timestamp = timestamp,
|
|
Open = open,
|
|
High = high,
|
|
Low = low,
|
|
Close = close,
|
|
Volume = volume,
|
|
AssetType = assetType
|
|
});
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// 新增到 MockMarketDataService 类中
|
|
public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType)
|
|
{
|
|
_logger.LogInformation("Mock 获取价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType);
|
|
// Mock 一个随机价格
|
|
var random = new Random();
|
|
return await Task.FromResult(new MarketPriceResponse
|
|
{
|
|
Symbol = symbol,
|
|
Price = (decimal)(random.NextDouble() * 100 + 50),
|
|
PreviousClose = (decimal)(random.NextDouble() * 100 + 45),
|
|
Timestamp = DateTime.UtcNow,
|
|
AssetType = assetType
|
|
});
|
|
}
|
|
|
|
public async Task<List<MarketDataResponse>> GetHistoricalDataAsync(string symbol, string assetType, string timeframe, int limit)
|
|
{
|
|
_logger.LogInformation("Mock 获取历史数据: {Symbol}, 资产类型: {AssetType}, 周期: {Timeframe}, 数量: {Limit}",
|
|
symbol, assetType, timeframe, limit);
|
|
// Mock 历史数据
|
|
var result = new List<MarketDataResponse>();
|
|
var random = new Random();
|
|
var basePrice = (decimal)(random.NextDouble() * 100 + 50);
|
|
|
|
for (int i = 0; i < limit; i++)
|
|
{
|
|
var change = (decimal)(random.NextDouble() * 10 - 5);
|
|
basePrice += change;
|
|
result.Add(new MarketDataResponse
|
|
{
|
|
Symbol = symbol,
|
|
Open = basePrice - (decimal)random.NextDouble() * 2,
|
|
High = basePrice + (decimal)random.NextDouble() * 2,
|
|
Low = basePrice - (decimal)random.NextDouble() * 2,
|
|
Close = basePrice,
|
|
Volume = (decimal)random.NextDouble() * 1000000,
|
|
Timestamp = DateTime.UtcNow.AddDays(-(limit - i)),
|
|
AssetType = assetType
|
|
});
|
|
}
|
|
|
|
return await Task.FromResult(result);
|
|
}
|
|
|
|
}
|