5 changed files with 568 additions and 7 deletions
@ -0,0 +1,50 @@
|
||||
|
||||
# 添加风险管理配置 |
||||
risk_management: |
||||
max_position_size: 0.1 # 最大仓位比例 |
||||
max_drawdown: 0.1 # 最大回撤 |
||||
stop_loss: 0.05 # 止损比例 |
||||
take_profit: 0.1 # 止盈比例 |
||||
max_daily_trades: 50 # 单日最大交易次数 |
||||
max_daily_loss: 0.05 # 每日最大亏损比例 |
||||
max_leverage: 3.0 # 最大杠杆倍数 |
||||
min_volatility: 0.001 # 最小波动率阈值 |
||||
max_volatility: 0.05 # 最大波动率阈值 |
||||
correlation_threshold: 0.7 # 相关性阈值 |
||||
max_concentration: 0.3 # 最大集中度 |
||||
min_liquidity: 1000000 # 最小流动性要求(USDT) |
||||
max_slippage: 0.002 # 最大滑点容忍度 |
||||
circuit_breaker: |
||||
price_change: 0.1 # 价格剧烈变化阈值 |
||||
time_window: 300 # 监控时间窗口(秒) |
||||
cool_down: 600 # 冷却时间(秒) |
||||
# 交易频率控制 |
||||
min_trade_interval: 60 # 最小交易间隔(秒) |
||||
max_trades_per_hour: 10 # 每小时最大交易次数 |
||||
# 价格偏离度控制 |
||||
max_price_deviation: 0.05 # 与指数价格最大偏离度 |
||||
reference_exchanges: ["binance", "huobi", "ftx"] # 参考交易所 |
||||
# 资金利用率控制 |
||||
max_margin_usage: 0.8 # 最大保证金使用率 |
||||
min_free_margin: 1000 # 最小剩余保证金(USDT) |
||||
# 订单簿深度控制 |
||||
min_depth_ratio: 0.5 # 最小深度比率 |
||||
min_bid_ask_size: 10000 # 最小买卖盘大小(USDT) |
||||
# 价格趋势控制 |
||||
trend_window: 24 # 趋势判断窗口(小时) |
||||
max_trend_deviation: 0.1 # 最大趋势偏离度 |
||||
# 波动率分解 |
||||
volatility_control: |
||||
historical_window: 30 # 历史波动率窗口(天) |
||||
implied_weight: 0.6 # 隐含波动率权重 |
||||
realized_weight: 0.4 # 实现波动率权重 |
||||
# 相关性风险控制 |
||||
correlation_control: |
||||
min_pairs: 3 # 最小对冲币对数量 |
||||
max_correlation: 0.7 # 最大相关性系数 |
||||
lookback_period: 30 # 回溯期(天) |
||||
# 流动性压力测试 |
||||
liquidity_stress: |
||||
confidence_level: 0.95 # 置信水平 |
||||
stress_period: 7 # 压力测试期(天) |
||||
max_liquidation: 0.2 # 最大清算比例 |
||||
@ -0,0 +1,494 @@
|
||||
package risk |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"fmt" |
||||
"math" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
type Manager struct { |
||||
mu sync.RWMutex |
||||
maxPositionSize float64 |
||||
maxDrawdown float64 |
||||
stopLoss float64 |
||||
takeProfit float64 |
||||
maxDailyTrades int |
||||
maxDailyLoss float64 |
||||
maxLeverage float64 |
||||
minVolatility float64 |
||||
maxVolatility float64 |
||||
correlationThreshold float64 |
||||
maxConcentration float64 |
||||
minLiquidity float64 |
||||
maxSlippage float64 |
||||
circuitBreaker CircuitBreaker |
||||
|
||||
positions map[string]Position |
||||
dailyStats DailyStats |
||||
volatilityWindow []float64 |
||||
priceHistory map[string][]PricePoint |
||||
|
||||
minTradeInterval time.Duration |
||||
maxTradesPerHour int |
||||
maxPriceDeviation float64 |
||||
referenceExchanges []string |
||||
maxMarginUsage float64 |
||||
minFreeMargin float64 |
||||
minDepthRatio float64 |
||||
minBidAskSize float64 |
||||
trendWindow time.Duration |
||||
maxTrendDeviation float64 |
||||
|
||||
volatilityControl VolatilityControl |
||||
correlationControl CorrelationControl |
||||
liquidityStress LiquidityStress |
||||
|
||||
lastTradeTime time.Time |
||||
hourlyTradeCount int |
||||
hourlyTradeReset time.Time |
||||
} |
||||
|
||||
type CircuitBreaker struct { |
||||
PriceChangeThreshold float64 |
||||
TimeWindow time.Duration |
||||
CoolDown time.Duration |
||||
LastTriggered time.Time |
||||
IsTriggered bool |
||||
} |
||||
|
||||
type DailyStats struct { |
||||
Date time.Time |
||||
TradeCount int |
||||
TotalPnL float64 |
||||
HighestPrice float64 |
||||
LowestPrice float64 |
||||
} |
||||
|
||||
type PricePoint struct { |
||||
Price float64 |
||||
Volume float64 |
||||
Timestamp time.Time |
||||
} |
||||
|
||||
type Position struct { |
||||
Symbol string |
||||
EntryPrice float64 |
||||
Amount float64 |
||||
Leverage float64 |
||||
UnrealizedPnL float64 |
||||
OpenTime time.Time |
||||
} |
||||
|
||||
type VolatilityControl struct { |
||||
HistoricalWindow int |
||||
ImpliedWeight float64 |
||||
RealizedWeight float64 |
||||
HistoricalVol float64 |
||||
ImpliedVol float64 |
||||
} |
||||
|
||||
type CorrelationControl struct { |
||||
MinPairs int |
||||
MaxCorrelation float64 |
||||
LookbackPeriod int |
||||
PairCorrelations map[string]map[string]float64 |
||||
} |
||||
|
||||
type LiquidityStress struct { |
||||
ConfidenceLevel float64 |
||||
StressPeriod int |
||||
MaxLiquidation float64 |
||||
StressScenarios []StressScenario |
||||
} |
||||
|
||||
type StressScenario struct { |
||||
PriceChange float64 |
||||
VolumeChange float64 |
||||
SpreadChange float64 |
||||
Probability float64 |
||||
} |
||||
|
||||
func NewManager(config map[string]float64) *Manager { |
||||
return &Manager{ |
||||
maxPositionSize: config["max_position_size"], |
||||
maxDrawdown: config["max_drawdown"], |
||||
stopLoss: config["stop_loss"], |
||||
takeProfit: config["take_profit"], |
||||
positions: make(map[string]Position), |
||||
} |
||||
} |
||||
|
||||
func (m *Manager) CheckAndUpdatePosition(ctx context.Context, order exchange.Order) error { |
||||
m.mu.Lock() |
||||
defer m.mu.Unlock() |
||||
|
||||
// Check position size
|
||||
if order.Amount > m.maxPositionSize*m.currentBalance { |
||||
return errors.New("order exceeds maximum position size") |
||||
} |
||||
|
||||
// Check drawdown
|
||||
if m.currentBalance < m.initialBalance*(1-m.maxDrawdown) { |
||||
return errors.New("maximum drawdown reached") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) UpdatePositionPrice(symbol string, currentPrice float64) (bool, error) { |
||||
m.mu.Lock() |
||||
defer m.mu.Unlock() |
||||
|
||||
pos, exists := m.positions[symbol] |
||||
if !exists { |
||||
return false, nil |
||||
} |
||||
|
||||
// Calculate unrealized PnL
|
||||
pnlPercent := (currentPrice - pos.EntryPrice) / pos.EntryPrice |
||||
|
||||
// Check stop loss
|
||||
if pnlPercent <= -m.stopLoss { |
||||
return true, nil // Should close position
|
||||
} |
||||
|
||||
// Check take profit
|
||||
if pnlPercent >= m.takeProfit { |
||||
return true, nil // Should close position
|
||||
} |
||||
|
||||
return false, nil |
||||
} |
||||
|
||||
func (m *Manager) CheckRisk(ctx context.Context, order exchange.Order, marketData exchange.MarketData) error { |
||||
m.mu.Lock() |
||||
defer m.mu.Unlock() |
||||
|
||||
// 1. 检查每日交易次数限制
|
||||
if m.dailyStats.TradeCount >= m.maxDailyTrades { |
||||
return errors.New("daily trade limit exceeded") |
||||
} |
||||
|
||||
// 2. 检查每日亏损限制
|
||||
if m.dailyStats.TotalPnL <= -m.maxDailyLoss*m.initialBalance { |
||||
return errors.New("daily loss limit reached") |
||||
} |
||||
|
||||
// 3. 检查杠杆率
|
||||
if order.Leverage > m.maxLeverage { |
||||
return errors.New("leverage exceeds maximum allowed") |
||||
} |
||||
|
||||
// 4. 检查波动率
|
||||
volatility := m.calculateVolatility() |
||||
if volatility < m.minVolatility { |
||||
return errors.New("market volatility too low") |
||||
} |
||||
if volatility > m.maxVolatility { |
||||
return errors.New("market volatility too high") |
||||
} |
||||
|
||||
// 5. 检查流动性
|
||||
if !m.checkLiquidity(marketData) { |
||||
return errors.New("insufficient market liquidity") |
||||
} |
||||
|
||||
// 6. 检查集中度
|
||||
if !m.checkConcentration(order) { |
||||
return errors.New("position concentration too high") |
||||
} |
||||
|
||||
// 7. 检查熔断机制
|
||||
if m.checkCircuitBreaker(marketData) { |
||||
return errors.New("circuit breaker triggered") |
||||
} |
||||
|
||||
// 8. 检查滑点
|
||||
if !m.checkSlippage(order, marketData) { |
||||
return errors.New("expected slippage too high") |
||||
} |
||||
|
||||
// 交易频率检查
|
||||
if err := m.checkTradeFrequency(); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 价格偏离度检查
|
||||
if err := m.checkPriceDeviation(marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 保证金使用率检查
|
||||
if err := m.checkMarginUsage(order); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 订单簿深度检查
|
||||
if err := m.checkOrderBookDepth(marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 趋势偏离检查
|
||||
if err := m.checkTrendDeviation(marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 综合波动率检查
|
||||
if err := m.checkCompositeVolatility(marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 相关性风险检查
|
||||
if err := m.checkCorrelationRisk(marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// 流动性压力测试
|
||||
if err := m.checkLiquidityStress(order, marketData); err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) calculateVolatility() float64 { |
||||
if len(m.volatilityWindow) < 2 { |
||||
return 0 |
||||
} |
||||
|
||||
// 计算对数收益率
|
||||
returns := make([]float64, len(m.volatilityWindow)-1) |
||||
for i := 1; i < len(m.volatilityWindow); i++ { |
||||
returns[i-1] = math.Log(m.volatilityWindow[i] / m.volatilityWindow[i-1]) |
||||
} |
||||
|
||||
// 计算标准差
|
||||
mean := 0.0 |
||||
for _, r := range returns { |
||||
mean += r |
||||
} |
||||
mean /= float64(len(returns)) |
||||
|
||||
variance := 0.0 |
||||
for _, r := range returns { |
||||
variance += math.Pow(r-mean, 2) |
||||
} |
||||
variance /= float64(len(returns)) |
||||
|
||||
return math.Sqrt(variance) |
||||
} |
||||
|
||||
func (m *Manager) checkLiquidity(data exchange.MarketData) bool { |
||||
// 检查24小时成交量是否满足最小流动性要求
|
||||
return data.Volume*data.Price >= m.minLiquidity |
||||
} |
||||
|
||||
func (m *Manager) checkConcentration(order exchange.Order) bool { |
||||
totalPositionValue := 0.0 |
||||
for _, pos := range m.positions { |
||||
totalPositionValue += pos.Amount * pos.EntryPrice |
||||
} |
||||
|
||||
// 添加新订单的价值
|
||||
newPositionValue := order.Amount * order.Price |
||||
totalPositionValue += newPositionValue |
||||
|
||||
// 检查单个仓位是否超过总仓位的最大集中度
|
||||
for _, pos := range m.positions { |
||||
positionValue := pos.Amount * pos.EntryPrice |
||||
if positionValue/totalPositionValue > m.maxConcentration { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
func (m *Manager) checkCircuitBreaker(data exchange.MarketData) bool { |
||||
if m.circuitBreaker.IsTriggered { |
||||
if time.Since(m.circuitBreaker.LastTriggered) > m.circuitBreaker.CoolDown { |
||||
m.circuitBreaker.IsTriggered = false |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
// 检查价格变化
|
||||
priceHistory := m.priceHistory[data.Symbol] |
||||
if len(priceHistory) == 0 { |
||||
return false |
||||
} |
||||
|
||||
timeWindow := time.Now().Add(-m.circuitBreaker.TimeWindow) |
||||
var oldPrice float64 |
||||
for i := len(priceHistory) - 1; i >= 0; i-- { |
||||
if priceHistory[i].Timestamp.Before(timeWindow) { |
||||
oldPrice = priceHistory[i].Price |
||||
break |
||||
} |
||||
} |
||||
|
||||
if oldPrice > 0 { |
||||
priceChange := math.Abs(data.Price-oldPrice) / oldPrice |
||||
if priceChange > m.circuitBreaker.PriceChangeThreshold { |
||||
m.circuitBreaker.IsTriggered = true |
||||
m.circuitBreaker.LastTriggered = time.Now() |
||||
return true |
||||
} |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
func (m *Manager) checkSlippage(order exchange.Order, data exchange.MarketData) bool { |
||||
expectedSlippage := math.Abs(order.Price-data.Price) / data.Price |
||||
return expectedSlippage <= m.maxSlippage |
||||
} |
||||
|
||||
func (m *Manager) UpdateDailyStats(pnl float64) { |
||||
today := time.Now().UTC().Truncate(24 * time.Hour) |
||||
if m.dailyStats.Date != today { |
||||
// Reset daily stats
|
||||
m.dailyStats = DailyStats{ |
||||
Date: today, |
||||
TradeCount: 0, |
||||
TotalPnL: 0, |
||||
HighestPrice: 0, |
||||
LowestPrice: math.MaxFloat64, |
||||
} |
||||
} |
||||
|
||||
m.dailyStats.TradeCount++ |
||||
m.dailyStats.TotalPnL += pnl |
||||
} |
||||
|
||||
func (m *Manager) UpdatePriceHistory(data exchange.MarketData) { |
||||
if m.priceHistory == nil { |
||||
m.priceHistory = make(map[string][]PricePoint) |
||||
} |
||||
|
||||
pricePoint := PricePoint{ |
||||
Price: data.Price, |
||||
Volume: data.Volume, |
||||
Timestamp: data.Timestamp, |
||||
} |
||||
|
||||
// 保持价格历史在合理范围内
|
||||
history := m.priceHistory[data.Symbol] |
||||
if len(history) > 1000 { |
||||
history = history[1:] |
||||
} |
||||
history = append(history, pricePoint) |
||||
m.priceHistory[data.Symbol] = history |
||||
|
||||
// 更新波动率窗口
|
||||
if len(m.volatilityWindow) > 100 { |
||||
m.volatilityWindow = m.volatilityWindow[1:] |
||||
} |
||||
m.volatilityWindow = append(m.volatilityWindow, data.Price) |
||||
} |
||||
|
||||
func (m *Manager) checkTradeFrequency() error { |
||||
now := time.Now() |
||||
|
||||
// 检查最小交易间隔
|
||||
if now.Sub(m.lastTradeTime) < m.minTradeInterval { |
||||
return errors.New("trade frequency too high") |
||||
} |
||||
|
||||
// 检查每小时交易次数
|
||||
if now.Sub(m.hourlyTradeReset) >= time.Hour { |
||||
m.hourlyTradeCount = 0 |
||||
m.hourlyTradeReset = now |
||||
} |
||||
if m.hourlyTradeCount >= m.maxTradesPerHour { |
||||
return errors.New("hourly trade limit exceeded") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) checkPriceDeviation(data exchange.MarketData) error { |
||||
// 获取参考价格
|
||||
var prices []float64 |
||||
for _, ex := range m.referenceExchanges { |
||||
price, err := m.getPriceFromExchange(ex, data.Symbol) |
||||
if err != nil { |
||||
continue |
||||
} |
||||
prices = append(prices, price) |
||||
} |
||||
|
||||
if len(prices) == 0 { |
||||
return nil // 无法获取参考价格时暂时跳过检查
|
||||
} |
||||
|
||||
// 计算平均参考价格
|
||||
avgPrice := 0.0 |
||||
for _, p := range prices { |
||||
avgPrice += p |
||||
} |
||||
avgPrice /= float64(len(prices)) |
||||
|
||||
// 检查价格偏离度
|
||||
deviation := math.Abs(data.Price-avgPrice) / avgPrice |
||||
if deviation > m.maxPriceDeviation { |
||||
return fmt.Errorf("price deviation %.2f%% exceeds limit", deviation*100) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) checkCompositeVolatility(data exchange.MarketData) error { |
||||
// 计算历史波动率
|
||||
historicalVol := m.calculateHistoricalVolatility() |
||||
|
||||
// 获取期权隐含波动率(如果可用)
|
||||
impliedVol := m.getImpliedVolatility(data.Symbol) |
||||
|
||||
// 计算综合波动率
|
||||
compositeVol := historicalVol*m.volatilityControl.RealizedWeight + |
||||
impliedVol*m.volatilityControl.ImpliedWeight |
||||
|
||||
if compositeVol > m.maxVolatility { |
||||
return fmt.Errorf("composite volatility %.2f%% too high", compositeVol*100) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) checkCorrelationRisk(data exchange.MarketData) error { |
||||
// 更新相关性矩阵
|
||||
m.updateCorrelationMatrix(data) |
||||
|
||||
// 检查是否有足够的对冲币对
|
||||
hedgePairs := m.findHedgePairs(data.Symbol) |
||||
if len(hedgePairs) < m.correlationControl.MinPairs { |
||||
return errors.New("insufficient hedge pairs") |
||||
} |
||||
|
||||
// 检查相关性是否在允许范围内
|
||||
for _, pair := range hedgePairs { |
||||
if corr := m.getCorrelation(data.Symbol, pair); corr > m.correlationControl.MaxCorrelation { |
||||
return fmt.Errorf("correlation with %s too high: %.2f", pair, corr) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (m *Manager) checkLiquidityStress(order exchange.Order, data exchange.MarketData) error { |
||||
// 运行压力测试场景
|
||||
for _, scenario := range m.liquidityStress.StressScenarios { |
||||
// 计算在压力情况下的清算风险
|
||||
liquidationRisk := m.calculateLiquidationRisk(order, data, scenario) |
||||
if liquidationRisk > m.liquidityStress.MaxLiquidation { |
||||
return fmt.Errorf("stress test liquidation risk %.2f%% too high", liquidationRisk*100) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
Loading…
Reference in new issue