fix: 修复 PortfolioService 和 PortfolioNavService 变量命名
- 统一局部变量使用 camelCase(C# 规范) - 修复 DTO 属性名引用 - NavItem -> NavHistoryItem - 修复 for 循环变量名
This commit is contained in:
parent
da12f6d163
commit
60f3b487ff
@ -110,7 +110,19 @@ public class PortfolioFacade : IPortfolioFacade
|
|||||||
|
|
||||||
// 计算信号
|
// 计算信号
|
||||||
var positions = _databaseService.GetPositionsByPortfolioId(portfolioId);
|
var positions = _databaseService.GetPositionsByPortfolioId(portfolioId);
|
||||||
return await _strategyEngine.GenerateSignalAsync(strategy, positions);
|
var signal = await _strategyEngine.CalculateSignalAsync(strategy, positions);
|
||||||
|
|
||||||
|
return new StrategySignalResponse
|
||||||
|
{
|
||||||
|
Signal = signal.Signal?.ToLower() ?? "hold",
|
||||||
|
Reason = signal.Reason,
|
||||||
|
Actions = signal.PositionSignals?.Select(p => new SignalAction
|
||||||
|
{
|
||||||
|
Symbol = p.Symbol,
|
||||||
|
Action = p.Signal?.ToLower(),
|
||||||
|
Target = (double?)p.TargetWeight
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DeletePortfolioAsync(string portfolioId, string userId)
|
public async Task<bool> DeletePortfolioAsync(string portfolioId, string userId)
|
||||||
|
|||||||
@ -41,8 +41,8 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置默认日期范围(最近30天)
|
// 设置默认日期范围(最近30天)
|
||||||
var EndDate = request.EndDate ?? DateTime.Today;
|
var endDate = request.EndDate ?? DateTime.Today;
|
||||||
var StartDate = request.StartDate ?? endDate.AddDays(-30);
|
var startDate = request.StartDate ?? endDate.AddDays(-30);
|
||||||
|
|
||||||
// 检查是否有历史数据,没有则自动回填
|
// 检查是否有历史数据,没有则自动回填
|
||||||
var existingCount = await _db.Queryable<PortfolioNavHistory>()
|
var existingCount = await _db.Queryable<PortfolioNavHistory>()
|
||||||
@ -56,7 +56,7 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询净值历史
|
// 查询净值历史
|
||||||
var NavHistory = await _db.Queryable<PortfolioNavHistory>()
|
var navHistory = await _db.Queryable<PortfolioNavHistory>()
|
||||||
.Where(n => n.PortfolioId == portfolioId)
|
.Where(n => n.PortfolioId == portfolioId)
|
||||||
.Where(n => n.NavDate >= startDate && n.NavDate <= endDate)
|
.Where(n => n.NavDate >= startDate && n.NavDate <= endDate)
|
||||||
.OrderBy(n => n.NavDate)
|
.OrderBy(n => n.NavDate)
|
||||||
@ -69,20 +69,20 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
{
|
{
|
||||||
PortfolioId = portfolioId,
|
PortfolioId = portfolioId,
|
||||||
Currency = portfolio.Currency,
|
Currency = portfolio.Currency,
|
||||||
NavHistory = new List<NavItem>(),
|
NavHistory = new List<NavHistoryItem>(),
|
||||||
Statistics = new NavStatistics()
|
Statistics = new NavStatistics()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算统计指标
|
// 计算统计指标
|
||||||
var returns = navHistory.Select(n => (double)n.DailyReturn).Where(r => r != 0).ToList();
|
var returns = navHistory.Select(n => (double)n.DailyReturn).Where(r => r != 0).ToList();
|
||||||
var Statistics = CalculateStatistics(returns, navHistory);
|
var statistics = CalculateStatistics(returns, navHistory);
|
||||||
|
|
||||||
return new NavHistoryResponse
|
return new NavHistoryResponse
|
||||||
{
|
{
|
||||||
PortfolioId = portfolioId,
|
PortfolioId = portfolioId,
|
||||||
Currency = portfolio.Currency,
|
Currency = portfolio.Currency,
|
||||||
NavHistory = navHistory.Select(n => new NavItem
|
NavHistory = navHistory.Select(n => new NavHistoryItem
|
||||||
{
|
{
|
||||||
Date = n.NavDate.ToString("yyyy-MM-dd"),
|
Date = n.NavDate.ToString("yyyy-MM-dd"),
|
||||||
Nav = (double)n.Nav,
|
Nav = (double)n.Nav,
|
||||||
@ -128,7 +128,7 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
string targetCurrency = portfolio.Currency ?? "CNY";
|
string targetCurrency = portfolio.Currency ?? "CNY";
|
||||||
decimal TotalValue = 0;
|
decimal totalValue = 0;
|
||||||
|
|
||||||
// 计算总市值
|
// 计算总市值
|
||||||
foreach (var pos in positions)
|
foreach (var pos in positions)
|
||||||
@ -166,7 +166,7 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
.Where(t => t.PortfolioId == portfolioId && t.TransactionTime.Date <= today)
|
.Where(t => t.PortfolioId == portfolioId && t.TransactionTime.Date <= today)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
decimal TotalCost = 0;
|
decimal totalCost = 0;
|
||||||
foreach (var tx in transactions)
|
foreach (var tx in transactions)
|
||||||
{
|
{
|
||||||
if (tx.Type == "buy")
|
if (tx.Type == "buy")
|
||||||
@ -186,13 +186,13 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计算净值
|
// 计算净值
|
||||||
decimal Nav = totalCost > 0 ? totalValue / totalCost : 1.0m;
|
decimal nav = totalCost > 0 ? totalValue / totalCost : 1.0m;
|
||||||
decimal DailyReturn = 0;
|
decimal dailyReturn = 0;
|
||||||
decimal CumulativeReturn = totalCost > 0 ? (totalValue - totalCost) / totalCost * 100 : 0;
|
decimal cumulativeReturn = totalCost > 0 ? (totalValue - totalCost) / totalCost * 100 : 0;
|
||||||
|
|
||||||
if (yesterdayNav != null && yesterdayNav.TotalValue > 0)
|
if (yesterdayNav != null && yesterdayNav.TotalValue > 0)
|
||||||
{
|
{
|
||||||
DailyReturn = (totalValue - yesterdayNav.TotalValue) / yesterdayNav.TotalValue * 100;
|
dailyReturn = (totalValue - yesterdayNav.TotalValue) / yesterdayNav.TotalValue * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存净值记录
|
// 保存净值记录
|
||||||
@ -280,8 +280,8 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确定起始日期(最早交易日期)
|
// 确定起始日期(最早交易日期)
|
||||||
var StartDate = transactions.Min(t => t.TransactionTime).Date;
|
var startDate = transactions.Min(t => t.TransactionTime).Date;
|
||||||
var EndDate = DateTime.Today;
|
var endDate = DateTime.Today;
|
||||||
string targetCurrency = portfolio.Currency ?? "CNY";
|
string targetCurrency = portfolio.Currency ?? "CNY";
|
||||||
|
|
||||||
_logger.LogInformation("开始回填净值历史: {PortfolioId}, 日期范围: {StartDate} ~ {EndDate}",
|
_logger.LogInformation("开始回填净值历史: {PortfolioId}, 日期范围: {StartDate} ~ {EndDate}",
|
||||||
@ -302,10 +302,10 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
decimal cumulativeCost = 0;
|
decimal cumulativeCost = 0;
|
||||||
|
|
||||||
// 记录创建数量
|
// 记录创建数量
|
||||||
int RecordsCreated = 0;
|
int recordsCreated = 0;
|
||||||
|
|
||||||
// 遍历每个交易日
|
// 遍历每个交易日
|
||||||
for (var Date = startDate; date <= endDate; Date = date.AddDays(1))
|
for (var date = startDate; date <= endDate; date = date.AddDays(1))
|
||||||
{
|
{
|
||||||
// 检查是否已存在该日期的净值记录(非强制模式)
|
// 检查是否已存在该日期的净值记录(非强制模式)
|
||||||
if (!force)
|
if (!force)
|
||||||
@ -372,7 +372,7 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
if (!holdings.Any()) continue;
|
if (!holdings.Any()) continue;
|
||||||
|
|
||||||
// 计算当日市值
|
// 计算当日市值
|
||||||
decimal TotalValue = 0;
|
decimal totalValue = 0;
|
||||||
bool hasValidPrice = true;
|
bool hasValidPrice = true;
|
||||||
List<string> failedSymbols = new List<string>();
|
List<string> failedSymbols = new List<string>();
|
||||||
|
|
||||||
@ -402,8 +402,8 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计算净值
|
// 计算净值
|
||||||
decimal Nav = cumulativeCost > 0 ? totalValue / cumulativeCost : 1.0m;
|
decimal nav = cumulativeCost > 0 ? totalValue / cumulativeCost : 1.0m;
|
||||||
decimal CumulativeReturn = cumulativeCost > 0 ? (totalValue - cumulativeCost) / cumulativeCost * 100 : 0;
|
decimal cumulativeReturn = cumulativeCost > 0 ? (totalValue - cumulativeCost) / cumulativeCost * 100 : 0;
|
||||||
|
|
||||||
// 获取昨日净值以计算日收益率
|
// 获取昨日净值以计算日收益率
|
||||||
var yesterdayNav = await _db.Queryable<PortfolioNavHistory>()
|
var yesterdayNav = await _db.Queryable<PortfolioNavHistory>()
|
||||||
@ -411,10 +411,10 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
.OrderByDescending(n => n.NavDate)
|
.OrderByDescending(n => n.NavDate)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
decimal DailyReturn = 0;
|
decimal dailyReturn = 0;
|
||||||
if (yesterdayNav != null && yesterdayNav.TotalValue > 0)
|
if (yesterdayNav != null && yesterdayNav.TotalValue > 0)
|
||||||
{
|
{
|
||||||
DailyReturn = (totalValue - yesterdayNav.TotalValue) / yesterdayNav.TotalValue * 100;
|
dailyReturn = (totalValue - yesterdayNav.TotalValue) / yesterdayNav.TotalValue * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存净值记录
|
// 保存净值记录
|
||||||
@ -536,33 +536,33 @@ public class PortfolioNavService : IPortfolioNavService
|
|||||||
{
|
{
|
||||||
if (!history.Any()) return new NavStatistics();
|
if (!history.Any()) return new NavStatistics();
|
||||||
|
|
||||||
var MaxReturn = returns.Any() ? returns.Max() : 0;
|
var maxReturn = returns.Any() ? returns.Max() : 0;
|
||||||
var MinReturn = returns.Any() ? returns.Min() : 0;
|
var minReturn = returns.Any() ? returns.Min() : 0;
|
||||||
|
|
||||||
// 计算最大回撤
|
// 计算最大回撤
|
||||||
double MaxDrawdown = 0;
|
double maxDrawdown = 0;
|
||||||
double peak = 1.0;
|
double peak = 1.0;
|
||||||
foreach (var item in history.OrderBy(h => h.NavDate))
|
foreach (var item in history.OrderBy(h => h.NavDate))
|
||||||
{
|
{
|
||||||
var Nav = (double)item.Nav;
|
var nav = (double)item.Nav;
|
||||||
if (nav > peak) peak = nav;
|
if (nav > peak) peak = nav;
|
||||||
var drawdown = (peak - nav) / peak * 100;
|
var drawdown = (peak - nav) / peak * 100;
|
||||||
if (drawdown > maxDrawdown) MaxDrawdown = drawdown;
|
if (drawdown > maxDrawdown) maxDrawdown = drawdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算夏普比率(简化版,假设无风险利率=3%年化)
|
// 计算夏普比率(简化版,假设无风险利率=3%年化)
|
||||||
double SharpeRatio = 0;
|
double sharpeRatio = 0;
|
||||||
double Volatility = 0;
|
double volatility = 0;
|
||||||
if (returns.Any())
|
if (returns.Any())
|
||||||
{
|
{
|
||||||
var avgReturn = returns.Average();
|
var avgReturn = returns.Average();
|
||||||
var stdDev = Math.Sqrt(returns.Sum(r => Math.Pow(r - avgReturn, 2)) / returns.Count);
|
var stdDev = Math.Sqrt(returns.Sum(r => Math.Pow(r - avgReturn, 2)) / returns.Count);
|
||||||
Volatility = stdDev * Math.Sqrt(252); // 年化波动率
|
volatility = stdDev * Math.Sqrt(252); // 年化波动率
|
||||||
SharpeRatio = stdDev > 0 ? (avgReturn * 252 - 3) / (stdDev * Math.Sqrt(252)) : 0;
|
sharpeRatio = stdDev > 0 ? (avgReturn * 252 - 3) / (stdDev * Math.Sqrt(252)) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 总收益率
|
// 总收益率
|
||||||
var TotalReturn = history.Any()
|
var totalReturn = history.Any()
|
||||||
? (double)(history.Last().CumulativeReturn)
|
? (double)(history.Last().CumulativeReturn)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
|||||||
@ -159,7 +159,7 @@ public class PortfolioService : IPortfolioService
|
|||||||
return new CreatePortfolioResponse
|
return new CreatePortfolioResponse
|
||||||
{
|
{
|
||||||
Id = portfolio.Id,
|
Id = portfolio.Id,
|
||||||
totalValue = (double)portfolio.TotalValue,
|
TotalValue = (double)portfolio.TotalValue,
|
||||||
ReturnRate = 0,
|
ReturnRate = 0,
|
||||||
Currency = portfolio.Currency,
|
Currency = portfolio.Currency,
|
||||||
CreatedAt = portfolio.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
|
CreatedAt = portfolio.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
@ -266,11 +266,11 @@ public class PortfolioService : IPortfolioService
|
|||||||
|
|
||||||
return new TotalAssetsResponse
|
return new TotalAssetsResponse
|
||||||
{
|
{
|
||||||
totalValue = (double)totalValueInTargetCurrency,
|
TotalValue = (double)totalValueInTargetCurrency,
|
||||||
Currency = targetCurrency,
|
Currency = targetCurrency,
|
||||||
TodayProfit = (double)totalTodayProfitInTargetCurrency,
|
TodayProfit = (double)totalTodayProfitInTargetCurrency,
|
||||||
TodayProfitCurrency = targetCurrency,
|
TodayProfitCurrency = targetCurrency,
|
||||||
totalReturnRate = Math.Round(totalReturnRate, 2)
|
TotalReturnRate = Math.Round(totalReturnRate, 2)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +350,7 @@ public class PortfolioService : IPortfolioService
|
|||||||
Amount = (int)pos.Shares,
|
Amount = (int)pos.Shares,
|
||||||
AveragePrice = (double)pos.AvgPrice,
|
AveragePrice = (double)pos.AvgPrice,
|
||||||
CurrentPrice = (double)currentPrice,
|
CurrentPrice = (double)currentPrice,
|
||||||
totalValue = (double)positionValueInTarget,
|
TotalValue = (double)positionValueInTarget,
|
||||||
Profit = (double)(positionValueInTarget - costInTarget),
|
Profit = (double)(positionValueInTarget - costInTarget),
|
||||||
ProfitRate = profitRate,
|
ProfitRate = profitRate,
|
||||||
ChangeAmount = (double)todayProfitInTarget,
|
ChangeAmount = (double)todayProfitInTarget,
|
||||||
@ -379,7 +379,7 @@ public class PortfolioService : IPortfolioService
|
|||||||
{
|
{
|
||||||
Id = portfolio.StrategyId,
|
Id = portfolio.StrategyId,
|
||||||
Name = "策略名称",
|
Name = "策略名称",
|
||||||
description = "策略描述"
|
Description = "策略描述"
|
||||||
},
|
},
|
||||||
PortfolioValue = (double)totalPortfolioValue,
|
PortfolioValue = (double)totalPortfolioValue,
|
||||||
TotalReturn = (double)totalReturn,
|
TotalReturn = (double)totalReturn,
|
||||||
@ -427,7 +427,7 @@ public class PortfolioService : IPortfolioService
|
|||||||
|
|
||||||
return new GetTransactionsResponse
|
return new GetTransactionsResponse
|
||||||
{
|
{
|
||||||
items = transactions.Select(t => new TransactionItem
|
Items = transactions.Select(t => new TransactionItem
|
||||||
{
|
{
|
||||||
Id = t.Id,
|
Id = t.Id,
|
||||||
PortfolioId = t.PortfolioId,
|
PortfolioId = t.PortfolioId,
|
||||||
@ -441,9 +441,9 @@ public class PortfolioService : IPortfolioService
|
|||||||
Status = t.Status,
|
Status = t.Status,
|
||||||
Remark = t.Remark
|
Remark = t.Remark
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
total = total,
|
Total = total,
|
||||||
page = offset / limit + 1,
|
Page = offset / limit + 1,
|
||||||
pageSize = limit
|
PageSize = limit
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user