Browse Source

fetch rpc

main
strange 10 months ago
parent
commit
0777bb1fe2
  1. 9
      README.md
  2. 4
      api/exchange.proto
  3. 14
      api/pub.proto
  4. 1
      api/trading.proto
  5. 5
      config/config.toml
  6. 4
      config/exchange.toml
  7. 31
      internal/exchange/exchange_service.go
  8. 2
      internal/trading/kline_series.go
  9. 13
      internal/trading/trading_service.go
  10. 1
      pkg/config/config.go
  11. 9
      pkg/config/grpc_options.go
  12. 2
      pkg/data/entity/trade_order.go
  13. 6
      pkg/strategy/sig_strategy_params.go
  14. 8
      pkg/types/decimals/decimal.go
  15. 35
      pkg/types/kline.go

9
README.md

@ -49,6 +49,9 @@ k线推送
价格/交易量精度->tsdb读写存储 价格/交易量精度->tsdb读写存储
kline时间窗口 kline时间窗口
Dragonfly redis替换
### 绘图框架
tradingview advanced-charts: tradingview advanced-charts:
- https://cn.tradingview.com/advanced-charts/ - https://cn.tradingview.com/advanced-charts/
tradingview lightweight-charts: tradingview lightweight-charts:
@ -56,6 +59,8 @@ tradingview lightweight-charts:
- https://github.com/tradingview/lightweight-charts - https://github.com/tradingview/lightweight-charts
d3js: d3js:
- https://d3js.org/ - https://d3js.org/
night-vision:
- [night-vision](https://github.com/project-nv/night-vision)
DXcharts: DXcharts:
- https://devexperts.com/dxcharts/ - https://devexperts.com/dxcharts/
@ -71,3 +76,7 @@ strategy0: 趋势追踪,增长趋势,
2. 当前属于波段增长点 2. 当前属于波段增长点
3. 买卖点画叉 3. 买卖点画叉
4. 移动止盈,固定止损,风险评估止损 4. 移动止盈,固定止损,风险评估止损
### 量化框架参考
[investing-algorithm-framework](https://github.com/coding-kitties/investing-algorithm-framework)

4
api/exchange.proto

@ -63,9 +63,9 @@ message ReqHistoryKline {
int64 before = 4; int64 before = 4;
int64 after = 5; int64 after = 5;
uint32 count = 6; // k线条数,before或after其中一个为0时有效 uint32 count = 6; // k线条数,before或after其中一个为0时有效
// bool open = 7; // , before/after bool open = 7; // , before/after不为0时不包含
bool live = 8; // k线, before和after为0时是否追加实时k线 bool live = 8; // k线, before和after为0时是否追加实时k线
bool asc = 9; // , bool desc = 9; // ,
} }
message RspHistoryKline { message RspHistoryKline {
ExchangeType exchange = 1; // ExchangeType exchange = 1; //

14
api/pub.proto

@ -106,14 +106,14 @@ message TradeInstanceState {
} }
message Kline { message Kline {
int64 ts = 2; int64 time = 2;
string interval = 3; // string interval = 3; //
string open = 4; double open = 4;
string high = 5; double high = 5;
string low = 6; double low = 6;
string close = 7; double close = 7;
string vol = 8; // double vol = 8; //
string volQuote = 9; // double volQuote = 9; //
bool confirm = 10; // k线是否完结 bool confirm = 10; // k线是否完结
} }

1
api/trading.proto

@ -48,6 +48,7 @@ message ReqStrategySeries {
int64 before = 6; // 0 int64 before = 6; // 0
int64 after = 7; // 0 int64 after = 7; // 0
int32 count = 8; // k线条数,before或after其中一个为0时有效 int32 count = 8; // k线条数,before或after其中一个为0时有效
map<string,string> sigParam = 15; //
} }
message RspStrategySeries { message RspStrategySeries {
repeated Side signal = 1; // 0.sell,1.buy repeated Side signal = 1; // 0.sell,1.buy

5
config/config.toml

@ -1,13 +1,12 @@
[grpc] [grpc]
maxSendMsgSize = "16Mi" maxSendMsgSize = "8Mi"
maxRecvMsgSize = "16Mi" maxRecvMsgSize = "8Mi"
readBufferSize = "8Ki" readBufferSize = "8Ki"
writeBufferSize = "8Ki" writeBufferSize = "8Ki"
[grpc.keepalive] [grpc.keepalive]
idleTimeout = "60s" # 空闲连接超时 idleTimeout = "60s" # 空闲连接超时
forceCloseWait = "20s"
keepAliveInterval = "60s" # 发送 ping 的间隔 keepAliveInterval = "60s" # 发送 ping 的间隔
keepAliveTimeout = "20s" # ping 超时 keepAliveTimeout = "20s" # ping 超时
maxLifeTime = "2h" maxLifeTime = "2h"

4
config/exchange.toml

@ -18,8 +18,8 @@ receiveBuffer = 4096
marketSubscribeLimit = 16 marketSubscribeLimit = 16
consumeBatch = 1024 consumeBatch = 1024
consumeLater = 2000 # 时间到达later或者数据累计到batch触发consume consumeLater = 2000 # 时间到达later或者数据累计到batch触发consume
# httpProxy = "http://192.168.1.5:7890" httpProxy = "http://192.168.1.5:7890"
httpProxy = "http://10.255.183.209:7890" # httpProxy = "http://10.255.183.209:7890"
# 模拟盘API交易地址如下: # 模拟盘API交易地址如下:
# REST:https://www.okx.com # REST:https://www.okx.com

31
internal/exchange/exchange_service.go

@ -658,7 +658,7 @@ func (svc *ExchangeService) ExchangeInstanceState(req *pb.ReqExchangeInstanceSta
} }
const ( const (
MaxHistoryKlines = 100 MaxHistoryKlines = 200
) )
// HistoryKline 获取交易产品历史k线 (before < klines... < after) // HistoryKline 获取交易产品历史k线 (before < klines... < after)
@ -691,7 +691,7 @@ func (svc *ExchangeService) HistoryKline(ctx context.Context, req *pb.ReqHistory
} }
// 拉取最新的 // 拉取最新的
lastTs := int64(0) lastTs := int64(0)
if afterTs == 0 { if req.After == 0 {
liveK := exchangeInst.LiveKline.Get(interval) liveK := exchangeInst.LiveKline.Get(interval)
lastTs = liveK.Ts lastTs = liveK.Ts
if !liveK.Confirm { if !liveK.Confirm {
@ -707,6 +707,21 @@ func (svc *ExchangeService) HistoryKline(ctx context.Context, req *pb.ReqHistory
if beforeTs == 0 { if beforeTs == 0 {
beforeTs = max(intervalAdder(afterTs, -count+1), KlineBefore0) beforeTs = max(intervalAdder(afterTs, -count+1), KlineBefore0)
} }
// 开区间
if req.Open {
if req.After != 0 {
afterTs = max(intervalAdder(afterTs, -1), beforeTs)
if req.Before == 0 {
beforeTs = max(intervalAdder(beforeTs, -1), KlineBefore0)
}
}
if req.Before != 0 {
beforeTs = min(intervalAdder(beforeTs, 1), afterTs)
if req.After == 0 {
afterTs = min(intervalAdder(beforeTs, 1), lastTs)
}
}
}
if beforeTs > afterTs { if beforeTs > afterTs {
err = fmt.Errorf("time range invalid: before must less then after") err = fmt.Errorf("time range invalid: before must less then after")
return return
@ -726,6 +741,10 @@ func (svc *ExchangeService) HistoryKline(ctx context.Context, req *pb.ReqHistory
if len(klines) == 0 { if len(klines) == 0 {
return return
} }
// 检查k线是否连续进行补齐
if err = svc.paddingKlinesIfNotSeries(exchange, req.InstId, interval, klines); err != nil {
return
}
lastK := klines[len(klines)-1] lastK := klines[len(klines)-1]
// vmtsdb 数据刷盘30s延迟, 使用内存数据替代第一根k线 // vmtsdb 数据刷盘30s延迟, 使用内存数据替代第一根k线
@ -740,7 +759,7 @@ func (svc *ExchangeService) HistoryKline(ctx context.Context, req *pb.ReqHistory
} }
// 降序排序 // 降序排序
if !req.Asc { if req.Desc {
collect.Reverse(klines) collect.Reverse(klines)
} }
@ -748,10 +767,10 @@ func (svc *ExchangeService) HistoryKline(ctx context.Context, req *pb.ReqHistory
if req.Live && len(klines) > 0 { if req.Live && len(klines) > 0 {
liveK := exchangeInst.LiveKline.Get(interval) liveK := exchangeInst.LiveKline.Get(interval)
if latest := intervalAdder(lastK.Ts, 1) == liveK.Ts; latest { if latest := intervalAdder(lastK.Ts, 1) == liveK.Ts; latest {
if req.Asc { if req.Desc {
klines = append(klines, &liveK)
} else {
klines = append([]*types.Kline{&liveK}, klines...) klines = append([]*types.Kline{&liveK}, klines...)
} else {
klines = append(klines, &liveK)
} }
rsp.Live = true rsp.Live = true
} }

2
internal/trading/kline_series.go

@ -80,7 +80,7 @@ func (s *KlineSeries) Series(offset, count int16) (klines series.Klines, ok bool
if ok = offset >= 0 && offset < MaxSeriesKlines; !ok { if ok = offset >= 0 && offset < MaxSeriesKlines; !ok {
return return
} }
if ok = count >= 0 && offset+count < MaxSeriesKlines; !ok { if ok = count > 0 && offset+count < MaxSeriesKlines; !ok {
return return
} }

13
internal/trading/trading_service.go

@ -208,11 +208,20 @@ func (svc *TradingService) IndicatorSeries(req *pb.ReqIndicatorSeries, rsp *pb.R
// StrategySeries 简单策略信号测试 // StrategySeries 简单策略信号测试
func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) { func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) {
strategy, ok := svc.strategyReg.NewSigStrategy(req.Strategy) // sigStrategy
sigStrategy, ok := svc.strategyReg.NewSigStrategy(req.Strategy)
if !ok { if !ok {
err = fmt.Errorf("strategy %s not exists", req.Strategy) err = fmt.Errorf("strategy %s not exists", req.Strategy)
return return
} }
err = sigStrategy.Init(strategy.SigStrategyParam{
Interval: types.Interval(req.Interval),
Param: req.SigParam,
})
if err != nil {
return
}
interval := types.Interval(req.Interval) interval := types.Interval(req.Interval)
intervalAdd, ok := types.SupportedIntervals[interval] intervalAdd, ok := types.SupportedIntervals[interval]
if !ok { if !ok {
@ -230,7 +239,7 @@ func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.Rsp
strategyCtx := NewStrategyContext(klineSeries, svc.indicatorReg) strategyCtx := NewStrategyContext(klineSeries, svc.indicatorReg)
for i := range req.Count { for i := range req.Count {
strategyCtx.SetOffset(int16(i)) strategyCtx.SetOffset(int16(i))
strategy.Update(strategyCtx) sigStrategy.Update(strategyCtx)
} }
rsp.Signal = strategyCtx.signal rsp.Signal = strategyCtx.signal
rsp.Times = strategyCtx.signalTimes rsp.Times = strategyCtx.signalTimes

1
pkg/config/config.go

@ -35,7 +35,6 @@ type GrpcConfig struct {
type GrpcKeepalive struct { type GrpcKeepalive struct {
IdleTimeout string IdleTimeout string
ForceCloseWait string
KeepAliveInterval string KeepAliveInterval string
KeepAliveTimeout string KeepAliveTimeout string
MaxLifeTime string MaxLifeTime string

9
pkg/config/grpc_options.go

@ -12,13 +12,13 @@ func GetGrpcOptions(c GrpcConfig, customOpts ...grpc.ServerOption) (opts []grpc.
opts = append(opts, grpc.MaxSendMsgSize(conver.MustParseDataUnitInt(c.MaxSendMsgSize))) opts = append(opts, grpc.MaxSendMsgSize(conver.MustParseDataUnitInt(c.MaxSendMsgSize)))
} }
if c.MaxRecvMsgSize != "" { if c.MaxRecvMsgSize != "" {
opts = append(opts, grpc.MaxSendMsgSize(conver.MustParseDataUnitInt(c.MaxRecvMsgSize))) opts = append(opts, grpc.MaxRecvMsgSize(conver.MustParseDataUnitInt(c.MaxRecvMsgSize)))
} }
if c.ReadBufferSize != "" { if c.ReadBufferSize != "" {
opts = append(opts, grpc.MaxSendMsgSize(conver.MustParseDataUnitInt(c.ReadBufferSize))) opts = append(opts, grpc.ReadBufferSize(conver.MustParseDataUnitInt(c.ReadBufferSize)))
} }
if c.WriteBufferSize != "" { if c.WriteBufferSize != "" {
opts = append(opts, grpc.MaxSendMsgSize(conver.MustParseDataUnitInt(c.WriteBufferSize))) opts = append(opts, grpc.WriteBufferSize(conver.MustParseDataUnitInt(c.WriteBufferSize)))
} }
// grpc server keepalive // grpc server keepalive
@ -26,9 +26,6 @@ func GetGrpcOptions(c GrpcConfig, customOpts ...grpc.ServerOption) (opts []grpc.
if c.keepalive.IdleTimeout != "" { if c.keepalive.IdleTimeout != "" {
keep.MaxConnectionIdle = conver.MustParseDuration(c.keepalive.IdleTimeout) keep.MaxConnectionIdle = conver.MustParseDuration(c.keepalive.IdleTimeout)
} }
if c.keepalive.ForceCloseWait != "" {
keep.MaxConnectionAgeGrace = conver.MustParseDuration(c.keepalive.ForceCloseWait)
}
if c.keepalive.KeepAliveInterval != "" { if c.keepalive.KeepAliveInterval != "" {
keep.Time = conver.MustParseDuration(c.keepalive.KeepAliveInterval) keep.Time = conver.MustParseDuration(c.keepalive.KeepAliveInterval)
} }

2
pkg/data/entity/trade_order.go

@ -25,6 +25,8 @@ type TradeOrder struct {
TradeTime int64 `gorm:"column:trade_time" json:"tradeTime"` // 成交时间 TradeTime int64 `gorm:"column:trade_time" json:"tradeTime"` // 成交时间
CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间 CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间
UpdateTime int64 `gorm:"column:update_time" json:"updateTime"` // 更新时间 UpdateTime int64 `gorm:"column:update_time" json:"updateTime"` // 更新时间
Decision string `gorm:"column:decision" json:"decision"` // 决策过程数据 json map
DecisionM map[string]any `gorm:"-" json:"decisionM"`
} }
func (TradeOrder) TableName() string { func (TradeOrder) TableName() string {

6
pkg/strategy/sig_strategy_params.go

@ -81,14 +81,14 @@ type ISigStrategyParamGenerator interface {
type SigStrategyParam struct { type SigStrategyParam struct {
Interval types.Interval `json:"interval"` // 策略驱动周期 Interval types.Interval `json:"interval"` // 策略驱动周期
Params map[string]string `json:"params"` // 策略执行参数 Param map[string]string `json:"param"` // 策略执行参数
} }
func (s *SigStrategyParam) Get(key string) (v string, ok bool) { func (s *SigStrategyParam) Get(key string) (v string, ok bool) {
if len(s.Params) == 0 { if len(s.Param) == 0 {
return return
} }
v, ok = s.Params[key] v, ok = s.Param[key]
return return
} }

8
pkg/types/decimals/decimal.go

@ -13,3 +13,11 @@ func MustToFloat64(v decimal.Decimal) float64 {
} }
return f return f
} }
func MustFromFloat64(f float64) (v decimal.Decimal) {
v, err := decimal.NewFromFloat64(f)
if err != nil {
panic(fmt.Errorf("decimal from float64 error: %v", err))
}
return
}

35
pkg/types/kline.go

@ -2,6 +2,7 @@ package types
import ( import (
"sig-pub/api/pb" "sig-pub/api/pb"
"sig-pub/pkg/types/decimals"
"github.com/govalues/decimal" "github.com/govalues/decimal"
) )
@ -27,28 +28,34 @@ type Kline struct {
func (k *Kline) ParsePBKline(exchange pb.ExchangeType, kline *pb.Kline) { func (k *Kline) ParsePBKline(exchange pb.ExchangeType, kline *pb.Kline) {
// k.Exchange = exchange.String() // k.Exchange = exchange.String()
k.Ts = kline.Time
k.Interval = Interval(kline.Interval) k.Interval = Interval(kline.Interval)
k.Ts = kline.Ts
k.Open = decimal.MustParse(kline.Open)
k.High = decimal.MustParse(kline.High)
k.Low = decimal.MustParse(kline.Low)
k.Close = decimal.MustParse(kline.Close)
k.Vol = decimal.MustParse(kline.Vol)
k.VolQuote = decimal.MustParse(kline.VolQuote)
k.Confirm = kline.Confirm k.Confirm = kline.Confirm
// k.Open = decimal.MustParse(kline.Open)
// k.High = decimal.MustParse(kline.High)
// k.Low = decimal.MustParse(kline.Low)
// k.Close = decimal.MustParse(kline.Close)
// k.Vol = decimal.MustParse(kline.Vol)
// k.VolQuote = decimal.MustParse(kline.VolQuote)
k.Open = decimals.MustFromFloat64(kline.Open)
k.High = decimals.MustFromFloat64(kline.High)
k.Low = decimals.MustFromFloat64(kline.Low)
k.Close = decimals.MustFromFloat64(kline.Close)
k.Vol = decimals.MustFromFloat64(kline.Vol)
k.VolQuote = decimals.MustFromFloat64(kline.VolQuote)
} }
func (k *Kline) ToPBKline() (kline *pb.Kline) { func (k *Kline) ToPBKline() (kline *pb.Kline) {
kline = &pb.Kline{ kline = &pb.Kline{
Ts: k.Ts, Time: k.Ts,
Interval: string(k.Interval), Interval: string(k.Interval),
Open: k.Open.String(),
High: k.High.String(),
Low: k.Low.String(),
Close: k.Close.String(),
Vol: k.Vol.String(),
VolQuote: k.VolQuote.String(),
Confirm: k.Confirm, Confirm: k.Confirm,
Open: decimals.MustToFloat64(k.Open),
High: decimals.MustToFloat64(k.High),
Low: decimals.MustToFloat64(k.Low),
Close: decimals.MustToFloat64(k.Close),
Vol: decimals.MustToFloat64(k.Vol),
VolQuote: decimals.MustToFloat64(k.VolQuote),
} }
return return
} }

Loading…
Cancel
Save