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