AssetManager.API/README.md
OpenClaw Agent 4ce29a1036 refactor: 架构优化 P0-P3
P0 - 安全修复:
- 移除硬编码 API Key,启动时校验必填环境变量

P1 - 高优先级:
- Entity 拆分:Position.cs, Transaction.cs 独立文件
- Controller Facade 封装:IPortfolioFacade 减少依赖注入

P2 - 中优先级:
- Repository 抽象:IPortfolioRepository, IMarketDataRepository
- MarketDataService 拆分:组合模式整合 Tencent/Tiingo/OKX

P3 - 低优先级:
- DTO 命名规范:统一 PascalCase
- 单元测试框架:xUnit + Moq + FluentAssertions
2026-03-15 12:54:05 +00:00

757 lines
18 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AssetManager Backend (个人资产策略管理系统)
基于 **.NET 8** + **MySQL** + **SqlSugar** 构建的高性能资产管理系统后端。采用 **Database First (数据库优先)** 模式开发,专注于量化策略管理与资产分析。
## 🛠 技术栈 (Tech Stack)
| 模块 | 技术选型 | 说明 |
| :--- | :--- | :--- |
| **核心框架** | .NET 8 Web API | 最新长期支持版,高性能 |
| **数据库** | MySQL 8.0+ | 稳定、开源的关系型数据库 |
| **ORM** | **SqlSugar** |哪怕是 .NET 老手也爱用的国产轻量级 ORM |
| **架构模式** | Repository Pattern | 仓储模式,解耦业务与数据访问 |
| **接口文档** | Swagger / Knife4j | 在线接口调试 |
| **测试框架** | xUnit + Moq + FluentAssertions | 单元测试与 Mock |
## 🏗️ 架构设计 (Architecture)
### 分层架构
```
┌─────────────────────────────────────────────────────────────┐
│ API Layer (Controllers) │
│ 路由、认证、参数校验、响应封装 │
├─────────────────────────────────────────────────────────────┤
│ Service Layer (Facade) │
│ IPortfolioFacade, IMarketDataService... │
│ 业务逻辑编排、事务管理、权限校验 │
├─────────────────────────────────────────────────────────────┤
│ Repository Layer (数据访问) │
│ IPortfolioRepository, IMarketDataRepository │
│ 数据持久化、查询封装、缓存管理 │
├─────────────────────────────────────────────────────────────┤
│ Infrastructure Layer (基础设施) │
│ TencentMarketService, TiingoMarketService, OkxMarketService
│ 外部API调用、数据源路由、降级策略 │
└─────────────────────────────────────────────────────────────┘
```
### 市场数据服务架构(组合模式)
```
MarketDataService
┌──────────────────┼──────────────────┐
▼ ▼ ▼
TencentMarketService TiingoMarketService OkxMarketService
(腾讯财经-优先) (Tiingo-降级) (OKX-加密货币)
│ │ │
└──────────────────┴──────────────────┘
IMarketDataRepository
(缓存层)
```
### 设计原则
1. **单一职责**:每个 Service/Repository 只负责一个业务领域
2. **依赖倒置**Service 依赖抽象接口,不依赖具体实现
3. **开闭原则**:新增数据源只需实现接口,无需修改现有代码
4. **降级策略**:腾讯财经 → Tiingo 自动降级,保证高可用
## 📂 项目结构 (Project Structure)
```text
AssetManager.API
├── AssetManager.API # [入口层] Controllers, Middleware, IOC配置
├── AssetManager.Services # [业务层] PortfolioService, StrategyService, Facade
├── AssetManager.Data # [数据层] Entities, Repositories, DatabaseService
├── AssetManager.Models # [模型层] DTOs, Enums, Constants
├── AssetManager.Infrastructure # [基础层] MarketDataService, ExchangeRateService
└── AssetManager.Tests # [测试层] 单元测试, Mock测试
```
### 核心文件说明
```
AssetManager.Data/Repositories/
├── IPortfolioRepository.cs # 组合仓储接口
├── IMarketDataRepository.cs # 市场数据仓储接口
├── PortfolioRepository.cs # 组合仓储实现
└── MarketDataRepository.cs # 市场数据仓储实现
AssetManager.Infrastructure/Services/
├── TencentMarketService.cs # 腾讯财经数据源(免费无限制)
├── TiingoMarketService.cs # Tiingo数据源美股
├── OkxMarketService.cs # OKX数据源加密货币
└── MarketDataService.cs # 组合模式整合
AssetManager.Services/
├── IPortfolioFacade.cs # 组合门面接口
├── PortfolioFacade.cs # 组合门面实现
├── IPortfolioService.cs # 组合服务接口
└── PortfolioService.cs # 组合服务实现
```
## 📊 策略引擎 (Strategy Engine)
### 功能介绍
策略引擎是系统的核心组件,负责根据配置的策略参数计算交易信号。支持以下三种策略类型:
1. **双均线策略 (ma_trend)** - 经典的趋势跟踪策略,通过短期均线和长期均线的交叉产生买卖信号
2. **吊灯止损策略 (chandelier_exit)** - 趋势跟踪止损策略,通过计算最高价/最低价和 ATR平均真实波幅来设置止损止盈位
3. **风险平价策略 (risk_parity)** - 资产配置策略,通过调整各资产权重使每个资产对组合的风险贡献相等
### 策略配置示例
#### 1. 双均线策略 (ma_trend)
```json
{
"maType": "SMA", // 均线类型SMA(简单移动平均) / EMA(指数移动平均)
"shortPeriod": 20, // 短期均线周期
"longPeriod": 60 // 长期均线周期
}
```
#### 2. 吊灯止损策略 (chandelier_exit)
```json
{
"period": 22, // 周期(通常为 22
"multiplier": 3.0, // ATR 倍数(通常为 3.0
"useClose": false // 是否使用收盘价计算false 表示用最高价/最低价)
}
```
#### 3. 风险平价策略 (risk_parity)
```json
{
"lookbackPeriod": 60, // 历史数据回看周期
"rebalanceThreshold": 0.05, // 再平衡阈值(偏离度超过 5% 触发再平衡)
"assets": [ // 目标资产列表(可选,不指定则使用当前持仓)
{ "symbol": "AAPL", "targetWeight": 0.6 },
{ "symbol": "BTC/USD", "targetWeight": 0.4 }
]
}
```
## 📡 API 示例
## 🎯 策略 API (Strategy API)
### 1. 获取策略列表
**请求 URL**: `GET /api/v1/strategy/strategies`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": [
{
"Id": "12345678-1234-1234-1234-1234567890ab",
"Title": "双均线策略",
"Type": "ma_trend",
"Description": "经典趋势跟踪策略",
"RiskLevel": "medium",
"Tags": ["趋势", "均线"],
"Parameters": {
"maType": "SMA",
"shortPeriod": 20,
"longPeriod": 60
},
"UserId": "user-123",
"CreatedAt": "2024-01-01T10:00:00",
"UpdatedAt": "2024-01-01T10:00:00"
},
{
"Id": "87654321-4321-4321-4321-210987654321",
"Title": "风险平价策略",
"Type": "risk_parity",
"Description": "资产配置策略",
"RiskLevel": "low",
"Tags": ["资产配置", "风险控制"],
"Parameters": {
"lookbackPeriod": 60,
"rebalanceThreshold": 0.05,
"assets": [
{ "symbol": "AAPL", "targetWeight": 0.6 },
{ "symbol": "BTC/USD", "targetWeight": 0.4 }
]
},
"UserId": "user-123",
"CreatedAt": "2024-01-02T10:00:00",
"UpdatedAt": "2024-01-02T10:00:00"
}
],
"message": "Success"
}
```
### 2. 获取单个策略详情
**请求 URL**: `GET /api/v1/strategy/{id}`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"Id": "12345678-1234-1234-1234-1234567890ab",
"Title": "双均线策略",
"Type": "ma_trend",
"Description": "经典趋势跟踪策略",
"RiskLevel": "medium",
"Tags": ["趋势", "均线"],
"Parameters": {
"maType": "SMA",
"shortPeriod": 20,
"longPeriod": 60
},
"UserId": "user-123",
"CreatedAt": "2024-01-01T10:00:00",
"UpdatedAt": "2024-01-01T10:00:00"
},
"message": "Success"
}
```
### 3. 创建策略
**请求 URL**: `POST /api/v1/strategy`
**请求头**:
```
Authorization: Bearer {token}
Content-Type: application/json
```
**请求体示例**:
#### 3.1 创建双均线策略
```json
{
"name": "双均线策略",
"type": "ma_trend",
"description": "经典趋势跟踪策略",
"riskLevel": "medium",
"tags": ["趋势", "均线"],
"parameters": {
"maType": "SMA",
"shortPeriod": 20,
"longPeriod": 60
}
}
```
#### 3.2 创建风险平价策略
```json
{
"name": "风险平价策略",
"type": "risk_parity",
"description": "资产配置策略",
"riskLevel": "low",
"tags": ["资产配置", "风险控制"],
"parameters": {
"lookbackPeriod": 60,
"rebalanceThreshold": 0.05,
"assets": [
{ "symbol": "AAPL", "targetWeight": 0.6 },
{ "symbol": "BTC/USD", "targetWeight": 0.4 }
]
}
}
```
#### 3.3 创建吊灯止损策略
```json
{
"name": "吊灯止损策略",
"type": "chandelier_exit",
"description": "趋势跟踪止损策略",
"riskLevel": "high",
"tags": ["止损", "趋势"],
"parameters": {
"period": 22,
"multiplier": 3.0,
"useClose": false
}
}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"Id": "12345678-1234-1234-1234-1234567890ab",
"Title": "双均线策略",
"Type": "ma_trend",
"Description": "经典趋势跟踪策略",
"RiskLevel": "medium",
"Tags": ["趋势", "均线"],
"Parameters": {
"maType": "SMA",
"shortPeriod": 20,
"longPeriod": 60
},
"UserId": "user-123",
"CreatedAt": "2024-01-01T10:00:00",
"UpdatedAt": "2024-01-01T10:00:00"
},
"message": "Strategy created successfully"
}
```
### 4. 更新策略
**请求 URL**: `PUT /api/v1/strategy/{id}`
**请求头**:
```
Authorization: Bearer {token}
Content-Type: application/json
```
**请求体示例**:
```json
{
"name": "双均线策略(更新)",
"type": "ma_trend",
"description": "经典趋势跟踪策略",
"riskLevel": "medium",
"tags": ["趋势", "均线"],
"parameters": {
"maType": "EMA",
"shortPeriod": 15,
"longPeriod": 50
}
}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"Id": "12345678-1234-1234-1234-1234567890ab",
"Title": "双均线策略(更新)",
"Type": "ma_trend",
"Description": "经典趋势跟踪策略",
"RiskLevel": "medium",
"Tags": ["趋势", "均线"],
"Parameters": {
"maType": "EMA",
"shortPeriod": 15,
"longPeriod": 50
},
"UserId": "user-123",
"CreatedAt": "2024-01-01T10:00:00",
"UpdatedAt": "2024-01-15T14:30:00"
},
"message": "Strategy updated successfully"
}
```
### 5. 删除策略
**请求 URL**: `DELETE /api/v1/strategy/{id}`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"id": "12345678-1234-1234-1234-1234567890ab",
"status": "deleted"
},
"message": "Strategy deleted successfully"
}
```
### 错误响应格式
所有策略接口在发生错误时都会返回统一的错误格式:
```json
{
"code": 401,
"data": null,
"message": "用户未授权"
}
```
**常见错误码**:
- `401`: 用户未授权
- `404`: 策略不存在
- `500`: 服务器内部错误
## 💼 投资组合 API (Portfolio API)
### 1. 创建投资组合
**请求 URL**: `POST /api/v1/portfolio`
**请求头**:
```
Authorization: Bearer {token}
Content-Type: application/json
```
**请求体示例**:
```json
{
"name": "我的投资组合",
"strategyId": "strategy-123",
"currency": "USD",
"stocks": [
{
"name": "Apple Inc.",
"code": "AAPL",
"price": 150.50,
"amount": 100,
"date": "2024-01-01",
"currency": "USD"
},
{
"name": "Microsoft Corp.",
"code": "MSFT",
"price": 300.25,
"amount": 50,
"date": "2024-01-01",
"currency": "USD"
}
]
}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"id": "port-abc12345",
"totalValue": 30075.00,
"returnRate": 0.0,
"currency": "USD",
"createdAt": "2024-01-01 10:30:00"
},
"message": "success"
}
```
### 2. 获取投资组合列表
**请求 URL**: `GET /api/v1/portfolio`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"items": [
{
"id": "port-abc12345",
"name": "我的投资组合",
"tags": "科技股",
"status": "运行中",
"statusType": "active",
"iconChar": "P",
"iconBgClass": "bg-blue-500",
"iconTextClass": "text-white",
"value": 30075.00,
"currency": "USD",
"returnRate": 0.15,
"returnType": "positive"
},
{
"id": "port-def67890",
"name": "保守组合",
"tags": "债券",
"status": "运行中",
"statusType": "active",
"iconChar": "C",
"iconBgClass": "bg-green-500",
"iconTextClass": "text-white",
"value": 50000.00,
"currency": "USD",
"returnRate": 0.08,
"returnType": "positive"
}
]
},
"message": "success"
}
```
### 3. 获取总资产情况
**请求 URL**: `GET /api/v1/portfolio/assets`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"totalValue": 80075.00,
"currency": "USD",
"todayProfit": 1250.50,
"todayProfitCurrency": "USD",
"totalReturnRate": 0.12
},
"message": "success"
}
```
### 4. 获取单个投资组合详情
**请求 URL**: `GET /api/v1/portfolio/{id}`
**请求头**:
```
Authorization: Bearer {token}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"id": "port-abc12345",
"name": "我的投资组合",
"currency": "CNY",
"status": "记录中",
"strategy": {
"id": "strategy-123",
"name": "HFEA 风险平价逻辑",
"description": "目标权重 季度调仓"
},
"portfolioValue": 156240.00,
"totalReturn": 0.4282,
"todayProfit": 1240.50,
"historicalChange": 42.82,
"dailyVolatility": 1240.50,
"todayProfitCurrency": "CNY",
"logicModel": "HFEA 风险平价逻辑",
"logicModelStatus": "监控中",
"logicModelDescription": "目标权重 季度调仓",
"totalItems": 2,
"totalRatio": 100.0,
"positions": [
{
"id": "pos-xyz12345",
"stockCode": "UPRO",
"stockName": "UPRO",
"symbol": "UPRO.US",
"amount": 142,
"averagePrice": 500.00,
"currentPrice": 605.00,
"totalValue": 85932.00,
"profit": 12400.00,
"profitRate": 0.248,
"changeAmount": 12400.00,
"ratio": 55.0,
"deviationRatio": 16.8,
"currency": "CNY"
},
{
"id": "pos-xyz67890",
"stockCode": "TMF",
"stockName": "TMF",
"symbol": "TMF.US",
"amount": 800,
"averagePrice": 90.00,
"currentPrice": 87.89,
"totalValue": 70308.00,
"profit": -3200.50,
"profitRate": -0.0445,
"changeAmount": -3200.50,
"ratio": 45.0,
"deviationRatio": -4.3,
"currency": "CNY"
}
]
},
"message": "success"
}
```
### 5. 获取交易记录
**请求 URL**: `GET /api/v1/portfolio/transactions`
**请求头**:
```
Authorization: Bearer {token}
```
**查询参数**:
- `portfolioId` (必填): 投资组合ID
- `limit` (可选): 每页记录数,默认 10
- `offset` (可选): 偏移量,默认 0
**请求示例**:
```
GET /api/v1/portfolio/transactions?portfolioId=port-abc12345&limit=10&offset=0
```
**响应示例**:
```json
{
"code": 200,
"data": {
"items": [
{
"id": "trans-abc12345",
"portfolioId": "port-abc12345",
"date": "2024-02-14",
"time": "14:30",
"type": "buy",
"title": "定期定投 UPRO录入增加",
"amount": 500.00,
"currency": "USD",
"status": "completed",
"remark": "定期定投"
},
{
"id": "trans-def67890",
"portfolioId": "port-abc12345",
"date": "2024-01-01",
"time": "09:15",
"type": "sell",
"title": "季度再平衡 TMF结出减少 200股",
"amount": 200.00,
"currency": "USD",
"status": "completed",
"remark": "季度再平衡"
},
{
"id": "trans-ghi78901",
"portfolioId": "port-abc12345",
"date": "2023-12-15",
"time": "10:00",
"type": "buy",
"title": "建仓买入录入增加",
"amount": 100000.00,
"currency": "CNY",
"status": "completed",
"remark": "建仓买入"
},
{
"id": "trans-jkl23456",
"portfolioId": "port-abc12345",
"date": "2023-12-10",
"time": "11:20",
"type": "buy",
"title": "建仓买入录入增加",
"amount": 50000.00,
"currency": "CNY",
"status": "completed",
"remark": "建仓买入"
}
],
"total": 4,
"page": 1,
"pageSize": 10
},
"message": "success"
}
```
### 6. 创建交易
**请求 URL**: `POST /api/v1/portfolio/transactions`
**请求头**:
```
Authorization: Bearer {token}
Content-Type: application/json
```
**请求体示例**:
```json
{
"portfolioId": "port-abc12345",
"type": "buy",
"stockCode": "AAPL",
"amount": 50,
"price": 155.00,
"currency": "USD",
"remark": "加仓"
}
```
**响应示例**:
```json
{
"code": 200,
"data": {
"id": "trans-xyz12345",
"totalAmount": 7750.00,
"status": "processing",
"createdAt": "2024-01-15 14:30:00"
},
"message": "success"
}
```
### 错误响应格式
所有接口在发生错误时都会返回统一的错误格式:
```json
{
"code": 401,
"data": null,
"message": "用户未授权"
}
```
**常见错误码**:
- `401`: 用户未授权
- `500`: 服务器内部错误