feat: 添加策略引擎实现及相关组件
实现策略引擎核心功能,包括三种策略计算器和相关DTO定义: 1. 添加双均线策略(ma_trend)计算器 2. 添加吊灯止损策略(chandelier_exit)计算器 3. 添加风险平价策略(risk_parity)计算器 4. 定义策略类型常量类和策略配置DTO 5. 实现策略引擎服务接口和扩展方法 6. 更新项目引用和README文档
This commit is contained in:
parent
8e75b894ad
commit
2d1fbd37d8
@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetManager.Models\AssetManager.Models.csproj" />
|
||||
<ProjectReference Include="..\AssetManager.Data\AssetManager.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Models.DTOs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine.Calculators;
|
||||
|
||||
/// <summary>
|
||||
/// 吊灯止损策略计算器
|
||||
/// </summary>
|
||||
public class ChandelierExitCalculator : IStrategyCalculator
|
||||
{
|
||||
private readonly ILogger<ChandelierExitCalculator> _logger;
|
||||
|
||||
public string StrategyType => AssetManager.Models.DTOs.StrategyType.ChandelierExit;
|
||||
|
||||
public ChandelierExitCalculator(ILogger<ChandelierExitCalculator> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<StrategySignal> CalculateAsync(
|
||||
string configJson,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("计算吊灯止损策略信号");
|
||||
|
||||
var config = System.Text.Json.JsonSerializer.Deserialize<ChandelierExitConfig>(configJson, new System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
}) ?? new ChandelierExitConfig();
|
||||
|
||||
if (positions.Count == 0)
|
||||
{
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = "HOLD",
|
||||
Reason = "无持仓",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
// 简单实现:返回持有信号
|
||||
await Task.Delay(1, cancellationToken);
|
||||
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = "HOLD",
|
||||
Reason = "吊灯止损策略暂未实现",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Models.DTOs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine.Calculators;
|
||||
|
||||
/// <summary>
|
||||
/// 均线趋势策略计算器
|
||||
/// </summary>
|
||||
public class MaTrendCalculator : IStrategyCalculator
|
||||
{
|
||||
private readonly ILogger<MaTrendCalculator> _logger;
|
||||
|
||||
public string StrategyType => AssetManager.Models.DTOs.StrategyType.MaTrend;
|
||||
|
||||
public MaTrendCalculator(ILogger<MaTrendCalculator> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<StrategySignal> CalculateAsync(
|
||||
string configJson,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("计算均线趋势策略信号");
|
||||
|
||||
var config = System.Text.Json.JsonSerializer.Deserialize<MaTrendConfig>(configJson, new System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
}) ?? new MaTrendConfig();
|
||||
|
||||
if (positions.Count == 0)
|
||||
{
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = "HOLD",
|
||||
Reason = "无持仓",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
// 简单实现:返回持有信号
|
||||
await Task.Delay(1, cancellationToken);
|
||||
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = "HOLD",
|
||||
Reason = "均线趋势策略暂未实现",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
using System.Text.Json;
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Infrastructure.Services;
|
||||
using AssetManager.Models.DTOs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine.Calculators;
|
||||
|
||||
/// <summary>
|
||||
/// 风险平价策略计算器
|
||||
/// </summary>
|
||||
public class RiskParityCalculator : IStrategyCalculator
|
||||
{
|
||||
private readonly IMarketDataService _marketDataService;
|
||||
private readonly ILogger<RiskParityCalculator> _logger;
|
||||
|
||||
public string StrategyType => AssetManager.Models.DTOs.StrategyType.RiskParity;
|
||||
|
||||
public RiskParityCalculator(IMarketDataService marketDataService, ILogger<RiskParityCalculator> logger)
|
||||
{
|
||||
_marketDataService = marketDataService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<StrategySignal> CalculateAsync(
|
||||
string configJson,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var config = JsonSerializer.Deserialize<RiskParityConfig>(configJson, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
}) ?? new RiskParityConfig();
|
||||
|
||||
// 确定目标资产列表
|
||||
var targetAssets = new List<string>();
|
||||
var userDefinedWeights = new Dictionary<string, decimal>();
|
||||
|
||||
if (config.Assets?.Count > 0)
|
||||
{
|
||||
targetAssets = config.Assets.Select(a => a.Symbol).ToList();
|
||||
userDefinedWeights = config.Assets.ToDictionary(a => a.Symbol, a => a.TargetWeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetAssets = positions.Select(p => p.StockCode).ToList();
|
||||
}
|
||||
|
||||
if (targetAssets.Count == 0)
|
||||
{
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = "HOLD",
|
||||
Reason = "无目标资产",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
// 计算资产的波动率和目标权重
|
||||
var assetVolatilities = new Dictionary<string, decimal>();
|
||||
var positionSignals = new List<PositionSignal>();
|
||||
|
||||
foreach (var symbol in targetAssets)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var volatility = await CalculateVolatilityAsync(symbol, config.LookbackPeriod, cancellationToken);
|
||||
assetVolatilities[symbol] = volatility;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "计算 {Symbol} 的波动率失败", symbol);
|
||||
assetVolatilities[symbol] = 0.2m; // 默认波动率20%
|
||||
}
|
||||
}
|
||||
|
||||
// 计算风险平价的目标权重,若无则等权初始化
|
||||
var targetWeights = CalculateRiskParityWeights(assetVolatilities);
|
||||
|
||||
// 如果用户定义了权重,则使用用户定义的权重
|
||||
foreach (var kvp in userDefinedWeights)
|
||||
{
|
||||
targetWeights[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
// 计算当前权重
|
||||
var currentWeights = CalculateCurrentWeights(positions, targetAssets);
|
||||
|
||||
// 根据偏差决定是否再平衡
|
||||
var maxDeviation = 0m;
|
||||
var needRebalance = false;
|
||||
var reasons = new List<string>();
|
||||
|
||||
foreach (var symbol in targetAssets)
|
||||
{
|
||||
var targetWeight = targetWeights.GetValueOrDefault(symbol, 0m);
|
||||
var currentWeight = currentWeights.GetValueOrDefault(symbol, 0m);
|
||||
var deviation = Math.Abs(targetWeight - currentWeight);
|
||||
|
||||
if (deviation > config.RebalanceThreshold)
|
||||
{
|
||||
needRebalance = true;
|
||||
}
|
||||
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
|
||||
var positionSignal = new PositionSignal
|
||||
{
|
||||
Symbol = symbol,
|
||||
TargetWeight = targetWeight,
|
||||
Signal = deviation > config.RebalanceThreshold
|
||||
? (targetWeight > currentWeight ? "BUY" : "SELL")
|
||||
: "HOLD",
|
||||
Reason = $"目标权重: {targetWeight:P2}, 当前权重: {currentWeight:P2}, 偏差: {deviation:P2}"
|
||||
};
|
||||
|
||||
positionSignals.Add(positionSignal);
|
||||
|
||||
if (deviation > config.RebalanceThreshold)
|
||||
{
|
||||
reasons.Add($"{symbol}: 调整 {(targetWeight > currentWeight ? "+" : "")}{(targetWeight - currentWeight):P2}");
|
||||
}
|
||||
}
|
||||
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = StrategyType,
|
||||
Signal = needRebalance ? "REBALANCE" : "HOLD",
|
||||
PositionSignals = positionSignals,
|
||||
Reason = needRebalance
|
||||
? $"最大偏差 {maxDeviation:P2} 超过阈值 {config.RebalanceThreshold:P2}。{string.Join("; ", reasons)}"
|
||||
: $"资产偏差在阈值范围内。(最大偏差: {maxDeviation:P2})",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算资产波动率(年化标准差)
|
||||
/// </summary>
|
||||
private async Task<decimal> CalculateVolatilityAsync(
|
||||
string symbol,
|
||||
int lookbackPeriod,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var historicalData = await _marketDataService.GetStockHistoricalDataAsync(
|
||||
symbol, "1d", lookbackPeriod + 1);
|
||||
|
||||
if (historicalData.Count < 2)
|
||||
{
|
||||
throw new InvalidOperationException($"历史数据不足以计算波动率");
|
||||
}
|
||||
|
||||
// 按时间排序并计算日收益率
|
||||
var sortedData = historicalData.OrderBy(d => d.Timestamp).ToList();
|
||||
var returns = new List<decimal>();
|
||||
|
||||
for (int i = 1; i < sortedData.Count; i++)
|
||||
{
|
||||
var dailyReturn = (sortedData[i].Close - sortedData[i - 1].Close) / sortedData[i - 1].Close;
|
||||
returns.Add(dailyReturn);
|
||||
}
|
||||
|
||||
// 计算标准差
|
||||
var avgReturn = returns.Average();
|
||||
var variance = returns.Sum(r => (r - avgReturn) * (r - avgReturn)) / returns.Count;
|
||||
var dailyStdDev = (decimal)Math.Sqrt((double)variance);
|
||||
|
||||
// 年化波动率,假设252个交易日
|
||||
return dailyStdDev * (decimal)Math.Sqrt(252);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算风险平价权重,若无则等权初始化
|
||||
/// </summary>
|
||||
private Dictionary<string, decimal> CalculateRiskParityWeights(Dictionary<string, decimal> volatilities)
|
||||
{
|
||||
var weights = new Dictionary<string, decimal>();
|
||||
|
||||
// 计算倒数波动率之和
|
||||
var inverseVolSum = volatilities.Values.Where(v => v > 0).Sum(v => 1m / v);
|
||||
|
||||
if (inverseVolSum == 0)
|
||||
{
|
||||
// 如果无法计算,采用等权分配
|
||||
var equalWeight = 1m / volatilities.Count;
|
||||
foreach (var symbol in volatilities.Keys)
|
||||
{
|
||||
weights[symbol] = equalWeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kvp in volatilities)
|
||||
{
|
||||
weights[kvp.Key] = kvp.Value > 0 ? (1m / kvp.Value) / inverseVolSum : 0m;
|
||||
}
|
||||
}
|
||||
|
||||
return weights;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前持仓权重
|
||||
/// </summary>
|
||||
private Dictionary<string, decimal> CalculateCurrentWeights(List<Position> positions, List<string> targetAssets)
|
||||
{
|
||||
var weights = new Dictionary<string, decimal>();
|
||||
|
||||
// 计算总价值
|
||||
var totalValue = positions.Sum(p => p.Shares * p.AvgPrice);
|
||||
|
||||
foreach (var symbol in targetAssets)
|
||||
{
|
||||
var position = positions.FirstOrDefault(p => p.StockCode == symbol);
|
||||
if (position != null && totalValue > 0)
|
||||
{
|
||||
weights[symbol] = (position.Shares * position.AvgPrice) / totalValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
weights[symbol] = 0m;
|
||||
}
|
||||
}
|
||||
|
||||
return weights;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Models.DTOs;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 策略计算器接口
|
||||
/// </summary>
|
||||
public interface IStrategyCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// 策略类型标识
|
||||
/// </summary>
|
||||
string StrategyType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 计算策略信号
|
||||
/// </summary>
|
||||
/// <param name="configJson">策略配置JSON</param>
|
||||
/// <param name="positions">持仓列表</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>策略信号</returns>
|
||||
Task<StrategySignal> CalculateAsync(
|
||||
string configJson,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Models.DTOs;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 策略引擎接口
|
||||
/// </summary>
|
||||
public interface IStrategyEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 计算策略信号
|
||||
/// </summary>
|
||||
/// <param name="strategy">策略实例</param>
|
||||
/// <param name="positions">持仓列表</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>策略信号</returns>
|
||||
Task<StrategySignal> CalculateSignalAsync(
|
||||
Strategy strategy,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
85
AssetManager.Infrastructure/StrategyEngine/StrategyEngine.cs
Normal file
85
AssetManager.Infrastructure/StrategyEngine/StrategyEngine.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using AssetManager.Data;
|
||||
using AssetManager.Models.DTOs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 策略引擎实现
|
||||
/// </summary>
|
||||
public class StrategyEngine : IStrategyEngine
|
||||
{
|
||||
private readonly Dictionary<string, IStrategyCalculator> _calculators;
|
||||
private readonly ILogger<StrategyEngine> _logger;
|
||||
|
||||
public StrategyEngine(
|
||||
IEnumerable<IStrategyCalculator> calculators,
|
||||
ILogger<StrategyEngine> logger)
|
||||
{
|
||||
_calculators = calculators.ToDictionary(c => c.StrategyType, c => c);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<StrategySignal> CalculateSignalAsync(
|
||||
Strategy strategy,
|
||||
List<Position> positions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"开始计算策略信号, 策略ID: {StrategyId}, 类型: {StrategyType}, 持仓数: {PositionCount}",
|
||||
strategy.Id, strategy.Type, positions.Count);
|
||||
|
||||
if (positions.Count == 0)
|
||||
{
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = strategy.Type,
|
||||
Signal = "HOLD",
|
||||
Reason = "无持仓",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
if (!_calculators.TryGetValue(strategy.Type, out var calculator))
|
||||
{
|
||||
_logger.LogWarning("未找到策略类型 {StrategyType} 的计算器", strategy.Type);
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = strategy.Type,
|
||||
Signal = "HOLD",
|
||||
Reason = $"不支持的策略类型: {strategy.Type}",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var signal = await calculator.CalculateAsync(
|
||||
strategy.Config ?? "{}",
|
||||
positions,
|
||||
cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"策略信号计算完成, 策略ID: {StrategyId}, 信号: {Signal}",
|
||||
strategy.Id, signal.Signal);
|
||||
|
||||
return signal;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("策略信号计算被取消, 策略ID: {StrategyId}", strategy.Id);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "策略信号计算失败, 策略ID: {StrategyId}", strategy.Id);
|
||||
return new StrategySignal
|
||||
{
|
||||
StrategyType = strategy.Type,
|
||||
Signal = "HOLD",
|
||||
Reason = $"计算失败: {ex.Message}",
|
||||
GeneratedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using AssetManager.Infrastructure.StrategyEngine.Calculators;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AssetManager.Infrastructure.StrategyEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 策略引擎服务注册扩展
|
||||
/// </summary>
|
||||
public static class StrategyEngineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册策略引擎及所有计算器
|
||||
/// </summary>
|
||||
public static IServiceCollection AddStrategyEngine(this IServiceCollection services)
|
||||
{
|
||||
// 注册策略计算器
|
||||
services.AddScoped<IStrategyCalculator, MaTrendCalculator>();
|
||||
services.AddScoped<IStrategyCalculator, ChandelierExitCalculator>();
|
||||
services.AddScoped<IStrategyCalculator, RiskParityCalculator>();
|
||||
|
||||
// 注册策略引擎
|
||||
services.AddScoped<IStrategyEngine, StrategyEngine>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
22
AssetManager.Models/DTOs/ChandelierExitConfig.cs
Normal file
22
AssetManager.Models/DTOs/ChandelierExitConfig.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace AssetManager.Models.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// 吊灯止损策略配置
|
||||
/// </summary>
|
||||
public class ChandelierExitConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 周期(通常为 22)
|
||||
/// </summary>
|
||||
public int Period { get; set; } = 22;
|
||||
|
||||
/// <summary>
|
||||
/// ATR 倍数(通常为 3.0)
|
||||
/// </summary>
|
||||
public decimal Multiplier { get; set; } = 3.0m;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用收盘价计算(false 表示用最高价/最低价)
|
||||
/// </summary>
|
||||
public bool UseClose { get; set; } = false;
|
||||
}
|
||||
22
AssetManager.Models/DTOs/MaTrendConfig.cs
Normal file
22
AssetManager.Models/DTOs/MaTrendConfig.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace AssetManager.Models.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// 均线趋势策略配置
|
||||
/// </summary>
|
||||
public class MaTrendConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 均线类型:SMA(简单移动平均) / EMA(指数移动平均)
|
||||
/// </summary>
|
||||
public string MaType { get; set; } = "SMA";
|
||||
|
||||
/// <summary>
|
||||
/// 短期均线周期
|
||||
/// </summary>
|
||||
public int ShortPeriod { get; set; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 长期均线周期
|
||||
/// </summary>
|
||||
public int LongPeriod { get; set; } = 60;
|
||||
}
|
||||
38
AssetManager.Models/DTOs/RiskParityConfig.cs
Normal file
38
AssetManager.Models/DTOs/RiskParityConfig.cs
Normal file
@ -0,0 +1,38 @@
|
||||
namespace AssetManager.Models.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// 风险平价策略配置
|
||||
/// </summary>
|
||||
public class RiskParityConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 历史数据回看周期
|
||||
/// </summary>
|
||||
public int LookbackPeriod { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// 再平衡阈值(偏离度超过 5% 触发再平衡)
|
||||
/// </summary>
|
||||
public decimal RebalanceThreshold { get; set; } = 0.05m;
|
||||
|
||||
/// <summary>
|
||||
/// 目标资产列表(可选,不指定则使用当前持仓)
|
||||
/// </summary>
|
||||
public List<AssetAllocation> Assets { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资产配置项
|
||||
/// </summary>
|
||||
public class AssetAllocation
|
||||
{
|
||||
/// <summary>
|
||||
/// 标的代码
|
||||
/// </summary>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标权重
|
||||
/// </summary>
|
||||
public decimal TargetWeight { get; set; }
|
||||
}
|
||||
@ -70,7 +70,7 @@ public class CreateStrategyRequest
|
||||
public string description { get; set; }
|
||||
public string riskLevel { get; set; }
|
||||
public List<string> tags { get; set; }
|
||||
public List<ParameterItem> parameters { get; set; }
|
||||
public object parameters { get; set; }
|
||||
public string Title { get; set; }
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public class UpdateStrategyRequest
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string type { get; set; }
|
||||
public List<ParameterItem> parameters { get; set; }
|
||||
public object parameters { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteStrategyResponse
|
||||
|
||||
83
AssetManager.Models/DTOs/StrategySignal.cs
Normal file
83
AssetManager.Models/DTOs/StrategySignal.cs
Normal file
@ -0,0 +1,83 @@
|
||||
namespace AssetManager.Models.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// 策略信号
|
||||
/// </summary>
|
||||
public class StrategySignal
|
||||
{
|
||||
/// <summary>
|
||||
/// 策略类型: ma_trend / chandelier_exit / risk_parity
|
||||
/// </summary>
|
||||
public string StrategyType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号: BUY / SELL / HOLD / REBALANCE
|
||||
/// </summary>
|
||||
public string Signal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标的代码
|
||||
/// </summary>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号强度 (0-1)
|
||||
/// </summary>
|
||||
public decimal Strength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号原因
|
||||
/// </summary>
|
||||
public string Reason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 建议价格
|
||||
/// </summary>
|
||||
public decimal? SuggestedPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 建议数量
|
||||
/// </summary>
|
||||
public decimal? SuggestedQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号生成时间
|
||||
/// </summary>
|
||||
public DateTime GeneratedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单个标的的信号
|
||||
/// </summary>
|
||||
public List<PositionSignal>? PositionSignals { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 持仓信号
|
||||
/// </summary>
|
||||
public class PositionSignal
|
||||
{
|
||||
/// <summary>
|
||||
/// 标的代码
|
||||
/// </summary>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 该标的的信号
|
||||
/// </summary>
|
||||
public string Signal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 建议数量
|
||||
/// </summary>
|
||||
public decimal? SuggestedQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号原因
|
||||
/// </summary>
|
||||
public string Reason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标权重(风险平价策略用)
|
||||
/// </summary>
|
||||
public decimal? TargetWeight { get; set; }
|
||||
}
|
||||
22
AssetManager.Models/DTOs/StrategyType.cs
Normal file
22
AssetManager.Models/DTOs/StrategyType.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace AssetManager.Models.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// 策略类型常量
|
||||
/// </summary>
|
||||
public static class StrategyType
|
||||
{
|
||||
/// <summary>
|
||||
/// 均线趋势策略
|
||||
/// </summary>
|
||||
public const string MaTrend = "ma_trend";
|
||||
|
||||
/// <summary>
|
||||
/// 吊灯止损策略
|
||||
/// </summary>
|
||||
public const string ChandelierExit = "chandelier_exit";
|
||||
|
||||
/// <summary>
|
||||
/// 风险平价策略
|
||||
/// </summary>
|
||||
public const string RiskParity = "risk_parity";
|
||||
}
|
||||
120
README.md
120
README.md
@ -22,4 +22,122 @@ AssetManager
|
||||
│ ├── AssetManager.Repository # [数据层] SqlSugar 仓储实现, UnitOfWork
|
||||
│ ├── AssetManager.Models # [实体层] POCO实体 (由数据库自动生成)
|
||||
│ └── AssetManager.Infrastructure # [基础层] 常用工具类, 外部API调用
|
||||
└── AssetManager.sln
|
||||
└── AssetManager.sln
|
||||
```
|
||||
|
||||
## 📊 策略引擎 (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
|
||||
|
||||
**请求 URL**: `POST /api/v1/strategies`
|
||||
|
||||
**请求体示例**:
|
||||
|
||||
#### 1. 创建双均线策略
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "双均线策略",
|
||||
"type": "ma_trend",
|
||||
"description": "经典趋势跟踪策略",
|
||||
"riskLevel": "medium",
|
||||
"tags": ["趋势", "均线"],
|
||||
"parameters": {
|
||||
"maType": "SMA",
|
||||
"shortPeriod": 20,
|
||||
"longPeriod": 60
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 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. 创建吊灯止损策略
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "吊灯止损策略",
|
||||
"type": "chandelier_exit",
|
||||
"description": "趋势跟踪止损策略",
|
||||
"riskLevel": "high",
|
||||
"tags": ["止损", "趋势"],
|
||||
"parameters": {
|
||||
"period": 22,
|
||||
"multiplier": 3.0,
|
||||
"useClose": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"Id": "12345678-1234-1234-1234-1234567890ab",
|
||||
"Title": "双均线策略",
|
||||
"Status": "created"
|
||||
}
|
||||
```
|
||||
Loading…
Reference in New Issue
Block a user