mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 00:07:01 +08:00
新增内置AI评分
修改market/data.go Get函数获取K线为流式获取(可以解决传入币种比较多的情况下耗时问题) market目录下新增文件 main.go 新增运行入口 通过inside_coins=true控制 该评分默认初始化大约需要2分钟左右(因为币种列表比较多,api有限速) 使用时应该注意engine.go下的流动性过滤问题
This commit is contained in:
229
market/feature_engine.go
Normal file
229
market/feature_engine.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package market
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FeatureEngine struct {
|
||||
alertThresholds AlertThresholds
|
||||
}
|
||||
|
||||
func NewFeatureEngine(thresholds AlertThresholds) *FeatureEngine {
|
||||
return &FeatureEngine{
|
||||
alertThresholds: thresholds,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) CalculateFeatures(symbol string, klines []Kline) *SymbolFeatures {
|
||||
if len(klines) < 20 {
|
||||
return nil
|
||||
}
|
||||
|
||||
features := &SymbolFeatures{
|
||||
Symbol: symbol,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
// 提取价格和交易量数据
|
||||
closes := make([]float64, len(klines))
|
||||
volumes := make([]float64, len(klines))
|
||||
highs := make([]float64, len(klines))
|
||||
lows := make([]float64, len(klines))
|
||||
|
||||
for i, k := range klines {
|
||||
closes[i] = k.Close
|
||||
volumes[i] = k.Volume
|
||||
highs[i] = k.High
|
||||
lows[i] = k.Low
|
||||
}
|
||||
|
||||
// 价格特征
|
||||
features.Price = closes[len(closes)-1]
|
||||
features.PriceChange15Min = (closes[len(closes)-1] - closes[len(closes)-2]) / closes[len(closes)-2]
|
||||
|
||||
if len(closes) >= 5 {
|
||||
features.PriceChange1H = (closes[len(closes)-1] - closes[len(closes)-5]) / closes[len(closes)-5]
|
||||
}
|
||||
if len(closes) >= 17 {
|
||||
features.PriceChange4H = (closes[len(closes)-1] - closes[len(closes)-17]) / closes[len(closes)-17]
|
||||
}
|
||||
|
||||
// 交易量特征
|
||||
currentVolume := volumes[len(volumes)-1]
|
||||
features.Volume = currentVolume
|
||||
|
||||
// 5周期平均交易量
|
||||
if len(volumes) >= 6 {
|
||||
avgVolume5 := e.calculateAverage(volumes[len(volumes)-6 : len(volumes)-1])
|
||||
features.VolumeRatio5 = currentVolume / avgVolume5
|
||||
}
|
||||
|
||||
// 20周期平均交易量
|
||||
if len(volumes) >= 21 {
|
||||
avgVolume20 := e.calculateAverage(volumes[len(volumes)-21 : len(volumes)-1])
|
||||
features.VolumeRatio20 = currentVolume / avgVolume20
|
||||
}
|
||||
|
||||
// 交易量趋势
|
||||
if features.VolumeRatio20 > 0 {
|
||||
features.VolumeTrend = features.VolumeRatio5 / features.VolumeRatio20
|
||||
}
|
||||
|
||||
// 技术指标
|
||||
features.RSI14 = e.calculateRSI(closes, 14)
|
||||
features.SMA5 = e.calculateSMA(closes, 5)
|
||||
features.SMA10 = e.calculateSMA(closes, 10)
|
||||
features.SMA20 = e.calculateSMA(closes, 20)
|
||||
|
||||
// 波动特征
|
||||
currentHigh := highs[len(highs)-1]
|
||||
currentLow := lows[len(lows)-1]
|
||||
features.HighLowRatio = (currentHigh - currentLow) / features.Price
|
||||
features.Volatility20 = e.calculateVolatility(closes, 20)
|
||||
|
||||
// 价格在区间中的位置
|
||||
if currentHigh != currentLow {
|
||||
features.PositionInRange = (features.Price - currentLow) / (currentHigh - currentLow)
|
||||
} else {
|
||||
features.PositionInRange = 0.5
|
||||
}
|
||||
|
||||
return features
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) calculateAverage(values []float64) float64 {
|
||||
sum := 0.0
|
||||
for _, v := range values {
|
||||
sum += v
|
||||
}
|
||||
return sum / float64(len(values))
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) calculateSMA(prices []float64, period int) float64 {
|
||||
if len(prices) < period {
|
||||
return 0
|
||||
}
|
||||
return e.calculateAverage(prices[len(prices)-period:])
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) calculateRSI(prices []float64, period int) float64 {
|
||||
if len(prices) <= period {
|
||||
return 50
|
||||
}
|
||||
|
||||
gains := make([]float64, 0)
|
||||
losses := make([]float64, 0)
|
||||
|
||||
for i := 1; i < len(prices); i++ {
|
||||
change := prices[i] - prices[i-1]
|
||||
if change > 0 {
|
||||
gains = append(gains, change)
|
||||
losses = append(losses, 0)
|
||||
} else {
|
||||
gains = append(gains, 0)
|
||||
losses = append(losses, -change)
|
||||
}
|
||||
}
|
||||
|
||||
// 只取最近period个数据点
|
||||
if len(gains) > period {
|
||||
gains = gains[len(gains)-period:]
|
||||
losses = losses[len(losses)-period:]
|
||||
}
|
||||
|
||||
avgGain := e.calculateAverage(gains)
|
||||
avgLoss := e.calculateAverage(losses)
|
||||
|
||||
if avgLoss == 0 {
|
||||
return 100
|
||||
}
|
||||
|
||||
rs := avgGain / avgLoss
|
||||
return 100 - (100 / (1 + rs))
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) calculateVolatility(prices []float64, period int) float64 {
|
||||
if len(prices) < period {
|
||||
return 0
|
||||
}
|
||||
|
||||
periodPrices := prices[len(prices)-period:]
|
||||
mean := e.calculateAverage(periodPrices)
|
||||
|
||||
variance := 0.0
|
||||
for _, price := range periodPrices {
|
||||
variance += math.Pow(price-mean, 2)
|
||||
}
|
||||
variance /= float64(len(periodPrices))
|
||||
|
||||
return math.Sqrt(variance) / mean
|
||||
}
|
||||
|
||||
func (e *FeatureEngine) DetectAlerts(features *SymbolFeatures) []Alert {
|
||||
var alerts []Alert
|
||||
|
||||
// 交易量放大检测
|
||||
if features.VolumeRatio5 > e.alertThresholds.VolumeSpike {
|
||||
alerts = append(alerts, Alert{
|
||||
Type: "VOLUME_SPIKE",
|
||||
Symbol: features.Symbol,
|
||||
Value: features.VolumeRatio5,
|
||||
Threshold: e.alertThresholds.VolumeSpike,
|
||||
Message: fmt.Sprintf("%s 交易量放大 %.2f 倍", features.Symbol, features.VolumeRatio5),
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// 15分钟价格异动
|
||||
if math.Abs(features.PriceChange15Min) > e.alertThresholds.PriceChange15Min {
|
||||
direction := "上涨"
|
||||
if features.PriceChange15Min < 0 {
|
||||
direction = "下跌"
|
||||
}
|
||||
alerts = append(alerts, Alert{
|
||||
Type: "PRICE_CHANGE_15MIN",
|
||||
Symbol: features.Symbol,
|
||||
Value: features.PriceChange15Min,
|
||||
Threshold: e.alertThresholds.PriceChange15Min,
|
||||
Message: fmt.Sprintf("%s 15分钟%s %.2f%%", features.Symbol, direction, features.PriceChange15Min*100),
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// 交易量趋势
|
||||
if features.VolumeTrend > e.alertThresholds.VolumeTrend {
|
||||
alerts = append(alerts, Alert{
|
||||
Type: "VOLUME_TREND",
|
||||
Symbol: features.Symbol,
|
||||
Value: features.VolumeTrend,
|
||||
Threshold: e.alertThresholds.VolumeTrend,
|
||||
Message: fmt.Sprintf("%s 交易量趋势增强 %.2f 倍", features.Symbol, features.VolumeTrend),
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// RSI超买超卖
|
||||
if features.RSI14 > e.alertThresholds.RSIOverbought {
|
||||
alerts = append(alerts, Alert{
|
||||
Type: "RSI_OVERBOUGHT",
|
||||
Symbol: features.Symbol,
|
||||
Value: features.RSI14,
|
||||
Threshold: e.alertThresholds.RSIOverbought,
|
||||
Message: fmt.Sprintf("%s RSI超买: %.2f", features.Symbol, features.RSI14),
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
} else if features.RSI14 < e.alertThresholds.RSIOversold {
|
||||
alerts = append(alerts, Alert{
|
||||
Type: "RSI_OVERSOLD",
|
||||
Symbol: features.Symbol,
|
||||
Value: features.RSI14,
|
||||
Threshold: e.alertThresholds.RSIOversold,
|
||||
Message: fmt.Sprintf("%s RSI超卖: %.2f", features.Symbol, features.RSI14),
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
return alerts
|
||||
}
|
||||
Reference in New Issue
Block a user