add cassandra
This commit is contained in:
parent
c4abce7fff
commit
50db76e093
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Launch main.go",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "auto", // 也可選 debug/test/remote
|
|
||||||
"program": "${workspaceFolder}/blockchain.go", // 或你的專案主程式
|
|
||||||
"env": {
|
|
||||||
"GOPATH": "/home/wang/go"
|
|
||||||
},
|
|
||||||
// "args": ["-arg1", "value"]
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"editor.fontFamily": "Fira Code, 'Fira Code', monospace"
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
// goctl 1.8.5
|
// goctl 1.8.1
|
||||||
// Source: blockchain.proto
|
// Source: blockchain.proto
|
||||||
|
|
||||||
package blockchainservice
|
package blockchainservice
|
||||||
|
@ -7,13 +7,14 @@ package blockchainservice
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
app_cloudep_blockchain "blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
"blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/zrpc"
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
HistoryReq = app_cloudep_blockchain.HistoryReq
|
||||||
ListSymbolsRequest = app_cloudep_blockchain.ListSymbolsRequest
|
ListSymbolsRequest = app_cloudep_blockchain.ListSymbolsRequest
|
||||||
ListSymbolsResponse = app_cloudep_blockchain.ListSymbolsResponse
|
ListSymbolsResponse = app_cloudep_blockchain.ListSymbolsResponse
|
||||||
NoneReq = app_cloudep_blockchain.NoneReq
|
NoneReq = app_cloudep_blockchain.NoneReq
|
||||||
|
@ -23,6 +24,7 @@ type (
|
||||||
BlockchainService interface {
|
BlockchainService interface {
|
||||||
// ListSymbols retrieves all available trading symbols.
|
// ListSymbols retrieves all available trading symbols.
|
||||||
ListSymbols(ctx context.Context, in *ListSymbolsRequest, opts ...grpc.CallOption) (*ListSymbolsResponse, error)
|
ListSymbols(ctx context.Context, in *ListSymbolsRequest, opts ...grpc.CallOption) (*ListSymbolsResponse, error)
|
||||||
|
GetHistoryKlineData(ctx context.Context, in *HistoryReq, opts ...grpc.CallOption) (*OKResp, error)
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error)
|
Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error)
|
||||||
}
|
}
|
||||||
|
@ -44,6 +46,11 @@ func (m *defaultBlockchainService) ListSymbols(ctx context.Context, in *ListSymb
|
||||||
return client.ListSymbols(ctx, in, opts...)
|
return client.ListSymbols(ctx, in, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *defaultBlockchainService) GetHistoryKlineData(ctx context.Context, in *HistoryReq, opts ...grpc.CallOption) (*OKResp, error) {
|
||||||
|
client := app_cloudep_blockchain.NewBlockchainServiceClient(m.cli.Conn())
|
||||||
|
return client.GetHistoryKlineData(ctx, in, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
func (m *defaultBlockchainService) Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) {
|
func (m *defaultBlockchainService) Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) {
|
||||||
client := app_cloudep_blockchain.NewBlockchainServiceClient(m.cli.Conn())
|
client := app_cloudep_blockchain.NewBlockchainServiceClient(m.cli.Conn())
|
||||||
|
|
|
@ -12,12 +12,12 @@ Binance:
|
||||||
WorkerSize: 20
|
WorkerSize: 20
|
||||||
|
|
||||||
RedisCluster:
|
RedisCluster:
|
||||||
Host: 127.0.0.1:6379
|
Host: 10.0.0.13:6379
|
||||||
Type: node
|
Type: node
|
||||||
|
|
||||||
Cassandra:
|
Cassandra:
|
||||||
Hosts:
|
Hosts:
|
||||||
- 127.0.0.1
|
- 10.0.0.13
|
||||||
Port: 9042
|
Port: 9042
|
||||||
Keyspace: digimon
|
Keyspace: digimon
|
||||||
UseAuth: true
|
UseAuth: true
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.36.6
|
// protoc-gen-go v1.36.1
|
||||||
// protoc v3.21.12
|
// protoc v3.19.4
|
||||||
// source: generate/rpc/blockchain.proto
|
// source: generate/rpc/blockchain.proto
|
||||||
|
|
||||||
package app_cloudep_blockchain
|
package app_cloudep_blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
unsafe "unsafe"
|
|
||||||
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -264,58 +262,159 @@ func (x *Symbol) GetQuoteAssetPrecision() int32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HistoryReq struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` // 交易對名稱 (BTCUSDT)
|
||||||
|
Interval string `protobuf:"bytes,2,opt,name=interval,proto3" json:"interval,omitempty"` // 4h 1m 1d ...
|
||||||
|
StartTime string `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
|
||||||
|
EndTime string `protobuf:"bytes,4,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) Reset() {
|
||||||
|
*x = HistoryReq{}
|
||||||
|
mi := &file_generate_rpc_blockchain_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HistoryReq) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HistoryReq) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_generate_rpc_blockchain_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HistoryReq.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HistoryReq) Descriptor() ([]byte, []int) {
|
||||||
|
return file_generate_rpc_blockchain_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) GetSymbol() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Symbol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) GetInterval() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Interval
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) GetStartTime() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.StartTime
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HistoryReq) GetEndTime() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.EndTime
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_generate_rpc_blockchain_proto protoreflect.FileDescriptor
|
var File_generate_rpc_blockchain_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_generate_rpc_blockchain_proto_rawDesc = "" +
|
var file_generate_rpc_blockchain_proto_rawDesc = []byte{
|
||||||
"\n" +
|
0x0a, 0x1d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x62,
|
||||||
"\x1dgenerate/rpc/blockchain.proto\x12\n" +
|
0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||||
"blockchain\"\b\n" +
|
0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x08, 0x0a, 0x06, 0x4f,
|
||||||
"\x06OKResp\"\t\n" +
|
0x4b, 0x52, 0x65, 0x73, 0x70, 0x22, 0x09, 0x0a, 0x07, 0x4e, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71,
|
||||||
"\aNoneReq\"\x14\n" +
|
0x22, 0x14, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52,
|
||||||
"\x12ListSymbolsRequest\"C\n" +
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x79,
|
||||||
"\x13ListSymbolsResponse\x12,\n" +
|
0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a,
|
||||||
"\asymbols\x18\x01 \x03(\v2\x12.blockchain.SymbolR\asymbols\"\xde\x01\n" +
|
0x07, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12,
|
||||||
"\x06Symbol\x12\x16\n" +
|
0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x79, 0x6d, 0x62,
|
||||||
"\x06symbol\x18\x01 \x01(\tR\x06symbol\x12\x16\n" +
|
0x6f, 0x6c, 0x52, 0x07, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x06,
|
||||||
"\x06status\x18\x02 \x01(\tR\x06status\x12\x1d\n" +
|
0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
|
||||||
"\n" +
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16,
|
||||||
"base_asset\x18\x03 \x01(\tR\tbaseAsset\x120\n" +
|
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||||
"\x14base_asset_precision\x18\x04 \x01(\x05R\x12baseAssetPrecision\x12\x1f\n" +
|
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x61,
|
||||||
"\vquote_asset\x18\x05 \x01(\tR\n" +
|
0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65,
|
||||||
"quoteAsset\x122\n" +
|
0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x73,
|
||||||
"\x15quote_asset_precision\x18\x06 \x01(\x05R\x13quoteAssetPrecision2\x94\x01\n" +
|
0x73, 0x65, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20,
|
||||||
"\x11BlockchainService\x12N\n" +
|
0x01, 0x28, 0x05, 0x52, 0x12, 0x62, 0x61, 0x73, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72,
|
||||||
"\vListSymbols\x12\x1e.blockchain.ListSymbolsRequest\x1a\x1f.blockchain.ListSymbolsResponse\x12/\n" +
|
0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65,
|
||||||
"\x04Ping\x12\x13.blockchain.NoneReq\x1a\x12.blockchain.OKRespB.Z,code.30cm.net/digimon/app-cloudep-blockchainb\x06proto3"
|
0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x71, 0x75,
|
||||||
|
0x6f, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x71, 0x75, 0x6f, 0x74,
|
||||||
|
0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f,
|
||||||
|
0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x41, 0x73,
|
||||||
|
0x73, 0x65, 0x74, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x0a,
|
||||||
|
0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79,
|
||||||
|
0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62,
|
||||||
|
0x6f, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d,
|
||||||
|
0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a,
|
||||||
|
0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x32, 0xd7, 0x01, 0x0a, 0x11, 0x42, 0x6c, 0x6f,
|
||||||
|
0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e,
|
||||||
|
0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x1e, 0x2e,
|
||||||
|
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
|
||||||
|
0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e,
|
||||||
|
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
|
||||||
|
0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41,
|
||||||
|
0x0a, 0x13, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x6c, 0x69, 0x6e,
|
||||||
|
0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61,
|
||||||
|
0x69, 0x6e, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e,
|
||||||
|
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x4f, 0x4b, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63,
|
||||||
|
0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12,
|
||||||
|
0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x4f, 0x4b, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x42, 0x2e, 0x5a, 0x2c, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x33, 0x30, 0x63, 0x6d, 0x2e,
|
||||||
|
0x6e, 0x65, 0x74, 0x2f, 0x64, 0x69, 0x67, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, 0x61, 0x70, 0x70, 0x2d,
|
||||||
|
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61,
|
||||||
|
0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_generate_rpc_blockchain_proto_rawDescOnce sync.Once
|
file_generate_rpc_blockchain_proto_rawDescOnce sync.Once
|
||||||
file_generate_rpc_blockchain_proto_rawDescData []byte
|
file_generate_rpc_blockchain_proto_rawDescData = file_generate_rpc_blockchain_proto_rawDesc
|
||||||
)
|
)
|
||||||
|
|
||||||
func file_generate_rpc_blockchain_proto_rawDescGZIP() []byte {
|
func file_generate_rpc_blockchain_proto_rawDescGZIP() []byte {
|
||||||
file_generate_rpc_blockchain_proto_rawDescOnce.Do(func() {
|
file_generate_rpc_blockchain_proto_rawDescOnce.Do(func() {
|
||||||
file_generate_rpc_blockchain_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_generate_rpc_blockchain_proto_rawDesc), len(file_generate_rpc_blockchain_proto_rawDesc)))
|
file_generate_rpc_blockchain_proto_rawDescData = protoimpl.X.CompressGZIP(file_generate_rpc_blockchain_proto_rawDescData)
|
||||||
})
|
})
|
||||||
return file_generate_rpc_blockchain_proto_rawDescData
|
return file_generate_rpc_blockchain_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_generate_rpc_blockchain_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_generate_rpc_blockchain_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||||
var file_generate_rpc_blockchain_proto_goTypes = []any{
|
var file_generate_rpc_blockchain_proto_goTypes = []any{
|
||||||
(*OKResp)(nil), // 0: blockchain.OKResp
|
(*OKResp)(nil), // 0: blockchain.OKResp
|
||||||
(*NoneReq)(nil), // 1: blockchain.NoneReq
|
(*NoneReq)(nil), // 1: blockchain.NoneReq
|
||||||
(*ListSymbolsRequest)(nil), // 2: blockchain.ListSymbolsRequest
|
(*ListSymbolsRequest)(nil), // 2: blockchain.ListSymbolsRequest
|
||||||
(*ListSymbolsResponse)(nil), // 3: blockchain.ListSymbolsResponse
|
(*ListSymbolsResponse)(nil), // 3: blockchain.ListSymbolsResponse
|
||||||
(*Symbol)(nil), // 4: blockchain.Symbol
|
(*Symbol)(nil), // 4: blockchain.Symbol
|
||||||
|
(*HistoryReq)(nil), // 5: blockchain.HistoryReq
|
||||||
}
|
}
|
||||||
var file_generate_rpc_blockchain_proto_depIdxs = []int32{
|
var file_generate_rpc_blockchain_proto_depIdxs = []int32{
|
||||||
4, // 0: blockchain.ListSymbolsResponse.symbols:type_name -> blockchain.Symbol
|
4, // 0: blockchain.ListSymbolsResponse.symbols:type_name -> blockchain.Symbol
|
||||||
2, // 1: blockchain.BlockchainService.ListSymbols:input_type -> blockchain.ListSymbolsRequest
|
2, // 1: blockchain.BlockchainService.ListSymbols:input_type -> blockchain.ListSymbolsRequest
|
||||||
1, // 2: blockchain.BlockchainService.Ping:input_type -> blockchain.NoneReq
|
5, // 2: blockchain.BlockchainService.GetHistoryKlineData:input_type -> blockchain.HistoryReq
|
||||||
3, // 3: blockchain.BlockchainService.ListSymbols:output_type -> blockchain.ListSymbolsResponse
|
1, // 3: blockchain.BlockchainService.Ping:input_type -> blockchain.NoneReq
|
||||||
0, // 4: blockchain.BlockchainService.Ping:output_type -> blockchain.OKResp
|
3, // 4: blockchain.BlockchainService.ListSymbols:output_type -> blockchain.ListSymbolsResponse
|
||||||
3, // [3:5] is the sub-list for method output_type
|
0, // 5: blockchain.BlockchainService.GetHistoryKlineData:output_type -> blockchain.OKResp
|
||||||
1, // [1:3] is the sub-list for method input_type
|
0, // 6: blockchain.BlockchainService.Ping:output_type -> blockchain.OKResp
|
||||||
|
4, // [4:7] is the sub-list for method output_type
|
||||||
|
1, // [1:4] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
0, // [0:1] is the sub-list for field type_name
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
@ -330,9 +429,9 @@ func file_generate_rpc_blockchain_proto_init() {
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_generate_rpc_blockchain_proto_rawDesc), len(file_generate_rpc_blockchain_proto_rawDesc)),
|
RawDescriptor: file_generate_rpc_blockchain_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 5,
|
NumMessages: 6,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
@ -341,6 +440,7 @@ func file_generate_rpc_blockchain_proto_init() {
|
||||||
MessageInfos: file_generate_rpc_blockchain_proto_msgTypes,
|
MessageInfos: file_generate_rpc_blockchain_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_generate_rpc_blockchain_proto = out.File
|
File_generate_rpc_blockchain_proto = out.File
|
||||||
|
file_generate_rpc_blockchain_proto_rawDesc = nil
|
||||||
file_generate_rpc_blockchain_proto_goTypes = nil
|
file_generate_rpc_blockchain_proto_goTypes = nil
|
||||||
file_generate_rpc_blockchain_proto_depIdxs = nil
|
file_generate_rpc_blockchain_proto_depIdxs = nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.5.1
|
// - protoc-gen-go-grpc v1.5.1
|
||||||
// - protoc v3.21.12
|
// - protoc v3.19.4
|
||||||
// source: generate/rpc/blockchain.proto
|
// source: generate/rpc/blockchain.proto
|
||||||
|
|
||||||
package app_cloudep_blockchain
|
package app_cloudep_blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
codes "google.golang.org/grpc/codes"
|
codes "google.golang.org/grpc/codes"
|
||||||
status "google.golang.org/grpc/status"
|
status "google.golang.org/grpc/status"
|
||||||
|
@ -21,6 +20,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BlockchainService_ListSymbols_FullMethodName = "/blockchain.BlockchainService/ListSymbols"
|
BlockchainService_ListSymbols_FullMethodName = "/blockchain.BlockchainService/ListSymbols"
|
||||||
|
BlockchainService_GetHistoryKlineData_FullMethodName = "/blockchain.BlockchainService/GetHistoryKlineData"
|
||||||
BlockchainService_Ping_FullMethodName = "/blockchain.BlockchainService/Ping"
|
BlockchainService_Ping_FullMethodName = "/blockchain.BlockchainService/Ping"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ const (
|
||||||
type BlockchainServiceClient interface {
|
type BlockchainServiceClient interface {
|
||||||
// ListSymbols retrieves all available trading symbols.
|
// ListSymbols retrieves all available trading symbols.
|
||||||
ListSymbols(ctx context.Context, in *ListSymbolsRequest, opts ...grpc.CallOption) (*ListSymbolsResponse, error)
|
ListSymbols(ctx context.Context, in *ListSymbolsRequest, opts ...grpc.CallOption) (*ListSymbolsResponse, error)
|
||||||
|
GetHistoryKlineData(ctx context.Context, in *HistoryReq, opts ...grpc.CallOption) (*OKResp, error)
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error)
|
Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +53,16 @@ func (c *blockchainServiceClient) ListSymbols(ctx context.Context, in *ListSymbo
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *blockchainServiceClient) GetHistoryKlineData(ctx context.Context, in *HistoryReq, opts ...grpc.CallOption) (*OKResp, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(OKResp)
|
||||||
|
err := c.cc.Invoke(ctx, BlockchainService_GetHistoryKlineData_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *blockchainServiceClient) Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) {
|
func (c *blockchainServiceClient) Ping(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
out := new(OKResp)
|
out := new(OKResp)
|
||||||
|
@ -68,6 +79,7 @@ func (c *blockchainServiceClient) Ping(ctx context.Context, in *NoneReq, opts ..
|
||||||
type BlockchainServiceServer interface {
|
type BlockchainServiceServer interface {
|
||||||
// ListSymbols retrieves all available trading symbols.
|
// ListSymbols retrieves all available trading symbols.
|
||||||
ListSymbols(context.Context, *ListSymbolsRequest) (*ListSymbolsResponse, error)
|
ListSymbols(context.Context, *ListSymbolsRequest) (*ListSymbolsResponse, error)
|
||||||
|
GetHistoryKlineData(context.Context, *HistoryReq) (*OKResp, error)
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
Ping(context.Context, *NoneReq) (*OKResp, error)
|
Ping(context.Context, *NoneReq) (*OKResp, error)
|
||||||
mustEmbedUnimplementedBlockchainServiceServer()
|
mustEmbedUnimplementedBlockchainServiceServer()
|
||||||
|
@ -83,6 +95,9 @@ type UnimplementedBlockchainServiceServer struct{}
|
||||||
func (UnimplementedBlockchainServiceServer) ListSymbols(context.Context, *ListSymbolsRequest) (*ListSymbolsResponse, error) {
|
func (UnimplementedBlockchainServiceServer) ListSymbols(context.Context, *ListSymbolsRequest) (*ListSymbolsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListSymbols not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListSymbols not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedBlockchainServiceServer) GetHistoryKlineData(context.Context, *HistoryReq) (*OKResp, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetHistoryKlineData not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedBlockchainServiceServer) Ping(context.Context, *NoneReq) (*OKResp, error) {
|
func (UnimplementedBlockchainServiceServer) Ping(context.Context, *NoneReq) (*OKResp, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||||
}
|
}
|
||||||
|
@ -125,6 +140,24 @@ func _BlockchainService_ListSymbols_Handler(srv interface{}, ctx context.Context
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _BlockchainService_GetHistoryKlineData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(HistoryReq)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BlockchainServiceServer).GetHistoryKlineData(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: BlockchainService_GetHistoryKlineData_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BlockchainServiceServer).GetHistoryKlineData(ctx, req.(*HistoryReq))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _BlockchainService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _BlockchainService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(NoneReq)
|
in := new(NoneReq)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
|
@ -154,6 +187,10 @@ var BlockchainService_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "ListSymbols",
|
MethodName: "ListSymbols",
|
||||||
Handler: _BlockchainService_ListSymbols_Handler,
|
Handler: _BlockchainService_ListSymbols_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetHistoryKlineData",
|
||||||
|
Handler: _BlockchainService_GetHistoryKlineData_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "Ping",
|
MethodName: "Ping",
|
||||||
Handler: _BlockchainService_Ping_Handler,
|
Handler: _BlockchainService_Ping_Handler,
|
||||||
|
|
|
@ -26,9 +26,17 @@ message Symbol {
|
||||||
int32 quote_asset_precision = 6; // 報價資產顯示的小數位數
|
int32 quote_asset_precision = 6; // 報價資產顯示的小數位數
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message HistoryReq {
|
||||||
|
string symbol = 1; // 交易對名稱 (BTCUSDT)
|
||||||
|
string interval =2; // 4h 1m 1d ...
|
||||||
|
string start_time=3;
|
||||||
|
string end_time=4;
|
||||||
|
}
|
||||||
|
|
||||||
service BlockchainService{
|
service BlockchainService{
|
||||||
// ListSymbols retrieves all available trading symbols.
|
// ListSymbols retrieves all available trading symbols.
|
||||||
rpc ListSymbols(ListSymbolsRequest) returns(ListSymbolsResponse);
|
rpc ListSymbols(ListSymbolsRequest) returns(ListSymbolsResponse);
|
||||||
|
rpc GetHistoryKlineData(HistoryReq)returns(OKResp);
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
rpc Ping(NoneReq) returns(OKResp);
|
rpc Ping(NoneReq) returns(OKResp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,5 @@ const CodeBlockchain uint32 = 10
|
||||||
const (
|
const (
|
||||||
FailedToGetSymbolFormBinanceErrorCode errs.ErrorCode = 1
|
FailedToGetSymbolFormBinanceErrorCode errs.ErrorCode = 1
|
||||||
FailedToUpsertBinanceErrorCode errs.ErrorCode = 2
|
FailedToUpsertBinanceErrorCode errs.ErrorCode = 2
|
||||||
|
FailedToListBinanceKlineErrorCode errs.ErrorCode = 3
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,11 +14,12 @@ type Downloader interface {
|
||||||
// FetchHistoryKline 抓歷史 K 線資料
|
// FetchHistoryKline 抓歷史 K 線資料
|
||||||
FetchHistoryKline(ctx context.Context, param QueryKline) ([]*entity.Kline, error)
|
FetchHistoryKline(ctx context.Context, param QueryKline) ([]*entity.Kline, error)
|
||||||
SaveHistoryKline(ctx context.Context, data []*entity.Kline) error
|
SaveHistoryKline(ctx context.Context, data []*entity.Kline) error
|
||||||
|
GetKline(ctx context.Context, param QueryKline) ([]entity.Kline, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryKline struct {
|
type QueryKline struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
Interval string
|
Interval string
|
||||||
StartUnixNano int64
|
StartTime int64
|
||||||
EndUnixNano int64
|
EndTime int64
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
type DataSourceUseCase interface {
|
type DataSourceUseCase interface {
|
||||||
GetSymbols(ctx context.Context) ([]*Symbol, error)
|
GetSymbols(ctx context.Context) ([]*Symbol, error)
|
||||||
UpsertKline(ctx context.Context, data QueryKline) error
|
UpsertKline(ctx context.Context, data QueryKline) error
|
||||||
|
ListKline(ctx context.Context, param QueryKline) ([]Candle, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Symbol 代表交易對資訊
|
// Symbol 代表交易對資訊
|
||||||
|
@ -22,6 +23,23 @@ type Symbol struct {
|
||||||
type QueryKline struct {
|
type QueryKline struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
Interval string
|
Interval string
|
||||||
StartUnixNano int64
|
StartTime int64
|
||||||
EndUnixNano int64
|
EndTime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Candle struct {
|
||||||
|
OpenTime int64 `json:"open_time"` // 開盤時間(毫秒),clustering key,用於時序查詢
|
||||||
|
Open string `json:"open"` // 開盤價
|
||||||
|
High string `json:"high"` // 最高價
|
||||||
|
Low string `json:"low"` // 最低價
|
||||||
|
Close string `json:"close"` // 收盤價
|
||||||
|
Volume string `json:"volume" ` // 成交量
|
||||||
|
CloseTime int64 `json:"close_time" ` // 收盤時間(毫秒)
|
||||||
|
QuoteAssetVolume string `json:"quote_asset_volume" ` // 成交額(以報價資產計)
|
||||||
|
NumberOfTrades int `json:"number_of_trades"` // 交易筆數
|
||||||
|
TakerBuyBaseAssetVolume string `json:"taker_buy_base_asset_volume"` // 主動買入成交量
|
||||||
|
TakerBuyQuoteAssetVolume string `json:"taker_buy_quote_asset_volume"` // 主動買入成交額
|
||||||
|
Symbol string `json:"symbol"` // 交易對,partition key
|
||||||
|
// K 線時間區間,partition key // 12h,15m,1d,1h,1m,1s,2h,30m,3m,4h,5m,6h,8h
|
||||||
|
Interval string `json:"interval"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import "github.com/shopspring/decimal"
|
||||||
|
|
||||||
|
/************** EMA 指數移動平均 **************/
|
||||||
|
type EMA struct {
|
||||||
|
n int
|
||||||
|
alp decimal.Decimal // 平滑係數 α = 2 / (n + 1)
|
||||||
|
val decimal.Decimal // 當前EMA值
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEMA(n int) *EMA {
|
||||||
|
return &EMA{n: n, alp: decimal.NewFromFloat(2.0).Div(decimal.NewFromInt(int64(n + 1)))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EMA) Push(close decimal.Decimal) (decimal.Decimal, bool) {
|
||||||
|
if !e.ok {
|
||||||
|
// 第一筆資料直接當作EMA初始值
|
||||||
|
e.val = close
|
||||||
|
e.ok = true
|
||||||
|
return e.val, false
|
||||||
|
}
|
||||||
|
// EMA計算公式
|
||||||
|
e.val = e.alp.Mul(close).Add(decimal.NewFromInt(1).Sub(e.alp).Mul(e.val))
|
||||||
|
return e.val, true
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CandleForStrategy 表示一根K線資料
|
||||||
|
type CandleForStrategy struct {
|
||||||
|
T time.Time // T:時間
|
||||||
|
// O:開盤價
|
||||||
|
// H:最高價
|
||||||
|
// L:最低價
|
||||||
|
// C:收盤價
|
||||||
|
O, H, L, C decimal.Decimal
|
||||||
|
V decimal.Decimal // V:成交量
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
/************** 基礎的固定長度隊列,用來計算移動平均 **************/
|
||||||
|
type ringQD struct {
|
||||||
|
N int // 窗口大小(需要保留的資料數量)
|
||||||
|
l *list.List // 用於儲存資料的雙向鏈表
|
||||||
|
sum decimal.Decimal // 當前窗口的總和,方便快速計算平均值
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建立一個固定長度的隊列
|
||||||
|
func newRingQD(n int) *ringQD {
|
||||||
|
return &ringQD{N: n, l: list.New(), sum: decimal.Zero}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push:將新的數值放入隊列,並維護總和
|
||||||
|
func (q *ringQD) push(x decimal.Decimal) {
|
||||||
|
q.l.PushBack(x)
|
||||||
|
q.sum = q.sum.Add(x)
|
||||||
|
// 如果超出最大長度,移除最舊的數值
|
||||||
|
if q.l.Len() > q.N {
|
||||||
|
f := q.l.Front()
|
||||||
|
q.sum = q.sum.Sub(f.Value.(decimal.Decimal))
|
||||||
|
q.l.Remove(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ready:判斷隊列是否已經填滿
|
||||||
|
func (q *ringQD) ready() bool { return q.l.Len() == q.N }
|
||||||
|
|
||||||
|
// mean:計算平均值
|
||||||
|
func (q *ringQD) mean() decimal.Decimal {
|
||||||
|
if q.l.Len() == 0 {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
return q.sum.Div(decimal.NewFromInt(int64(q.l.Len())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// values:返回隊列中所有的值(複製一份,不影響原資料)
|
||||||
|
func (q *ringQD) values() []decimal.Decimal {
|
||||||
|
out := make([]decimal.Decimal, 0, q.l.Len())
|
||||||
|
for e := q.l.Front(); e != nil; e = e.Next() {
|
||||||
|
out = append(out, e.Value.(decimal.Decimal))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- 表格式驅動測試 (Table-Driven Test) ---
|
||||||
|
|
||||||
|
func TestRingQD(t *testing.T) {
|
||||||
|
// 為了方便測試,預先建立幾個 decimal 數值
|
||||||
|
d10 := decimal.NewFromInt(10)
|
||||||
|
d20 := decimal.NewFromInt(20)
|
||||||
|
d30 := decimal.NewFromInt(30)
|
||||||
|
d40 := decimal.NewFromInt(40)
|
||||||
|
d50 := decimal.NewFromInt(50)
|
||||||
|
d_neg5 := decimal.NewFromInt(-5)
|
||||||
|
|
||||||
|
// 定義測試案例的結構
|
||||||
|
testCases := []struct {
|
||||||
|
name string // 測試案例的名稱
|
||||||
|
n int // ringQD 的大小
|
||||||
|
inputs []decimal.Decimal // 輸入的數值序列
|
||||||
|
wantSum decimal.Decimal // 預期的總和
|
||||||
|
wantMean decimal.Decimal // 預期的平均值
|
||||||
|
wantValues []decimal.Decimal // 預期隊列中最後的值
|
||||||
|
wantReady bool // 預期的就緒狀態
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "未滿載的情況",
|
||||||
|
n: 5,
|
||||||
|
inputs: []decimal.Decimal{d10, d20},
|
||||||
|
wantSum: decimal.NewFromInt(30),
|
||||||
|
wantMean: decimal.NewFromInt(15),
|
||||||
|
wantValues: []decimal.Decimal{d10, d20},
|
||||||
|
wantReady: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "剛好滿載的情況",
|
||||||
|
n: 3,
|
||||||
|
inputs: []decimal.Decimal{d10, d20, d30},
|
||||||
|
wantSum: decimal.NewFromInt(60),
|
||||||
|
wantMean: decimal.NewFromInt(20),
|
||||||
|
wantValues: []decimal.Decimal{d10, d20, d30},
|
||||||
|
wantReady: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "超出容量,舊資料被移除",
|
||||||
|
n: 3,
|
||||||
|
inputs: []decimal.Decimal{d10, d20, d30, d40, d50},
|
||||||
|
wantSum: decimal.NewFromInt(120), // 30 + 40 + 50
|
||||||
|
wantMean: decimal.NewFromInt(40),
|
||||||
|
wantValues: []decimal.Decimal{d30, d40, d50},
|
||||||
|
wantReady: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "包含零與負數",
|
||||||
|
n: 4,
|
||||||
|
inputs: []decimal.Decimal{d10, d_neg5, decimal.Zero, d30, d_neg5},
|
||||||
|
wantSum: decimal.NewFromInt(20), // -5 + 0 + 30 + (-5)
|
||||||
|
wantMean: decimal.NewFromInt(5),
|
||||||
|
wantValues: []decimal.Decimal{d_neg5, decimal.Zero, d30, d_neg5},
|
||||||
|
wantReady: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "初始為空",
|
||||||
|
n: 5,
|
||||||
|
inputs: []decimal.Decimal{},
|
||||||
|
wantSum: decimal.Zero,
|
||||||
|
wantMean: decimal.Zero,
|
||||||
|
wantValues: []decimal.Decimal{},
|
||||||
|
wantReady: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "N 為 1 的邊界情況",
|
||||||
|
n: 1,
|
||||||
|
inputs: []decimal.Decimal{d10, d20, d30},
|
||||||
|
wantSum: d30,
|
||||||
|
wantMean: d30,
|
||||||
|
wantValues: []decimal.Decimal{d30},
|
||||||
|
wantReady: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "N 為 0 的無效情況",
|
||||||
|
n: 0,
|
||||||
|
inputs: []decimal.Decimal{d10, d20, d30},
|
||||||
|
wantSum: decimal.Zero,
|
||||||
|
wantMean: decimal.Zero,
|
||||||
|
wantValues: []decimal.Decimal{},
|
||||||
|
wantReady: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍歷所有測試案例
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// 針對每個案例建立一個新的 ringQD
|
||||||
|
q := newRingQD(tc.n)
|
||||||
|
|
||||||
|
// 依序推入資料
|
||||||
|
for _, val := range tc.inputs {
|
||||||
|
q.push(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證總和
|
||||||
|
if !q.sum.Equals(tc.wantSum) {
|
||||||
|
t.Errorf("Sum 錯誤:got %v, want %v", q.sum, tc.wantSum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證平均值
|
||||||
|
gotMean := q.mean()
|
||||||
|
if !gotMean.Equals(tc.wantMean) {
|
||||||
|
t.Errorf("Mean 錯誤:got %v, want %v", gotMean, tc.wantMean)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證就緒狀態
|
||||||
|
if q.ready() != tc.wantReady {
|
||||||
|
t.Errorf("Ready 狀態錯誤:got %v, want %v", q.ready(), tc.wantReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證隊列中的值
|
||||||
|
gotValues := q.values()
|
||||||
|
if !reflect.DeepEqual(gotValues, tc.wantValues) {
|
||||||
|
t.Errorf("Values 錯誤:got %v, want %v", gotValues, tc.wantValues)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import "github.com/shopspring/decimal"
|
||||||
|
|
||||||
|
/* SMA (Simple Moving Average) 簡單移動平均線。 它透過計算一段時間內股價的平均值,來判斷趨勢和提供交易信號。
|
||||||
|
|
||||||
|
SMA 的計算方式:
|
||||||
|
SMA 簡單地將一段時間內的收盤價加總,再除以該期間的交易日數。 例如,5 日SMA 就是將過去 5 個交易日的收盤價加總後除以 5。
|
||||||
|
SMA 的應用:
|
||||||
|
1. 趨勢判斷:
|
||||||
|
SMA 可以用來判斷價格的趨勢。 當股價在 SMA 上方,且 SMA 向上移動時,表示股價處於上升趨勢;
|
||||||
|
反之,當股價在 SMA 下方,且SMA 向下移動時,表示股價處於下降趨勢。
|
||||||
|
2. 支撐與阻力:
|
||||||
|
SMA 也可以被視為支撐位和阻力位。 當股價下跌到SMA 附近時,SMA 可能會提供支撐;而當股價上漲到SMA 附近時,SMA 可能會提供阻力。
|
||||||
|
3. 交易信號:
|
||||||
|
移動平均線的交叉也可以產生交易信號。 例如,當短期SMA 線向上穿越長期SMA 線時,被稱為黃金交叉,可能是一個買入信號;
|
||||||
|
反之,當短期SMA 線向下穿越長期SMA 線時,被稱為死亡交叉,可能是一個賣出信號。
|
||||||
|
SMA 的優缺點:
|
||||||
|
優點: SMA 計算簡單、易於理解,適合新手使用。
|
||||||
|
缺點: SMA 對於價格變動的反應較為遲鈍,可能會落後於市場,特別是短期波動時,可能不如其他移動平均線指標準確。
|
||||||
|
*/
|
||||||
|
|
||||||
|
type SMA struct {
|
||||||
|
q *ringQD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSMA 建立SMA計算器
|
||||||
|
func NewSMA(n int) *SMA { return &SMA{q: newRingQD(n)} }
|
||||||
|
|
||||||
|
// Push 輸入收盤價,返回當前SMA值
|
||||||
|
func (s *SMA) Push(close decimal.Decimal) (decimal.Decimal, bool) {
|
||||||
|
s.q.push(close)
|
||||||
|
if !s.q.ready() {
|
||||||
|
return decimal.Zero, false // 尚未湊滿資料
|
||||||
|
}
|
||||||
|
return s.q.mean(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSMA 取得目前 SMA 值
|
||||||
|
func (s *SMA) GetSMA() (decimal.Decimal, bool) {
|
||||||
|
if !s.q.ready() {
|
||||||
|
return decimal.Zero, false // 尚未湊滿資料
|
||||||
|
}
|
||||||
|
return s.q.mean(), true
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package blockchainservicelogic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blockchain/internal/domain/usecase"
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
||||||
|
"blockchain/internal/svc"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetHistoryKlineDataLogic struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
logx.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetHistoryKlineDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetHistoryKlineDataLogic {
|
||||||
|
return &GetHistoryKlineDataLogic{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetHistoryKlineDataLogic) GetHistoryKlineData(in *app_cloudep_blockchain.HistoryReq) (*app_cloudep_blockchain.OKResp, error) {
|
||||||
|
go func() {
|
||||||
|
st, _ := time.Parse(time.RFC3339, in.GetStartTime())
|
||||||
|
et, _ := time.Parse(time.RFC3339, in.GetEndTime())
|
||||||
|
_ = l.svcCtx.BinanceDataSource.UpsertKline(context.Background(), usecase.QueryKline{
|
||||||
|
Symbol: in.GetSymbol(),
|
||||||
|
Interval: in.GetInterval(),
|
||||||
|
StartTime: st.UnixNano(),
|
||||||
|
EndTime: et.UnixNano(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &app_cloudep_blockchain.OKResp{}, nil
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
package blockchainservicelogic
|
package blockchainservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"blockchain/internal/domain/usecase"
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
app_cloudep_blockchain "blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
app_cloudep_blockchain "blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
||||||
|
"blockchain/internal/domain/usecase"
|
||||||
"blockchain/internal/svc"
|
"blockchain/internal/svc"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
@ -26,11 +25,12 @@ func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *PingLogic) Ping(_ *app_cloudep_blockchain.NoneReq) (*app_cloudep_blockchain.OKResp, error) {
|
func (l *PingLogic) Ping(_ *app_cloudep_blockchain.NoneReq) (*app_cloudep_blockchain.OKResp, error) {
|
||||||
err := l.svcCtx.BinanceDataSource.UpsertKline(l.ctx, usecase.QueryKline{
|
|
||||||
Symbol: "BTCUSDT",
|
kline, err := l.svcCtx.BinanceDataSource.ListKline(l.ctx, usecase.QueryKline{
|
||||||
Interval: "1m",
|
Symbol: "ETHUSDT",
|
||||||
StartUnixNano: time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC).UnixNano(),
|
Interval: "1d",
|
||||||
EndUnixNano: time.Date(2025, 8, 2, 0, 0, 0, 0, time.UTC).UnixNano(),
|
StartTime: 1502928000000,
|
||||||
|
EndTime: 1503964800000,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
|
"github.com/scylladb/gocqlx/v3/qb"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -140,38 +141,24 @@ func (repo *BinanceRepository) GetSymbols(ctx context.Context) ([]*entity.Symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *BinanceRepository) FetchHistoryKline(ctx context.Context, param repository.QueryKline) ([]*entity.Kline, error) {
|
func (repo *BinanceRepository) FetchHistoryKline(ctx context.Context, param repository.QueryKline) ([]*entity.Kline, error) {
|
||||||
ch := make(chan []*entity.Kline, repo.workerSize)
|
start := time.Unix(0, param.StartTime)
|
||||||
var wg sync.WaitGroup
|
end := time.Unix(0, param.EndTime)
|
||||||
|
|
||||||
|
var allKLines []*entity.Kline
|
||||||
|
|
||||||
start := time.Unix(0, param.StartUnixNano)
|
|
||||||
end := time.Unix(0, param.EndUnixNano)
|
|
||||||
// 產生所有天的任務
|
// 產生所有天的任務
|
||||||
for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
|
for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
|
||||||
day := d
|
day := d
|
||||||
wg.Add(1)
|
a, err := repo.fetchHistoryKline(ctx, param.Symbol, param.Interval, day.Format(time.DateOnly))
|
||||||
_ = repo.workers.Submit(func() {
|
if err != nil {
|
||||||
defer wg.Done()
|
logx.Errorf("failed to get history of kline : %v", err)
|
||||||
klines, err := repo.fetchHistoryKline(ctx, param.Symbol, param.Interval, day.Format(time.DateOnly))
|
|
||||||
if err == nil && len(klines) > 0 {
|
continue
|
||||||
ch <- klines // 只要拿到資料就丟進 channel
|
|
||||||
}
|
}
|
||||||
// 沒資料不用丟,避免 nil append
|
allKLines = append(allKLines, a...)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等全部任務完成再關閉 channel
|
return allKLines, nil
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 收集所有 K 線
|
|
||||||
var allKlines []*entity.Kline
|
|
||||||
for klines := range ch {
|
|
||||||
allKlines = append(allKlines, klines...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return allKlines, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *BinanceRepository) SaveHistoryKline(ctx context.Context, data []*entity.Kline) error {
|
func (repo *BinanceRepository) SaveHistoryKline(ctx context.Context, data []*entity.Kline) error {
|
||||||
|
@ -187,7 +174,6 @@ func (repo *BinanceRepository) SaveHistoryKline(ctx context.Context, data []*ent
|
||||||
go func(k *entity.Kline) {
|
go func(k *entity.Kline) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer func() { <-ch }()
|
defer func() { <-ch }()
|
||||||
|
|
||||||
if err := repo.db.Insert(ctx, k, repo.KeySpace); err != nil {
|
if err := repo.db.Insert(ctx, k, repo.KeySpace); err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
errList = append(errList, err)
|
errList = append(errList, err)
|
||||||
|
@ -206,6 +192,35 @@ func (repo *BinanceRepository) SaveHistoryKline(ctx context.Context, data []*ent
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *BinanceRepository) GetKline(ctx context.Context, param repository.QueryKline) ([]entity.Kline, error) {
|
||||||
|
e := entity.Kline{}
|
||||||
|
stmt, names := qb.Select(fmt.Sprintf("%s.%s", repo.KeySpace, e.TableName())).
|
||||||
|
Columns(
|
||||||
|
"symbol", "interval", "open_time", "open", "high", "low", "close",
|
||||||
|
"volume", "close_time", "quote_asset_volume", "number_of_trades",
|
||||||
|
"taker_buy_base_asset_volume", "taker_buy_quote_asset_volume",
|
||||||
|
).
|
||||||
|
Where(
|
||||||
|
qb.Eq("symbol"),
|
||||||
|
qb.Eq("interval"),
|
||||||
|
qb.GtOrEqNamed("open_time", "start"),
|
||||||
|
qb.LtOrEqNamed("open_time", "end"),
|
||||||
|
).ToCql()
|
||||||
|
|
||||||
|
var result []entity.Kline
|
||||||
|
err := repo.db.GetSession().Query(stmt, names).BindMap(qb.M{
|
||||||
|
"symbol": param.Symbol,
|
||||||
|
"interval": param.Interval,
|
||||||
|
"start": param.StartTime,
|
||||||
|
"end": param.EndTime,
|
||||||
|
}).SelectRelease(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// =============
|
// =============
|
||||||
func (repo *BinanceRepository) getSymbolsFromSource(ctx context.Context) ([]binance.Symbol, error) {
|
func (repo *BinanceRepository) getSymbolsFromSource(ctx context.Context) ([]binance.Symbol, error) {
|
||||||
if repo.Client == nil {
|
if repo.Client == nil {
|
||||||
|
@ -229,11 +244,7 @@ func (repo *BinanceRepository) fetchHistoryKline(ctx context.Context, symbol str
|
||||||
if err := check(ctx, url); err != nil {
|
if err := check(ctx, url); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// 這個 URL 只可能指向 binance.vision 官方站,已限定字串組合,不可能被用戶控制。
|
|
||||||
// #nosec G107
|
|
||||||
// 下載 zip
|
|
||||||
// 這個 URL 只可能指向 binance.vision 官方站,已限定字串組合,不可能被用戶控制。
|
|
||||||
// #nosec G107
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -274,7 +285,6 @@ func (repo *BinanceRepository) fetchHistoryKline(ctx context.Context, symbol str
|
||||||
"quote_asset_volume", "number_of_trades", "taker_buy_base_asset_volume",
|
"quote_asset_volume", "number_of_trades", "taker_buy_base_asset_volume",
|
||||||
"taker_buy_quote_asset_volume", "ignore",
|
"taker_buy_quote_asset_volume", "ignore",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range r.File {
|
for _, f := range r.File {
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -293,11 +303,11 @@ func (repo *BinanceRepository) fetchHistoryKline(ctx context.Context, symbol str
|
||||||
if err != nil || len(record) < 12 {
|
if err != nil || len(record) < 12 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
logx.Infof("record: %s", record)
|
||||||
_ = writer.Write(record)
|
_ = writer.Write(record)
|
||||||
}
|
}
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
rc.Close()
|
rc.Close()
|
||||||
|
|
||||||
// csvutil parse
|
// csvutil parse
|
||||||
var klines []*entity.Kline
|
var klines []*entity.Kline
|
||||||
if err := csvutil.Unmarshal(buf.Bytes(), &klines); err != nil {
|
if err := csvutil.Unmarshal(buf.Bytes(), &klines); err != nil {
|
||||||
|
|
|
@ -102,8 +102,8 @@ func TestSymbol(t *testing.T) {
|
||||||
k, err := repo.FetchHistoryKline(context.Background(), repository.QueryKline{
|
k, err := repo.FetchHistoryKline(context.Background(), repository.QueryKline{
|
||||||
Symbol: "BTCUSDT",
|
Symbol: "BTCUSDT",
|
||||||
Interval: "1m",
|
Interval: "1m",
|
||||||
StartUnixNano: time.Date(2025, 8, 3, 0, 0, 0, 0, time.UTC).UnixNano(),
|
StartTime: time.Date(2025, 8, 3, 0, 0, 0, 0, time.UTC).UnixNano(),
|
||||||
EndUnixNano: time.Date(2025, 8, 4, 0, 0, 0, 0, time.UTC).UnixNano(),
|
EndTime: time.Date(2025, 8, 4, 0, 0, 0, 0, time.UTC).UnixNano(),
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
for _, item := range k {
|
for _, item := range k {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
// goctl 1.8.5
|
// goctl 1.8.1
|
||||||
// Source: blockchain.proto
|
// Source: blockchain.proto
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
@ -7,8 +7,8 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
app_cloudep_blockchain "blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
"blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
||||||
blockchainservicelogic "blockchain/internal/logic/blockchainservice"
|
"blockchain/internal/logic/blockchainservice"
|
||||||
"blockchain/internal/svc"
|
"blockchain/internal/svc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +29,11 @@ func (s *BlockchainServiceServer) ListSymbols(ctx context.Context, in *app_cloud
|
||||||
return l.ListSymbols(in)
|
return l.ListSymbols(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BlockchainServiceServer) GetHistoryKlineData(ctx context.Context, in *app_cloudep_blockchain.HistoryReq) (*app_cloudep_blockchain.OKResp, error) {
|
||||||
|
l := blockchainservicelogic.NewGetHistoryKlineDataLogic(ctx, s.svcCtx)
|
||||||
|
return l.GetHistoryKlineData(in)
|
||||||
|
}
|
||||||
|
|
||||||
// Ping is a health-check endpoint.
|
// Ping is a health-check endpoint.
|
||||||
func (s *BlockchainServiceServer) Ping(ctx context.Context, in *app_cloudep_blockchain.NoneReq) (*app_cloudep_blockchain.OKResp, error) {
|
func (s *BlockchainServiceServer) Ping(ctx context.Context, in *app_cloudep_blockchain.NoneReq) (*app_cloudep_blockchain.OKResp, error) {
|
||||||
l := blockchainservicelogic.NewPingLogic(ctx, s.svcCtx)
|
l := blockchainservicelogic.NewPingLogic(ctx, s.svcCtx)
|
||||||
|
|
|
@ -32,7 +32,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
DB: cassandra,
|
DB: cassandra,
|
||||||
KeySpace: c.Cassandra.Keyspace,
|
KeySpace: c.Cassandra.Keyspace,
|
||||||
})
|
})
|
||||||
InitBinanceKLineWebsocket()
|
// InitBinanceKLineWebsocket()
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
BinanceRepo: binanceRepo,
|
BinanceRepo: binanceRepo,
|
||||||
|
|
|
@ -4,9 +4,8 @@ import (
|
||||||
"blockchain/internal/domain/blockchain"
|
"blockchain/internal/domain/blockchain"
|
||||||
"blockchain/internal/domain/repository"
|
"blockchain/internal/domain/repository"
|
||||||
"blockchain/internal/domain/usecase"
|
"blockchain/internal/domain/usecase"
|
||||||
"context"
|
|
||||||
|
|
||||||
"code.30cm.net/digimon/library-go/errs"
|
"code.30cm.net/digimon/library-go/errs"
|
||||||
|
"context"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,8 +59,8 @@ func (use *BinanceUseCase) UpsertKline(ctx context.Context, data usecase.QueryKl
|
||||||
origianData, err := use.BinanceRepo.FetchHistoryKline(ctx, repository.QueryKline{
|
origianData, err := use.BinanceRepo.FetchHistoryKline(ctx, repository.QueryKline{
|
||||||
Symbol: data.Symbol,
|
Symbol: data.Symbol,
|
||||||
Interval: data.Interval,
|
Interval: data.Interval,
|
||||||
StartUnixNano: data.StartUnixNano,
|
StartTime: data.StartTime,
|
||||||
EndUnixNano: data.EndUnixNano,
|
EndTime: data.EndTime,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := errs.ThirdPartyErrorL(
|
e := errs.ThirdPartyErrorL(
|
||||||
|
@ -94,3 +93,47 @@ func (use *BinanceUseCase) UpsertKline(ctx context.Context, data usecase.QueryKl
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (use *BinanceUseCase) ListKline(ctx context.Context, param usecase.QueryKline) ([]usecase.Candle, error) {
|
||||||
|
kline, err := use.BinanceRepo.GetKline(ctx, repository.QueryKline{
|
||||||
|
Symbol: param.Symbol,
|
||||||
|
Interval: param.Interval,
|
||||||
|
StartTime: param.StartTime,
|
||||||
|
EndTime: param.EndTime,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e := errs.DatabaseErrorWithScopeL(
|
||||||
|
blockchain.CodeBlockchain,
|
||||||
|
blockchain.FailedToListBinanceKlineErrorCode,
|
||||||
|
logx.WithContext(ctx),
|
||||||
|
[]logx.LogField{
|
||||||
|
{Key: "func", Value: "BinanceRepo.GetKline"},
|
||||||
|
{Key: "err", Value: err.Error()},
|
||||||
|
},
|
||||||
|
"failed get kline data from db").Wrap(err)
|
||||||
|
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
candle := make([]usecase.Candle, 0, len(kline))
|
||||||
|
for _, item := range kline {
|
||||||
|
candle = append(candle, usecase.Candle{
|
||||||
|
OpenTime: item.OpenTime,
|
||||||
|
Open: item.Open,
|
||||||
|
High: item.High,
|
||||||
|
Low: item.Low,
|
||||||
|
Close: item.Close,
|
||||||
|
Volume: item.Volume,
|
||||||
|
CloseTime: item.CloseTime,
|
||||||
|
QuoteAssetVolume: item.QuoteAssetVolume,
|
||||||
|
NumberOfTrades: item.NumberOfTrades,
|
||||||
|
TakerBuyBaseAssetVolume: item.TakerBuyBaseAssetVolume,
|
||||||
|
TakerBuyQuoteAssetVolume: item.TakerBuyQuoteAssetVolume,
|
||||||
|
Symbol: item.Symbol,
|
||||||
|
Interval: item.Interval,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return candle, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue