feat: 添加OKX行情接入+趋势检测+累积变动系统+界面重构
- 新增OKX WebSocket行情连接器,扩展4交易所价格监控 - 新增z-score趋势检测引擎(TrendDetector),识别价格异动/趋势启动 - 新增累积变动跟踪(CumulativeTracker),基于1min/5min多交易所共识 - 趋势事件和累积变动事件持久化到SQLite - 新增Binance/OKX动量检测字段,扩展前端动量卡片至15列 - 迁移至macOS(darwin-arm64),更新前端依赖 - Dashboard网格重构:非交易卡片置顶,交易卡片置底 - TrackedCoin添加OK字段,添加ExBinance/ExOKX常量 - 前端新增趋势检测卡片、趋势历史卡片、累积变动卡片 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
047571921e
commit
b7767c95ae
@@ -0,0 +1,65 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CmEventRecord represents a persisted cumulative move state transition.
|
||||
type CmEventRecord struct {
|
||||
ID int64 `json:"id"`
|
||||
Coin string `json:"coin"`
|
||||
PrevState string `json:"prev_state"`
|
||||
NewState string `json:"new_state"`
|
||||
Direction string `json:"direction"`
|
||||
Score float64 `json:"score"`
|
||||
AvgChange float64 `json:"avg_change"`
|
||||
ExAgree int `json:"ex_agree"`
|
||||
ExTotal int `json:"ex_total"`
|
||||
BG1m float64 `json:"bg_1m"`
|
||||
HL1m float64 `json:"hl_1m"`
|
||||
BN1m float64 `json:"bn_1m"`
|
||||
OKX1m float64 `json:"okx_1m"`
|
||||
BG5m float64 `json:"bg_5m"`
|
||||
HL5m float64 `json:"hl_5m"`
|
||||
BN5m float64 `json:"bn_5m"`
|
||||
OKX5m float64 `json:"okx_5m"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// InsertCmEvent saves a cumulative move event to the database.
|
||||
func (d *DB) InsertCmEvent(coin, prevState, newState, direction string, score, avgChange float64, exAgree, exTotal int, bg1m, hl1m, bn1m, okx1m, bg5m, hl5m, bn5m, okx5m float64) error {
|
||||
_, err := d.Exec(`
|
||||
INSERT INTO cm_events (coin, prev_state, new_state, direction, score, avg_change, ex_agree, ex_total, bg_1m, hl_1m, bn_1m, okx_1m, bg_5m, hl_5m, bn_5m, okx_5m, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
coin, prevState, newState, direction, score, avgChange, exAgree, exTotal, bg1m, hl1m, bn1m, okx1m, bg5m, hl5m, bn5m, okx5m, Now().Format(time.RFC3339))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCmEvents returns cumulative move events ordered by creation time descending.
|
||||
func (d *DB) GetCmEvents(limit int) ([]CmEventRecord, error) {
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
rows, err := d.Query(`
|
||||
SELECT id, coin, prev_state, new_state, direction, score, avg_change, ex_agree, ex_total, bg_1m, hl_1m, bn_1m, okx_1m, bg_5m, hl_5m, bn_5m, okx_5m, created_at
|
||||
FROM cm_events
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []CmEventRecord
|
||||
for rows.Next() {
|
||||
var r CmEventRecord
|
||||
if err := rows.Scan(&r.ID, &r.Coin, &r.PrevState, &r.NewState, &r.Direction,
|
||||
&r.Score, &r.AvgChange, &r.ExAgree, &r.ExTotal,
|
||||
&r.BG1m, &r.HL1m, &r.BN1m, &r.OKX1m,
|
||||
&r.BG5m, &r.HL5m, &r.BN5m, &r.OKX5m, &r.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, r)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
@@ -108,6 +108,48 @@ func (d *DB) migrate() error {
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_system_orders_trade ON system_orders(trade_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS trend_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
coin TEXT NOT NULL,
|
||||
prev_state TEXT NOT NULL,
|
||||
new_state TEXT NOT NULL,
|
||||
direction TEXT NOT NULL,
|
||||
z_score REAL,
|
||||
volatility REAL,
|
||||
bg_change REAL,
|
||||
hl_change REAL,
|
||||
bn_change REAL,
|
||||
okx_change REAL,
|
||||
ex_agree INTEGER,
|
||||
ex_total INTEGER,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_trend_events_coin ON trend_events(coin);
|
||||
CREATE INDEX IF NOT EXISTS idx_trend_events_created ON trend_events(created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cm_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
coin TEXT NOT NULL,
|
||||
prev_state TEXT NOT NULL,
|
||||
new_state TEXT NOT NULL,
|
||||
direction TEXT NOT NULL,
|
||||
score REAL,
|
||||
avg_change REAL,
|
||||
ex_agree INTEGER,
|
||||
ex_total INTEGER,
|
||||
bg_1m REAL,
|
||||
hl_1m REAL,
|
||||
bn_1m REAL,
|
||||
okx_1m REAL,
|
||||
bg_5m REAL,
|
||||
hl_5m REAL,
|
||||
bn_5m REAL,
|
||||
okx_5m REAL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_cm_events_coin ON cm_events(coin);
|
||||
CREATE INDEX IF NOT EXISTS idx_cm_events_created ON cm_events(created_at);
|
||||
`
|
||||
_, err := d.Exec(schema)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TrendEventRecord represents a persisted trend state transition.
|
||||
type TrendEventRecord struct {
|
||||
ID int64 `json:"id"`
|
||||
Coin string `json:"coin"`
|
||||
PrevState string `json:"prev_state"`
|
||||
NewState string `json:"new_state"`
|
||||
Direction string `json:"direction"`
|
||||
ZScore float64 `json:"z_score"`
|
||||
Volatility float64 `json:"volatility"`
|
||||
BGChange float64 `json:"bg_change"`
|
||||
HLChange float64 `json:"hl_change"`
|
||||
BNChange float64 `json:"bn_change"`
|
||||
OKXChange float64 `json:"okx_change"`
|
||||
ExAgree int `json:"ex_agree"`
|
||||
ExTotal int `json:"ex_total"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// InsertTrendEvent saves a trend event to the database.
|
||||
func (d *DB) InsertTrendEvent(coin, prevState, newState, direction string, zScore, volatility, bgChange, hlChange, bnChange, okxChange float64, exAgree, exTotal int) error {
|
||||
_, err := d.Exec(`
|
||||
INSERT INTO trend_events (coin, prev_state, new_state, direction, z_score, volatility, bg_change, hl_change, bn_change, okx_change, ex_agree, ex_total, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
coin, prevState, newState, direction, zScore, volatility, bgChange, hlChange, bnChange, okxChange, exAgree, exTotal, Now().Format(time.RFC3339))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTrendEvents returns trend events ordered by creation time descending.
|
||||
func (d *DB) GetTrendEvents(limit int) ([]TrendEventRecord, error) {
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
rows, err := d.Query(`
|
||||
SELECT id, coin, prev_state, new_state, direction, z_score, volatility, bg_change, hl_change, bn_change, okx_change, ex_agree, ex_total, created_at
|
||||
FROM trend_events
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []TrendEventRecord
|
||||
for rows.Next() {
|
||||
var r TrendEventRecord
|
||||
if err := rows.Scan(&r.ID, &r.Coin, &r.PrevState, &r.NewState, &r.Direction,
|
||||
&r.ZScore, &r.Volatility, &r.BGChange, &r.HLChange, &r.BNChange, &r.OKXChange,
|
||||
&r.ExAgree, &r.ExTotal, &r.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, r)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user