You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
494 lines
12 KiB
494 lines
12 KiB
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 |
|
}
|
|
|