mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
feat: use OHLCV table format for kline data in AI prompts
- Add KlineBar struct with full OHLCV data and timestamp - Store complete kline data in TimeframeSeriesData.Klines - Format klines as readable table with Time, Open, High, Low, Close, Volume - Mark current (latest) bar for clarity - Use kline count from strategy config instead of hardcoded 10 - Keep MidPrices/Volume for backward compatibility - Update both market/data.go and decision/strategy_engine.go formatters
This commit is contained in:
+58
-23
@@ -162,8 +162,8 @@ func GetWithTimeframes(symbol string, timeframes []string, primaryTimeframe stri
|
||||
primaryKlines = klines
|
||||
}
|
||||
|
||||
// Calculate series data for this timeframe
|
||||
seriesData := calculateTimeframeSeries(klines, tf)
|
||||
// Calculate series data for this timeframe (use count from config)
|
||||
seriesData := calculateTimeframeSeries(klines, tf, count)
|
||||
timeframeData[tf] = seriesData
|
||||
}
|
||||
|
||||
@@ -212,25 +212,41 @@ func GetWithTimeframes(symbol string, timeframes []string, primaryTimeframe stri
|
||||
}
|
||||
|
||||
// calculateTimeframeSeries calculates series data for a single timeframe
|
||||
func calculateTimeframeSeries(klines []Kline, timeframe string) *TimeframeSeriesData {
|
||||
data := &TimeframeSeriesData{
|
||||
Timeframe: timeframe,
|
||||
MidPrices: make([]float64, 0, 10),
|
||||
EMA20Values: make([]float64, 0, 10),
|
||||
EMA50Values: make([]float64, 0, 10),
|
||||
MACDValues: make([]float64, 0, 10),
|
||||
RSI7Values: make([]float64, 0, 10),
|
||||
RSI14Values: make([]float64, 0, 10),
|
||||
Volume: make([]float64, 0, 10),
|
||||
func calculateTimeframeSeries(klines []Kline, timeframe string, count int) *TimeframeSeriesData {
|
||||
if count <= 0 {
|
||||
count = 10 // default
|
||||
}
|
||||
|
||||
// Get latest 10 data points
|
||||
start := len(klines) - 10
|
||||
data := &TimeframeSeriesData{
|
||||
Timeframe: timeframe,
|
||||
Klines: make([]KlineBar, 0, count),
|
||||
MidPrices: make([]float64, 0, count),
|
||||
EMA20Values: make([]float64, 0, count),
|
||||
EMA50Values: make([]float64, 0, count),
|
||||
MACDValues: make([]float64, 0, count),
|
||||
RSI7Values: make([]float64, 0, count),
|
||||
RSI14Values: make([]float64, 0, count),
|
||||
Volume: make([]float64, 0, count),
|
||||
}
|
||||
|
||||
// Get latest N data points based on count from config
|
||||
start := len(klines) - count
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
|
||||
for i := start; i < len(klines); i++ {
|
||||
// Store full OHLCV kline data
|
||||
data.Klines = append(data.Klines, KlineBar{
|
||||
Time: klines[i].OpenTime,
|
||||
Open: klines[i].Open,
|
||||
High: klines[i].High,
|
||||
Low: klines[i].Low,
|
||||
Close: klines[i].Close,
|
||||
Volume: klines[i].Volume,
|
||||
})
|
||||
|
||||
// Keep MidPrices and Volume for backward compatibility
|
||||
data.MidPrices = append(data.MidPrices, klines[i].Close)
|
||||
data.Volume = append(data.Volume, klines[i].Volume)
|
||||
|
||||
@@ -722,35 +738,54 @@ func Format(data *Data) string {
|
||||
|
||||
// formatTimeframeData formats data for a single timeframe
|
||||
func formatTimeframeData(sb *strings.Builder, data *TimeframeSeriesData) {
|
||||
if len(data.MidPrices) > 0 {
|
||||
// Use OHLCV table format if kline data is available
|
||||
if len(data.Klines) > 0 {
|
||||
sb.WriteString("Time(UTC) Open High Low Close Volume\n")
|
||||
for i, k := range data.Klines {
|
||||
t := time.Unix(k.Time/1000, 0).UTC()
|
||||
timeStr := t.Format("01-02 15:04")
|
||||
marker := ""
|
||||
if i == len(data.Klines)-1 {
|
||||
marker = " <- current"
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%-14s %-9.4f %-9.4f %-9.4f %-9.4f %-12.2f%s\n",
|
||||
timeStr, k.Open, k.High, k.Low, k.Close, k.Volume, marker))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
} else if len(data.MidPrices) > 0 {
|
||||
// Fallback to old format for backward compatibility
|
||||
sb.WriteString(fmt.Sprintf("Mid prices: %s\n\n", formatFloatSlice(data.MidPrices)))
|
||||
if len(data.Volume) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Volume: %s\n\n", formatFloatSlice(data.Volume)))
|
||||
}
|
||||
}
|
||||
|
||||
// Technical indicators
|
||||
if len(data.EMA20Values) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("EMA indicators (20‑period): %s\n\n", formatFloatSlice(data.EMA20Values)))
|
||||
sb.WriteString(fmt.Sprintf("EMA20: %s\n", formatFloatSlice(data.EMA20Values)))
|
||||
}
|
||||
|
||||
if len(data.EMA50Values) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("EMA indicators (50‑period): %s\n\n", formatFloatSlice(data.EMA50Values)))
|
||||
sb.WriteString(fmt.Sprintf("EMA50: %s\n", formatFloatSlice(data.EMA50Values)))
|
||||
}
|
||||
|
||||
if len(data.MACDValues) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("MACD indicators: %s\n\n", formatFloatSlice(data.MACDValues)))
|
||||
sb.WriteString(fmt.Sprintf("MACD: %s\n", formatFloatSlice(data.MACDValues)))
|
||||
}
|
||||
|
||||
if len(data.RSI7Values) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("RSI indicators (7‑Period): %s\n\n", formatFloatSlice(data.RSI7Values)))
|
||||
sb.WriteString(fmt.Sprintf("RSI7: %s\n", formatFloatSlice(data.RSI7Values)))
|
||||
}
|
||||
|
||||
if len(data.RSI14Values) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("RSI indicators (14‑Period): %s\n\n", formatFloatSlice(data.RSI14Values)))
|
||||
sb.WriteString(fmt.Sprintf("RSI14: %s\n", formatFloatSlice(data.RSI14Values)))
|
||||
}
|
||||
|
||||
if len(data.Volume) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Volume: %s\n\n", formatFloatSlice(data.Volume)))
|
||||
if data.ATR14 > 0 {
|
||||
sb.WriteString(fmt.Sprintf("ATR14: %.4f\n", data.ATR14))
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("ATR (14‑period): %.3f\n\n", data.ATR14))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// formatPriceWithDynamicPrecision dynamically selects precision based on price range
|
||||
|
||||
+20
-9
@@ -19,17 +19,28 @@ type Data struct {
|
||||
TimeframeData map[string]*TimeframeSeriesData `json:"timeframe_data,omitempty"`
|
||||
}
|
||||
|
||||
// KlineBar single kline bar with OHLCV data
|
||||
type KlineBar struct {
|
||||
Time int64 `json:"time"` // Unix timestamp in milliseconds
|
||||
Open float64 `json:"open"` // Open price
|
||||
High float64 `json:"high"` // High price
|
||||
Low float64 `json:"low"` // Low price
|
||||
Close float64 `json:"close"` // Close price
|
||||
Volume float64 `json:"volume"` // Volume
|
||||
}
|
||||
|
||||
// TimeframeSeriesData series data for a single timeframe
|
||||
type TimeframeSeriesData struct {
|
||||
Timeframe string `json:"timeframe"` // Timeframe identifier, e.g. "5m", "15m", "1h"
|
||||
MidPrices []float64 `json:"mid_prices"` // Price series
|
||||
EMA20Values []float64 `json:"ema20_values"` // EMA20 series
|
||||
EMA50Values []float64 `json:"ema50_values"` // EMA50 series
|
||||
MACDValues []float64 `json:"macd_values"` // MACD series
|
||||
RSI7Values []float64 `json:"rsi7_values"` // RSI7 series
|
||||
RSI14Values []float64 `json:"rsi14_values"` // RSI14 series
|
||||
Volume []float64 `json:"volume"` // Volume series
|
||||
ATR14 float64 `json:"atr14"` // ATR14
|
||||
Timeframe string `json:"timeframe"` // Timeframe identifier, e.g. "5m", "15m", "1h"
|
||||
Klines []KlineBar `json:"klines"` // Full OHLCV kline data
|
||||
MidPrices []float64 `json:"mid_prices"` // Price series (deprecated, kept for compatibility)
|
||||
EMA20Values []float64 `json:"ema20_values"` // EMA20 series
|
||||
EMA50Values []float64 `json:"ema50_values"` // EMA50 series
|
||||
MACDValues []float64 `json:"macd_values"` // MACD series
|
||||
RSI7Values []float64 `json:"rsi7_values"` // RSI7 series
|
||||
RSI14Values []float64 `json:"rsi14_values"` // RSI14 series
|
||||
Volume []float64 `json:"volume"` // Volume series (deprecated, use Klines)
|
||||
ATR14 float64 `json:"atr14"` // ATR14
|
||||
}
|
||||
|
||||
// OIData Open Interest data
|
||||
|
||||
Reference in New Issue
Block a user