26 changed files with 465 additions and 173 deletions
@ -1,25 +0,0 @@ |
|||||||
syntax = "proto3"; |
|
||||||
|
|
||||||
import "api/pub.proto"; |
|
||||||
|
|
||||||
option go_package = "./pb"; |
|
||||||
|
|
||||||
service IndicatorService { |
|
||||||
rpc Subscribe(IndicatorSubReq) returns (stream Indicator); // 订阅指标 |
|
||||||
rpc Plot(Indicator) returns (Indicator); // 绘图 |
|
||||||
} |
|
||||||
|
|
||||||
message IndicatorSubReq { |
|
||||||
string topic = 1; |
|
||||||
string instId = 2; |
|
||||||
int32 window = 3; |
|
||||||
} |
|
||||||
|
|
||||||
message Indicator { |
|
||||||
ExchangeType exhcange = 1; |
|
||||||
string instId = 2; |
|
||||||
string indicator = 3; |
|
||||||
string sub = 4; // 指标子标题, MA5, MA10, MA20 |
|
||||||
int64 Ts = 5; |
|
||||||
bytes payload = 8; |
|
||||||
} |
|
||||||
@ -0,0 +1,38 @@ |
|||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
import "api/pub.proto"; |
||||||
|
|
||||||
|
option go_package = "./pb"; |
||||||
|
|
||||||
|
service OrderService { |
||||||
|
// request-response |
||||||
|
rpc SubmitOrder(OrderSubmitReq) returns (OrderSubmitRsp) {} |
||||||
|
// rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse) {} |
||||||
|
// rpc QueryOrder(QueryOrderRequest) returns (QueryOrderResponse) {} |
||||||
|
// rpc QueryOrders(QueryOrdersRequest) returns (QueryOrdersResponse) {} |
||||||
|
// rpc QueryTrades(QueryTradesRequest) returns (QueryTradesResponse) {} |
||||||
|
} |
||||||
|
|
||||||
|
message OrderSubmit { |
||||||
|
string session = 1; |
||||||
|
string exchange = 2; |
||||||
|
string symbol = 3; |
||||||
|
Side side = 4; |
||||||
|
string price = 6; |
||||||
|
string quantity = 5; |
||||||
|
string stop_price = 7; |
||||||
|
OrderType order_type = 8; |
||||||
|
string client_order_id = 9; |
||||||
|
int64 group_id = 10; |
||||||
|
} |
||||||
|
|
||||||
|
message OrderSubmitReq { |
||||||
|
string session = 1; |
||||||
|
repeated OrderSubmit submit_orders = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message OrderSubmitRsp { |
||||||
|
string session = 1; |
||||||
|
repeated Order orders = 2; |
||||||
|
Error error = 3; |
||||||
|
} |
||||||
@ -1,35 +0,0 @@ |
|||||||
package indicator |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"sig-pub/api/pb" |
|
||||||
"sig-pub/pkg/types" |
|
||||||
) |
|
||||||
|
|
||||||
type RSI struct { |
|
||||||
// types.IntervalWindow
|
|
||||||
window int // 窗口大小
|
|
||||||
} |
|
||||||
|
|
||||||
func (ind *RSI) IntervalWindow() { |
|
||||||
// 指标参数注入
|
|
||||||
} |
|
||||||
|
|
||||||
func (ind *RSI) QueryRange(exchange pb.ExchangeType, instId string, interval types.Interval, rsi int) (query string, err error) { |
|
||||||
var r types.MeticMatrix |
|
||||||
_ = r |
|
||||||
intervalAdder, ok := types.SupportedIntervals[interval] |
|
||||||
if !ok { |
|
||||||
err = fmt.Errorf("unsupport interval %s", interval) |
|
||||||
return |
|
||||||
} |
|
||||||
minutes := intervalAdder(0, int64(rsi)) / 1000 / 60 |
|
||||||
|
|
||||||
query = fmt.Sprintf(` |
|
||||||
100 - 100 / (1 + ( |
|
||||||
avg_over_time(clamp_min(delta(%s{kind="close", interval="%s", exchange="%s"}), 0)[%dm]) / |
|
||||||
avg_over_time(abs(clamp_max(delta(%s{kind="close", interval="%s", exchange="%s"}), 0))[%dm]) |
|
||||||
)) |
|
||||||
`, instId, interval, exchange, minutes, instId, interval, exchange, minutes) |
|
||||||
return |
|
||||||
} |
|
||||||
@ -1,4 +1,4 @@ |
|||||||
package trading |
package order |
||||||
|
|
||||||
// 交易服务
|
// 交易服务
|
||||||
// load strategy plugins
|
// load strategy plugins
|
||||||
@ -0,0 +1,56 @@ |
|||||||
|
package trading |
||||||
|
|
||||||
|
import ( |
||||||
|
"sig-pub/api/pb" |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"sig-pub/pkg/types/series" |
||||||
|
) |
||||||
|
|
||||||
|
type KlineSeries struct { |
||||||
|
Exchange pb.ExchangeType |
||||||
|
InstId string |
||||||
|
Interval types.Interval |
||||||
|
Ts int64 |
||||||
|
klines []*types.Kline |
||||||
|
klineStore KlineStore |
||||||
|
} |
||||||
|
|
||||||
|
func NewKlineSeries(ts int64, interval types.Interval, klineStore KlineStore) KlineSeries { |
||||||
|
return KlineSeries{ |
||||||
|
Ts: ts, |
||||||
|
Interval: interval, |
||||||
|
klineStore: klineStore, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Get
|
||||||
|
// [0]当前k线
|
||||||
|
func (a KlineSeries) Get(start int16) (kline types.Kline) { |
||||||
|
ts := a.Interval.MustAddMul(a.Ts, int64(-start)) |
||||||
|
|
||||||
|
for _, k := range a.klines { |
||||||
|
if k.Ts == ts { |
||||||
|
return *k |
||||||
|
} |
||||||
|
} |
||||||
|
// todo query tsdb
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Series [start...end]
|
||||||
|
func (a KlineSeries) Series(start, end int16) (klines series.Klines) { |
||||||
|
endTs := a.Interval.MustAddMul(a.Ts, int64(-start)) |
||||||
|
startTs := a.Interval.MustAddMul(a.Ts, int64(-end)) |
||||||
|
_ = endTs |
||||||
|
_ = startTs |
||||||
|
// return a.klineStore.GetRange(startTs, endTs)
|
||||||
|
// todo
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (s *KlineSeries) Update(kline *types.Kline) []types.Kline { |
||||||
|
s.klines = append(s.klines, kline) |
||||||
|
// todo copy(s.klines, s.klines[0:1]) set index=20, ts=kline.ts
|
||||||
|
s.Ts = kline.Ts |
||||||
|
return nil |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
package trading |
||||||
|
|
||||||
|
import ( |
||||||
|
"sig-pub/api/pb" |
||||||
|
vmts "sig-pub/pkg/storage/tsdb/victoria_metrics" |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"sig-pub/pkg/utils/collect" |
||||||
|
) |
||||||
|
|
||||||
|
type KlineStore struct { |
||||||
|
vmdb vmts.VictoriaMetricsTSDB |
||||||
|
store [3]*collect.ConcurrentMap[string, collect.ConcurrentMap[types.Interval, *KlineSeries]] // K线列表: []exchange<instId, interval, klines>
|
||||||
|
} |
||||||
|
|
||||||
|
func NewKlineSeriesStore(vmdb vmts.VictoriaMetricsTSDB) (kss *KlineStore) { |
||||||
|
kss = &KlineStore{ |
||||||
|
vmdb: vmdb, |
||||||
|
} |
||||||
|
kss.store[pb.ExchangeType_OKX] = collect.NewConcurrentMap[string, collect.ConcurrentMap[types.Interval, *KlineSeries]](64, func(s string) string { return s }) |
||||||
|
// kss.klines[pb.ExchangeType_BINANCE] =
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (s *KlineStore) Update(exchange pb.ExchangeType, kline *types.Kline) (k types.Kline) { |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
package indicator |
||||||
|
|
||||||
|
import ( |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"sig-pub/pkg/types/series" |
||||||
|
) |
||||||
|
|
||||||
|
// IIndicator 指标基础计算接口
|
||||||
|
type IIndicator interface { |
||||||
|
Calculate() (vector float64) |
||||||
|
} |
||||||
|
|
||||||
|
// IKlineSeries k线序列, strategy服务提供
|
||||||
|
type IKlineSeries interface { |
||||||
|
Get(start int16) (kline types.Kline) |
||||||
|
Series(start, end int16) (klines series.Klines) |
||||||
|
} |
||||||
|
|
||||||
|
// IIndicatorSeries 指标序列, 供策略读取, strategy服务提供
|
||||||
|
type IIndicatorSeries interface { |
||||||
|
Get(start int16) (vector float64) |
||||||
|
Series(start, end int16) (matrix series.Floats) |
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
package indicator |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"sig-pub/pkg/trader" |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"sig-pub/pkg/types/series" |
||||||
|
|
||||||
|
"github.com/spf13/cast" |
||||||
|
) |
||||||
|
|
||||||
|
// RSI0: 相对强弱指数 (RSI0)
|
||||||
|
// rsi define: https://www.investopedia.com/terms/r/rsi.asp
|
||||||
|
type RSI0 struct { |
||||||
|
trader.Indicator |
||||||
|
series.Series |
||||||
|
values series.Floats |
||||||
|
prices series.Floats |
||||||
|
|
||||||
|
argBaseDay int32 |
||||||
|
} |
||||||
|
|
||||||
|
func NewRSI() *RSI0 { |
||||||
|
return &RSI0{} |
||||||
|
} |
||||||
|
|
||||||
|
func (ind RSI0) Meta() trader.IndicatorMeta { |
||||||
|
return trader.IndicatorMeta{ |
||||||
|
Name: "RSI", |
||||||
|
Desc: "", |
||||||
|
Args: []trader.Arg{ |
||||||
|
{Name: "基准天数", Desc: "", ArgType: trader.ArgTypeUInt}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (ind *RSI0) Init(indId int64, exchange any, args []string) (code trader.ErrorCode, err error) { |
||||||
|
arg0, err := cast.ToInt32E(args[0]) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
ind.argBaseDay = arg0 |
||||||
|
|
||||||
|
cast.ToIntE("1") |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (ind *RSI0) Update(klines []types.Kline) (err error) { |
||||||
|
for _, kline := range klines { |
||||||
|
c, ok := kline.Close.Float64() |
||||||
|
if !ok { |
||||||
|
err = fmt.Errorf("kline close to float64 error: %s", kline.Close.String()) |
||||||
|
return |
||||||
|
} |
||||||
|
ind.prices.Push(c) |
||||||
|
} |
||||||
|
|
||||||
|
diff := ind.prices.Diff() |
||||||
|
_ = diff |
||||||
|
return |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
package decimals |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/govalues/decimal" |
||||||
|
) |
||||||
|
|
||||||
|
func MustToFloat64(v decimal.Decimal) float64 { |
||||||
|
f, ok := v.Float64() |
||||||
|
if !ok { |
||||||
|
panic(fmt.Errorf("decimal to float64 error: %v", v)) |
||||||
|
} |
||||||
|
return f |
||||||
|
} |
||||||
@ -1,23 +0,0 @@ |
|||||||
package series |
|
||||||
|
|
||||||
import ( |
|
||||||
"sig-pub/pkg/types" |
|
||||||
|
|
||||||
"github.com/govalues/decimal" |
|
||||||
) |
|
||||||
|
|
||||||
func Klines2Decimals(klines []types.Kline, mapping func(kline types.Kline) decimal.Decimal) (r Decimals) { |
|
||||||
r = NewDecimals(len(klines)) |
|
||||||
for _, k := range klines { |
|
||||||
r.Push(mapping(k)) |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func Klines2Floats(klines []types.Kline, mapping func(kline types.Kline) float64) (r Floats) { |
|
||||||
r = NewFloats(len(klines)) |
|
||||||
for _, k := range klines { |
|
||||||
r.Push(mapping(k)) |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
@ -0,0 +1,37 @@ |
|||||||
|
package series |
||||||
|
|
||||||
|
import ( |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"sig-pub/pkg/types/decimals" |
||||||
|
"sig-pub/pkg/utils/collect" |
||||||
|
) |
||||||
|
|
||||||
|
type Klines []types.Kline |
||||||
|
|
||||||
|
func (s Klines) Times() []int64 { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) int64 { return k.Ts }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) Open() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.Open) }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) Close() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.Close) }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) High() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.High) }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) Low() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.Low) }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) Vol() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.Vol) }) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Klines) VolQuote() Floats { |
||||||
|
return collect.Mapping(s, func(_ int, k types.Kline) float64 { return decimals.MustToFloat64(k.VolQuote) }) |
||||||
|
} |
||||||
Loading…
Reference in new issue