Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
c1cf3bd2
Commit
c1cf3bd2
authored
May 12, 2025
by
vicotor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add kline generator
parent
7b933c41
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1438 additions
and
1 deletion
+1438
-1
server.go
exchain/exchainapi/server.go
+0
-1
klinegenerator.go
exchain/exmonitor/klinegenerator.go
+114
-0
markethandle.go
exchain/exmonitor/markethandle.go
+33
-0
marketservice.go
exchain/exmonitor/marketservice.go
+181
-0
monitor.go
exchain/exmonitor/monitor.go
+24
-0
processor.go
exchain/exmonitor/processor.go
+295
-0
orderbook.proto
exchain/protocol/proto/orderbook/v1/orderbook.proto
+785
-0
go.mod
go.mod
+2
-0
go.sum
go.sum
+4
-0
No files found.
exchain/exchainapi/server.go
View file @
c1cf3bd2
...
@@ -56,7 +56,6 @@ func (s *server) GetBlockByHash(ctx context.Context, request *nodev1.GetBlockReq
...
@@ -56,7 +56,6 @@ func (s *server) GetBlockByHash(ctx context.Context, request *nodev1.GetBlockReq
return
&
nodev1
.
GetBlockResponse
{
return
&
nodev1
.
GetBlockResponse
{
Block
:
blk
,
Block
:
blk
,
},
nil
},
nil
}
}
func
(
s
*
server
)
GetTransactionByHash
(
ctx
context
.
Context
,
request
*
nodev1
.
GetTransactionRequest
)
(
*
nodev1
.
GetTransactionResponse
,
error
)
{
func
(
s
*
server
)
GetTransactionByHash
(
ctx
context
.
Context
,
request
*
nodev1
.
GetTransactionRequest
)
(
*
nodev1
.
GetTransactionResponse
,
error
)
{
...
...
exchain/exmonitor/klinegenerator.go
0 → 100644
View file @
c1cf3bd2
package
exmonitor
import
(
"log"
"time"
"github.com/robfig/cron/v3"
)
const
(
K_FIELD_MIN
=
time
.
Minute
K_FIELD_HOUR
=
time
.
Hour
K_FIELD_DAY
=
24
*
time
.
Hour
K_FIELD_WEEK
=
7
*
24
*
time
.
Hour
K_FIELD_MONTH
=
30
*
24
*
time
.
Hour
K_FIELD_YEAR
=
365
*
24
*
time
.
Hour
)
type
CoinProcessorFactory
interface
{
GetCoinProcessor
(
symbol
,
baseCoin
string
)
*
DefaultCoinProcessor
GetProcessorMap
()
map
[
string
]
*
DefaultCoinProcessor
}
type
KLineGeneratorJob
struct
{
ProcessorFactory
CoinProcessorFactory
}
func
NewKLineGeneratorJob
(
factory
CoinProcessorFactory
,
service
MarketService
)
*
KLineGeneratorJob
{
return
&
KLineGeneratorJob
{
ProcessorFactory
:
factory
,
}
}
func
(
job
*
KLineGeneratorJob
)
Handle5MinKLine
()
{
now
:=
time
.
Now
()
log
.
Printf
(
"Minute KLine: %v"
,
now
)
// Set seconds and milliseconds to 0
truncatedTime
:=
now
.
Truncate
(
time
.
Minute
)
minute
:=
truncatedTime
.
Minute
()
hour
:=
truncatedTime
.
Hour
()
for
symbol
,
processor
:=
range
job
.
ProcessorFactory
.
GetProcessorMap
()
{
if
!
processor
.
IsStopKline
()
{
log
.
Printf
(
"Generating 1-minute KLine for %s"
,
symbol
)
processor
.
AutoGenerate
()
processor
.
Update24HVolume
(
truncatedTime
.
UnixMilli
())
if
minute
%
5
==
0
{
processor
.
GenerateKLine
(
5
,
K_FIELD_MIN
,
truncatedTime
.
UnixMilli
())
}
if
minute
%
10
==
0
{
processor
.
GenerateKLine
(
10
,
K_FIELD_MIN
,
truncatedTime
.
UnixMilli
())
}
if
minute
%
15
==
0
{
processor
.
GenerateKLine
(
15
,
K_FIELD_MIN
,
truncatedTime
.
UnixMilli
())
}
if
minute
%
30
==
0
{
processor
.
GenerateKLine
(
30
,
K_FIELD_MIN
,
truncatedTime
.
UnixMilli
())
}
if
hour
==
0
&&
minute
==
0
{
processor
.
ResetThumb
()
}
}
}
}
func
(
job
*
KLineGeneratorJob
)
HandleHourKLine
()
{
now
:=
time
.
Now
()
log
.
Printf
(
"Hour KLine: %v"
,
now
)
// Set minutes, seconds, and milliseconds to 0
truncatedTime
:=
now
.
Truncate
(
time
.
Hour
)
for
_
,
processor
:=
range
job
.
ProcessorFactory
.
GetProcessorMap
()
{
if
!
processor
.
IsStopKline
()
{
processor
.
GenerateKLine
(
1
,
K_FIELD_HOUR
,
truncatedTime
.
UnixMilli
())
}
}
}
func
(
job
*
KLineGeneratorJob
)
HandleDayKLine
()
{
now
:=
time
.
Now
()
log
.
Printf
(
"Day KLine: %v"
,
now
)
// Set hours, minutes, seconds, and milliseconds to 0
truncatedTime
:=
now
.
Truncate
(
24
*
time
.
Hour
)
week
:=
int
(
truncatedTime
.
Weekday
())
dayOfMonth
:=
truncatedTime
.
Day
()
for
_
,
processor
:=
range
job
.
ProcessorFactory
.
GetProcessorMap
()
{
if
!
processor
.
IsStopKline
()
{
if
week
==
0
{
// Sunday
processor
.
GenerateKLine
(
1
,
K_FIELD_WEEK
,
truncatedTime
.
UnixMilli
())
}
if
dayOfMonth
==
1
{
processor
.
GenerateKLine
(
1
,
K_FIELD_MONTH
,
truncatedTime
.
UnixMilli
())
}
processor
.
GenerateKLine
(
1
,
K_FIELD_YEAR
,
truncatedTime
.
UnixMilli
())
}
}
}
func
(
job
*
KLineGeneratorJob
)
Start
()
{
c
:=
cron
.
New
()
// Schedule tasks
c
.
AddFunc
(
"0 * * * *"
,
job
.
Handle5MinKLine
)
// Every minute
c
.
AddFunc
(
"0 0 * * *"
,
job
.
HandleHourKLine
)
// Every hour
c
.
AddFunc
(
"0 0 0 * *"
,
job
.
HandleDayKLine
)
// Every day at midnight
// Start the cron scheduler
c
.
Start
()
}
exchain/exmonitor/markethandle.go
0 → 100644
View file @
c1cf3bd2
package
exmonitor
import
(
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
)
// MongoMarketHandler handles market data operations
type
mongoMarketHandler
struct
{
client
*
mongo
.
Client
db
*
mongo
.
Database
}
// HandleTrade inserts an ExchangeTrade into the corresponding collection
func
(
h
*
mongoMarketHandler
)
HandleTrade
(
symbol
string
,
trade
*
ExchangeTrade
)
error
{
collection
:=
h
.
db
.
Collection
(
"exchange_trade_"
+
symbol
)
_
,
err
:=
collection
.
InsertOne
(
context
.
Background
(),
trade
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to insert trade: %v"
,
err
)
}
return
nil
}
// HandleKLine inserts a KLine into the corresponding collection
func
(
h
*
mongoMarketHandler
)
HandleKLine
(
symbol
string
,
kline
*
KLine
)
error
{
collection
:=
h
.
db
.
Collection
(
"exchange_kline_"
+
symbol
+
"_"
+
kline
.
Period
)
_
,
err
:=
collection
.
InsertOne
(
context
.
Background
(),
kline
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to insert KLine: %v"
,
err
)
}
return
nil
}
exchain/exmonitor/marketservice.go
0 → 100644
View file @
c1cf3bd2
package
exmonitor
import
(
"context"
"github.com/exchain/go-exchain/op-supervisor/config"
"log"
"math/big"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
const
(
mongodbName
=
"exmarket"
klinePrefix
=
"exchange_kline_"
tradePrefix
=
"exchange_trade_"
)
type
marketService
struct
{
mongoClient
*
mongo
.
Client
}
func
NewMarketService
(
conf
*
config
.
Config
)
MarketService
{
// Initialize MongoDB client
mongoURI
:=
""
// conf.MongoDBURI
clientOptions
:=
options
.
Client
()
.
ApplyURI
(
mongoURI
)
client
,
err
:=
mongo
.
Connect
(
context
.
TODO
(),
clientOptions
)
if
err
!=
nil
{
log
.
Fatalf
(
"Failed to connect to MongoDB: %v"
,
err
)
}
return
&
marketService
{
mongoClient
:
client
}
}
func
(
s
*
marketService
)
FindAllKLine
(
symbol
,
period
string
)
[]
*
KLine
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
klinePrefix
+
symbol
+
"_"
+
period
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
opts
:=
options
.
Find
()
.
SetSort
(
bson
.
D
{{
Key
:
"time"
,
Value
:
-
1
}})
.
SetLimit
(
1000
)
cursor
,
err
:=
collection
.
Find
(
ctx
,
bson
.
M
{},
opts
)
if
err
!=
nil
{
log
.
Fatalf
(
"Error finding KLine: %v"
,
err
)
}
defer
cursor
.
Close
(
ctx
)
var
kLines
[]
*
KLine
if
err
:=
cursor
.
All
(
ctx
,
&
kLines
);
err
!=
nil
{
log
.
Fatalf
(
"Error decoding KLine: %v"
,
err
)
}
return
kLines
}
func
(
s
*
marketService
)
FindAllKLineByTimeRange
(
symbol
string
,
fromTime
,
toTime
int64
,
period
string
)
[]
*
KLine
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
klinePrefix
+
symbol
+
"_"
+
period
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
filter
:=
bson
.
M
{
"time"
:
bson
.
M
{
"$gte"
:
fromTime
,
"$lte"
:
toTime
,
},
}
opts
:=
options
.
Find
()
.
SetSort
(
bson
.
D
{{
Key
:
"time"
,
Value
:
1
}})
cursor
,
err
:=
collection
.
Find
(
ctx
,
filter
,
opts
)
if
err
!=
nil
{
log
.
Fatalf
(
"Error finding KLine by time range: %v"
,
err
)
}
defer
cursor
.
Close
(
ctx
)
var
kLines
[]
*
KLine
if
err
:=
cursor
.
All
(
ctx
,
&
kLines
);
err
!=
nil
{
log
.
Fatalf
(
"Error decoding KLine: %v"
,
err
)
}
return
kLines
}
func
(
s
*
marketService
)
FindFirstTrade
(
symbol
string
,
fromTime
,
toTime
int64
)
*
ExchangeTrade
{
return
s
.
findTrade
(
symbol
,
fromTime
,
toTime
,
bson
.
D
{{
Key
:
"time"
,
Value
:
1
}})
}
func
(
s
*
marketService
)
FindLastTrade
(
symbol
string
,
fromTime
,
toTime
int64
)
*
ExchangeTrade
{
return
s
.
findTrade
(
symbol
,
fromTime
,
toTime
,
bson
.
D
{{
Key
:
"time"
,
Value
:
-
1
}})
}
func
(
s
*
marketService
)
findTrade
(
symbol
string
,
fromTime
,
toTime
int64
,
sortOrder
bson
.
D
)
*
ExchangeTrade
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
tradePrefix
+
symbol
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
filter
:=
bson
.
M
{
"time"
:
bson
.
M
{
"$gte"
:
fromTime
,
"$lte"
:
toTime
,
},
}
opts
:=
options
.
FindOne
()
.
SetSort
(
sortOrder
)
var
trade
ExchangeTrade
err
:=
collection
.
FindOne
(
ctx
,
filter
,
opts
)
.
Decode
(
&
trade
)
if
err
!=
nil
{
if
err
==
mongo
.
ErrNoDocuments
{
return
nil
}
log
.
Fatalf
(
"Error finding trade: %v"
,
err
)
}
return
&
trade
}
func
(
s
*
marketService
)
FindTradeByTimeRange
(
symbol
string
,
timeStart
,
timeEnd
int64
)
[]
*
ExchangeTrade
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
tradePrefix
+
symbol
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
filter
:=
bson
.
M
{
"time"
:
bson
.
M
{
"$gte"
:
timeStart
,
"$lt"
:
timeEnd
,
},
}
opts
:=
options
.
Find
()
.
SetSort
(
bson
.
D
{{
Key
:
"time"
,
Value
:
1
}})
cursor
,
err
:=
collection
.
Find
(
ctx
,
filter
,
opts
)
if
err
!=
nil
{
log
.
Fatalf
(
"Error finding trades by time range: %v"
,
err
)
}
defer
cursor
.
Close
(
ctx
)
var
trades
[]
*
ExchangeTrade
if
err
:=
cursor
.
All
(
ctx
,
&
trades
);
err
!=
nil
{
log
.
Fatalf
(
"Error decoding trades: %v"
,
err
)
}
return
trades
}
func
(
s
*
marketService
)
SaveKLine
(
symbol
string
,
kLine
*
KLine
)
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
klinePrefix
+
symbol
+
"_"
+
kLine
.
Period
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
_
,
err
:=
collection
.
InsertOne
(
ctx
,
kLine
)
if
err
!=
nil
{
log
.
Fatalf
(
"Error saving KLine: %v"
,
err
)
}
}
func
(
s
*
marketService
)
FindTradeVolume
(
symbol
string
,
timeStart
,
timeEnd
int64
)
*
big
.
Float
{
collection
:=
s
.
mongoClient
.
Database
(
mongodbName
)
.
Collection
(
klinePrefix
+
symbol
+
"_1min"
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
defer
cancel
()
filter
:=
bson
.
M
{
"time"
:
bson
.
M
{
"$gt"
:
timeStart
,
"$lte"
:
timeEnd
,
},
}
opts
:=
options
.
Find
()
.
SetSort
(
bson
.
D
{{
Key
:
"time"
,
Value
:
1
}})
cursor
,
err
:=
collection
.
Find
(
ctx
,
filter
,
opts
)
if
err
!=
nil
{
log
.
Fatalf
(
"Error finding trade volume: %v"
,
err
)
}
defer
cursor
.
Close
(
ctx
)
totalVolume
:=
big
.
NewFloat
(
0
)
for
cursor
.
Next
(
ctx
)
{
var
kLine
KLine
if
err
:=
cursor
.
Decode
(
&
kLine
);
err
!=
nil
{
log
.
Fatalf
(
"Error decoding KLine: %v"
,
err
)
}
totalVolume
=
totalVolume
.
Add
(
totalVolume
,
kLine
.
Volume
)
}
return
totalVolume
}
func
(
s
*
marketService
)
NewHandler
(
symbol
string
)
MarketHandler
{
return
&
mongoMarketHandler
{
client
:
s
.
mongoClient
,
db
:
s
.
mongoClient
.
Database
(
mongodbName
),
}
}
exchain/exmonitor/monitor.go
0 → 100644
View file @
c1cf3bd2
package
exmonitor
import
"github.com/exchain/go-exchain/op-supervisor/config"
type
Monitor
struct
{
kline
*
KLineGeneratorJob
service
MarketService
}
func
NewMonitor
(
conf
*
config
.
Config
)
*
Monitor
{
service
:=
NewMarketService
(
conf
)
kline
:=
NewKLineGeneratorJob
(
&
coinProcessorFactory
{},
service
)
return
&
Monitor
{
kline
:
kline
,
service
:
service
,
}
}
func
(
m
*
Monitor
)
Start
()
{
// Initialize the monitor
// Start the monitoring process
m
.
kline
.
Start
()
}
exchain/exmonitor/processor.go
0 → 100644
View file @
c1cf3bd2
package
exmonitor
import
(
"fmt"
"math/big"
"sync"
"time"
)
type
KLine
struct
{
Time
int64
Period
string
Count
int
OpenPrice
*
big
.
Float
ClosePrice
*
big
.
Float
HighestPrice
*
big
.
Float
LowestPrice
*
big
.
Float
Volume
*
big
.
Float
Turnover
*
big
.
Float
}
type
CoinThumb
struct
{
Symbol
string
Open
*
big
.
Float
Close
*
big
.
Float
High
*
big
.
Float
Low
*
big
.
Float
Volume
*
big
.
Float
Turnover
*
big
.
Float
Change
*
big
.
Float
Chg
*
big
.
Float
BaseUsdRate
*
big
.
Float
UsdRate
*
big
.
Float
LastDayClose
*
big
.
Float
}
type
ExchangeTrade
struct
{
Price
*
big
.
Float
Amount
*
big
.
Float
}
type
MarketHandler
interface
{
HandleTrade
(
symbol
string
,
trade
*
ExchangeTrade
)
error
HandleKLine
(
symbol
string
,
kline
*
KLine
)
}
type
MarketService
interface
{
//FindAllKLine(symbol string, start, end int64, period string) []*KLine
FindTradeVolume
(
symbol
string
,
start
,
end
int64
)
*
big
.
Float
FindTradeByTimeRange
(
symbol
string
,
start
,
end
int64
)
[]
*
ExchangeTrade
FindAllKLineByTimeRange
(
symbol
string
,
fromTime
,
toTime
int64
,
period
string
)
[]
*
KLine
SaveKLine
(
symbol
string
,
kline
*
KLine
)
}
type
DefaultCoinProcessor
struct
{
symbol
string
baseCoin
string
currentKLine
*
KLine
handlers
[]
MarketHandler
coinThumb
*
CoinThumb
service
MarketService
coinExchangeRate
map
[
string
]
*
big
.
Float
isHalt
bool
stopKLine
bool
mutex
sync
.
Mutex
}
type
coinProcessorFactory
struct
{
mux
sync
.
Mutex
coinProcessors
map
[
string
]
*
DefaultCoinProcessor
}
func
(
cpf
*
coinProcessorFactory
)
GetCoinProcessor
(
symbol
,
baseCoin
string
)
*
DefaultCoinProcessor
{
cpf
.
mux
.
Lock
()
defer
cpf
.
mux
.
Unlock
()
if
processor
,
exists
:=
cpf
.
coinProcessors
[
symbol
];
exists
{
return
processor
}
else
{
processor
=
&
DefaultCoinProcessor
{
symbol
:
symbol
,
baseCoin
:
baseCoin
,
currentKLine
:
createNewKLine
(),
handlers
:
[]
MarketHandler
{},
coinThumb
:
&
CoinThumb
{},
isHalt
:
true
,
stopKLine
:
false
,
}
cpf
.
coinProcessors
[
symbol
]
=
processor
return
processor
}
}
func
(
cpf
*
coinProcessorFactory
)
GetProcessorMap
()
map
[
string
]
*
DefaultCoinProcessor
{
cpf
.
mux
.
Lock
()
defer
cpf
.
mux
.
Unlock
()
// copy the map to avoid concurrent map writes
// and return a new map
processorMap
:=
make
(
map
[
string
]
*
DefaultCoinProcessor
)
for
k
,
v
:=
range
cpf
.
coinProcessors
{
processorMap
[
k
]
=
v
}
return
processorMap
}
func
createNewKLine
()
*
KLine
{
now
:=
time
.
Now
()
nextMinute
:=
now
.
Add
(
time
.
Minute
)
return
&
KLine
{
Time
:
nextMinute
.
UnixMilli
(),
Period
:
"1min"
,
Count
:
0
,
OpenPrice
:
big
.
NewFloat
(
0
),
ClosePrice
:
big
.
NewFloat
(
0
),
HighestPrice
:
big
.
NewFloat
(
0
),
LowestPrice
:
big
.
NewFloat
(
0
),
Volume
:
big
.
NewFloat
(
0
),
Turnover
:
big
.
NewFloat
(
0
),
}
}
func
(
p
*
DefaultCoinProcessor
)
InitializeThumb
()
{
p
.
mutex
.
Lock
()
defer
p
.
mutex
.
Unlock
()
now
:=
time
.
Now
()
startOfDay
:=
time
.
Date
(
now
.
Year
(),
now
.
Month
(),
now
.
Day
(),
0
,
0
,
0
,
0
,
now
.
Location
())
lines
:=
p
.
service
.
FindAllKLineByTimeRange
(
p
.
symbol
,
startOfDay
.
UnixMilli
(),
now
.
UnixMilli
(),
"1min"
)
p
.
coinThumb
=
&
CoinThumb
{
Symbol
:
p
.
symbol
,
Open
:
big
.
NewFloat
(
0
),
High
:
big
.
NewFloat
(
0
),
Low
:
big
.
NewFloat
(
0
),
Close
:
big
.
NewFloat
(
0
),
Volume
:
big
.
NewFloat
(
0
),
Turnover
:
big
.
NewFloat
(
0
),
}
for
_
,
line
:=
range
lines
{
if
line
.
OpenPrice
.
Cmp
(
big
.
NewFloat
(
0
))
==
0
{
continue
}
if
p
.
coinThumb
.
Open
.
Cmp
(
big
.
NewFloat
(
0
))
==
0
{
p
.
coinThumb
.
Open
=
line
.
OpenPrice
}
if
p
.
coinThumb
.
High
.
Cmp
(
line
.
HighestPrice
)
<
0
{
p
.
coinThumb
.
High
=
line
.
HighestPrice
}
if
line
.
LowestPrice
.
Cmp
(
big
.
NewFloat
(
0
))
>
0
&&
p
.
coinThumb
.
Low
.
Cmp
(
line
.
LowestPrice
)
>
0
{
p
.
coinThumb
.
Low
=
line
.
LowestPrice
}
if
line
.
ClosePrice
.
Cmp
(
big
.
NewFloat
(
0
))
>
0
{
p
.
coinThumb
.
Close
=
line
.
ClosePrice
}
p
.
coinThumb
.
Volume
.
Add
(
p
.
coinThumb
.
Volume
,
line
.
Volume
)
p
.
coinThumb
.
Turnover
.
Add
(
p
.
coinThumb
.
Turnover
,
line
.
Turnover
)
}
change
:=
new
(
big
.
Float
)
.
Sub
(
p
.
coinThumb
.
Close
,
p
.
coinThumb
.
Open
)
p
.
coinThumb
.
Change
=
change
if
p
.
coinThumb
.
Low
.
Cmp
(
big
.
NewFloat
(
0
))
>
0
{
p
.
coinThumb
.
Chg
=
new
(
big
.
Float
)
.
Quo
(
change
,
p
.
coinThumb
.
Low
)
}
}
func
(
p
*
DefaultCoinProcessor
)
IsStopKline
()
bool
{
return
p
.
stopKLine
}
func
(
p
*
DefaultCoinProcessor
)
ResetThumb
()
{
p
.
mutex
.
Lock
()
defer
p
.
mutex
.
Unlock
()
p
.
coinThumb
.
Open
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
High
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
Low
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
Close
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
Change
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
Chg
=
big
.
NewFloat
(
0
)
p
.
coinThumb
.
LastDayClose
=
p
.
coinThumb
.
Close
}
func
(
p
*
DefaultCoinProcessor
)
AutoGenerate
()
{
p
.
mutex
.
Lock
()
defer
p
.
mutex
.
Unlock
()
if
p
.
coinThumb
!=
nil
{
if
p
.
currentKLine
.
OpenPrice
.
Cmp
(
big
.
NewFloat
(
0
))
==
0
{
p
.
currentKLine
.
OpenPrice
=
p
.
coinThumb
.
Close
p
.
currentKLine
.
LowestPrice
=
p
.
coinThumb
.
Close
p
.
currentKLine
.
HighestPrice
=
p
.
coinThumb
.
Close
p
.
currentKLine
.
ClosePrice
=
p
.
coinThumb
.
Close
}
p
.
currentKLine
.
Time
=
time
.
Now
()
.
UnixMilli
()
p
.
handleKLineStorage
(
p
.
currentKLine
)
p
.
currentKLine
=
createNewKLine
()
}
}
func
(
p
*
DefaultCoinProcessor
)
handleKLineStorage
(
kline
*
KLine
)
{
for
_
,
handler
:=
range
p
.
handlers
{
handler
.
HandleKLine
(
p
.
symbol
,
kline
)
}
}
func
(
p
*
DefaultCoinProcessor
)
AddHandler
(
handler
MarketHandler
)
{
p
.
handlers
=
append
(
p
.
handlers
,
handler
)
}
func
(
p
*
DefaultCoinProcessor
)
GenerateKLine
(
rangeValue
int
,
field
time
.
Duration
,
timestamp
int64
)
{
p
.
stopKLine
=
false
defer
func
()
{
p
.
stopKLine
=
true
}()
endTime
:=
time
.
UnixMilli
(
timestamp
)
startTime
:=
endTime
.
Add
(
-
field
*
time
.
Duration
(
rangeValue
))
trades
:=
p
.
service
.
FindTradeByTimeRange
(
p
.
symbol
,
startTime
.
UnixMilli
(),
endTime
.
UnixMilli
())
kline
:=
&
KLine
{
Time
:
endTime
.
UnixMilli
(),
Period
:
formatPeriod
(
rangeValue
,
field
),
OpenPrice
:
big
.
NewFloat
(
0
),
ClosePrice
:
big
.
NewFloat
(
0
),
HighestPrice
:
big
.
NewFloat
(
0
),
LowestPrice
:
big
.
NewFloat
(
0
),
Volume
:
big
.
NewFloat
(
0
),
Turnover
:
big
.
NewFloat
(
0
),
}
for
_
,
trade
:=
range
trades
{
p
.
processTrade
(
kline
,
trade
)
}
if
kline
.
OpenPrice
.
Cmp
(
big
.
NewFloat
(
0
))
==
0
{
kline
.
OpenPrice
=
p
.
coinThumb
.
Close
kline
.
ClosePrice
=
p
.
coinThumb
.
Close
kline
.
LowestPrice
=
p
.
coinThumb
.
Close
kline
.
HighestPrice
=
p
.
coinThumb
.
Close
}
p
.
service
.
SaveKLine
(
p
.
symbol
,
kline
)
}
func
(
p
*
DefaultCoinProcessor
)
processTrade
(
kline
*
KLine
,
trade
*
ExchangeTrade
)
{
if
kline
.
OpenPrice
.
Cmp
(
big
.
NewFloat
(
0
))
==
0
{
kline
.
OpenPrice
=
trade
.
Price
kline
.
HighestPrice
=
trade
.
Price
kline
.
LowestPrice
=
trade
.
Price
kline
.
ClosePrice
=
trade
.
Price
}
else
{
if
trade
.
Price
.
Cmp
(
kline
.
HighestPrice
)
>
0
{
kline
.
HighestPrice
=
trade
.
Price
}
if
trade
.
Price
.
Cmp
(
kline
.
LowestPrice
)
<
0
{
kline
.
LowestPrice
=
trade
.
Price
}
kline
.
ClosePrice
=
trade
.
Price
}
kline
.
Count
++
kline
.
Volume
.
Add
(
kline
.
Volume
,
trade
.
Amount
)
turnover
:=
new
(
big
.
Float
)
.
Mul
(
trade
.
Price
,
trade
.
Amount
)
kline
.
Turnover
.
Add
(
kline
.
Turnover
,
turnover
)
}
func
formatPeriod
(
rangeValue
int
,
field
time
.
Duration
)
string
{
switch
field
{
case
K_FIELD_MIN
:
return
fmt
.
Sprintf
(
"%dmin"
,
rangeValue
)
case
K_FIELD_HOUR
:
return
fmt
.
Sprintf
(
"%dhour"
,
rangeValue
)
case
K_FIELD_DAY
:
return
fmt
.
Sprintf
(
"%dday"
,
rangeValue
)
case
K_FIELD_WEEK
:
return
fmt
.
Sprintf
(
"%dweek"
,
rangeValue
)
case
K_FIELD_MONTH
:
return
fmt
.
Sprintf
(
"%dmonth"
,
rangeValue
)
case
K_FIELD_YEAR
:
return
fmt
.
Sprintf
(
"%dyear"
,
rangeValue
)
default
:
return
"unknown"
}
}
func
(
p
*
DefaultCoinProcessor
)
Update24HVolume
(
currentTime
int64
)
{
if
p
.
coinThumb
!=
nil
{
p
.
mutex
.
Lock
()
defer
p
.
mutex
.
Unlock
()
// Calculate the start time (24 hours ago)
startTime
:=
time
.
UnixMilli
(
currentTime
)
.
Add
(
-
24
*
time
.
Hour
)
.
UnixMilli
()
// Fetch the trade volume from the service
volume
:=
p
.
service
.
FindTradeVolume
(
p
.
symbol
,
startTime
,
currentTime
)
// Set the volume in the coinThumb, rounded to 4 decimal places
p
.
coinThumb
.
Volume
=
new
(
big
.
Float
)
.
SetPrec
(
4
)
.
SetMode
(
big
.
ToZero
)
.
Set
(
volume
)
}
}
exchain/protocol/proto/orderbook/v1/orderbook.proto
View file @
c1cf3bd2
...
@@ -7,6 +7,12 @@ option go_package = "github.com/exchain/go-exchain/exchain/protocol/orderbook/v1
...
@@ -7,6 +7,12 @@ option go_package = "github.com/exchain/go-exchain/exchain/protocol/orderbook/v1
import
"nebula/v1/transaction.proto"
;
import
"nebula/v1/transaction.proto"
;
import
"google/api/annotations.proto"
;
import
"google/api/annotations.proto"
;
option
java_multiple_files
=
true
;
option
java_package
=
"com.exchain.protocol.orderbook.v1"
;
option
java_outer_classname
=
"ExChainOrderBookProto"
;
import
"google/protobuf/timestamp.proto"
;
message
PlaceLimitOrderRequest
{
message
PlaceLimitOrderRequest
{
exchain.nebula.v1.Transaction
transaction
=
1
;
exchain.nebula.v1.Transaction
transaction
=
1
;
...
@@ -55,3 +61,782 @@ service Orderbook {
...
@@ -55,3 +61,782 @@ service Orderbook {
}
}
}
}
message
Symbol
{
string
base_asset
=
1
;
string
quote_asset
=
2
;
}
message
PaginationRequest
{
int32
page
=
1
;
int32
limit
=
2
;
}
message
PaginationResponse
{
int32
total
=
1
;
int32
page
=
2
;
int32
limit
=
3
;
}
message
PriceLevel
{
string
price
=
1
;
string
quantity
=
2
;
}
message
OrderBookEntry
{
string
price
=
1
;
string
quantity
=
2
;
int64
order_count
=
3
;
// 可选,此价格的订单数量
}
// 订单簿快照
message
OrderBookSnapshot
{
string
symbol
=
1
;
int64
last_update_id
=
2
;
google.protobuf.Timestamp
time
=
3
;
repeated
OrderBookEntry
bids
=
4
;
// 按价格从高到低排序
repeated
OrderBookEntry
asks
=
5
;
// 按价格从低到高排序
}
// 订单簿增量更新
message
OrderBookDelta
{
string
symbol
=
1
;
int64
first_update_id
=
2
;
int64
last_update_id
=
3
;
google.protobuf.Timestamp
time
=
4
;
repeated
OrderBookEntry
bids
=
5
;
// 价格为0表示删除此价格级别
repeated
OrderBookEntry
asks
=
6
;
// 价格为0表示删除此价格级别
}
// 获取订单簿快照请求
message
GetOrderBookRequest
{
string
symbol
=
1
;
int32
limit
=
2
;
// 可选,每侧显示的价格级别数,默认100,最大1000
}
// 获取订单簿快照响应
message
GetOrderBookResponse
{
OrderBookSnapshot
snapshot
=
1
;
}
// 订阅订单簿增量更新请求
message
SubscribeOrderBookRequest
{
string
symbol
=
1
;
string
update_speed
=
2
;
// 可选,如"100ms"、"1000ms"等
}
// 订阅订单簿增量更新响应(流式)
message
SubscribeOrderBookResponse
{
oneof
update
{
OrderBookSnapshot
snapshot
=
1
;
// 初始快照
OrderBookDelta
delta
=
2
;
// 后续增量更新
}
}
// 获取订单簿顶部价位请求
message
GetOrderBookTickerRequest
{
string
symbol
=
1
;
// 可选,不提供则返回所有交易对
}
// 订单簿顶部价位
message
OrderBookTicker
{
string
symbol
=
1
;
string
bid_price
=
2
;
string
bid_qty
=
3
;
string
ask_price
=
4
;
string
ask_qty
=
5
;
google.protobuf.Timestamp
time
=
6
;
}
// 获取订单簿顶部价位响应
message
GetOrderBookTickerResponse
{
repeated
OrderBookTicker
tickers
=
1
;
}
// ======================== 系统API消息定义 ========================
message
PingRequest
{}
message
PingResponse
{}
message
ServerTimeRequest
{}
message
ServerTimeResponse
{
google.protobuf.Timestamp
server_time
=
1
;
}
message
ExchangeInfoRequest
{
string
symbol
=
1
;
// 可选,指定交易对
}
message
RateLimit
{
string
rate_limit_type
=
1
;
string
interval
=
2
;
int32
interval_num
=
3
;
int64
limit
=
4
;
}
message
SymbolFilter
{
string
filter_type
=
1
;
string
min_price
=
2
;
string
max_price
=
3
;
string
tick_size
=
4
;
string
min_qty
=
5
;
string
max_qty
=
6
;
string
step_size
=
7
;
string
min_notional
=
8
;
bool
apply_to_market
=
9
;
int32
avg_price_mins
=
10
;
string
multiplier_up
=
11
;
string
multiplier_down
=
12
;
int32
limit
=
13
;
}
message
SymbolInfo
{
string
symbol
=
1
;
string
status
=
2
;
string
base_asset
=
3
;
int32
base_asset_precision
=
4
;
string
quote_asset
=
5
;
int32
quote_asset_precision
=
6
;
repeated
string
order_types
=
7
;
bool
iceberg_allowed
=
8
;
bool
oco_allowed
=
9
;
bool
quote_order_qty_market_allowed
=
10
;
bool
allow_trailing_stop
=
11
;
bool
cancel_replace_allowed
=
12
;
bool
is_spot_trading_allowed
=
13
;
bool
is_margin_trading_allowed
=
14
;
repeated
SymbolFilter
filters
=
15
;
repeated
string
permissions
=
16
;
}
message
ExchangeInfoResponse
{
string
timezone
=
1
;
google.protobuf.Timestamp
server_time
=
2
;
repeated
RateLimit
rate_limits
=
3
;
repeated
SymbolInfo
symbols
=
4
;
}
// ======================== 市场数据API消息定义 ========================
message
TradesRequest
{
string
symbol
=
1
;
int32
limit
=
2
;
// 可选,默认500
}
message
Trade
{
int64
id
=
1
;
string
price
=
2
;
string
qty
=
3
;
string
quote_qty
=
4
;
google.protobuf.Timestamp
time
=
5
;
bool
is_buyer_maker
=
6
;
bool
is_best_match
=
7
;
}
message
TradesResponse
{
repeated
Trade
trades
=
1
;
}
message
HistoricalTradesRequest
{
string
symbol
=
1
;
int64
from_id
=
2
;
// 可选,从哪个交易ID开始
int32
limit
=
3
;
// 可选,默认500
}
message
HistoricalTradesResponse
{
repeated
Trade
trades
=
1
;
}
message
AggTradesRequest
{
string
symbol
=
1
;
int64
from_id
=
2
;
// 可选
google.protobuf.Timestamp
start_time
=
3
;
// 可选
google.protobuf.Timestamp
end_time
=
4
;
// 可选
int32
limit
=
5
;
// 可选,默认500
}
message
AggTrade
{
int64
id
=
1
;
string
price
=
2
;
string
qty
=
3
;
int64
first_id
=
4
;
int64
last_id
=
5
;
google.protobuf.Timestamp
time
=
6
;
bool
is_buyer_maker
=
7
;
bool
is_best_match
=
8
;
}
message
AggTradesResponse
{
repeated
AggTrade
agg_trades
=
1
;
}
enum
KlineInterval
{
KLINE_INTERVAL_UNSPECIFIED
=
0
;
KLINE_INTERVAL_1m
=
1
;
KLINE_INTERVAL_3m
=
2
;
KLINE_INTERVAL_5m
=
3
;
KLINE_INTERVAL_15m
=
4
;
KLINE_INTERVAL_30m
=
5
;
KLINE_INTERVAL_1h
=
6
;
KLINE_INTERVAL_2h
=
7
;
KLINE_INTERVAL_4h
=
8
;
KLINE_INTERVAL_6h
=
9
;
KLINE_INTERVAL_8h
=
10
;
KLINE_INTERVAL_12h
=
11
;
KLINE_INTERVAL_1d
=
12
;
KLINE_INTERVAL_3d
=
13
;
KLINE_INTERVAL_1w
=
14
;
KLINE_INTERVAL_1M
=
15
;
}
message
KlinesRequest
{
string
symbol
=
1
;
KlineInterval
interval
=
2
;
google.protobuf.Timestamp
start_time
=
3
;
// 可选
google.protobuf.Timestamp
end_time
=
4
;
// 可选
int32
limit
=
5
;
// 可选,默认500
}
message
Kline
{
google.protobuf.Timestamp
open_time
=
1
;
string
open
=
2
;
string
high
=
3
;
string
low
=
4
;
string
close
=
5
;
string
volume
=
6
;
google.protobuf.Timestamp
close_time
=
7
;
string
quote_asset_volume
=
8
;
int32
trade_count
=
9
;
string
taker_buy_base_asset_volume
=
10
;
string
taker_buy_quote_asset_volume
=
11
;
}
message
KlinesResponse
{
repeated
Kline
klines
=
1
;
}
message
TickerPriceRequest
{
string
symbol
=
1
;
// 可选,不提供则返回所有交易对
}
message
TickerPrice
{
string
symbol
=
1
;
string
price
=
2
;
google.protobuf.Timestamp
time
=
3
;
}
message
TickerPriceResponse
{
repeated
TickerPrice
tickers
=
1
;
}
message
Ticker24hrRequest
{
string
symbol
=
1
;
// 可选,不提供则返回所有交易对
}
message
Ticker24hr
{
string
symbol
=
1
;
string
price_change
=
2
;
string
price_change_percent
=
3
;
string
weighted_avg_price
=
4
;
string
prev_close_price
=
5
;
string
last_price
=
6
;
string
last_qty
=
7
;
string
bid_price
=
8
;
string
bid_qty
=
9
;
string
ask_price
=
10
;
string
ask_qty
=
11
;
string
open_price
=
12
;
string
high_price
=
13
;
string
low_price
=
14
;
string
volume
=
15
;
string
quote_volume
=
16
;
google.protobuf.Timestamp
open_time
=
17
;
google.protobuf.Timestamp
close_time
=
18
;
int64
first_id
=
19
;
int64
last_id
=
20
;
int64
count
=
21
;
}
message
Ticker24hrResponse
{
repeated
Ticker24hr
tickers
=
1
;
}
// ======================== 交易API消息定义 ========================
enum
OrderSide
{
ORDER_SIDE_UNSPECIFIED
=
0
;
ORDER_SIDE_BUY
=
1
;
ORDER_SIDE_SELL
=
2
;
}
enum
OrderType
{
ORDER_TYPE_UNSPECIFIED
=
0
;
ORDER_TYPE_LIMIT
=
1
;
ORDER_TYPE_MARKET
=
2
;
ORDER_TYPE_STOP_LOSS
=
3
;
ORDER_TYPE_STOP_LOSS_LIMIT
=
4
;
ORDER_TYPE_TAKE_PROFIT
=
5
;
ORDER_TYPE_TAKE_PROFIT_LIMIT
=
6
;
ORDER_TYPE_LIMIT_MAKER
=
7
;
}
enum
TimeInForce
{
TIME_IN_FORCE_UNSPECIFIED
=
0
;
TIME_IN_FORCE_GTC
=
1
;
// Good Till Cancel
TIME_IN_FORCE_IOC
=
2
;
// Immediate or Cancel
TIME_IN_FORCE_FOK
=
3
;
// Fill or Kill
}
enum
OrderResponseType
{
ORDER_RESPONSE_TYPE_UNSPECIFIED
=
0
;
ORDER_RESPONSE_TYPE_ACK
=
1
;
ORDER_RESPONSE_TYPE_RESULT
=
2
;
ORDER_RESPONSE_TYPE_FULL
=
3
;
}
enum
OrderStatus
{
ORDER_STATUS_UNSPECIFIED
=
0
;
ORDER_STATUS_NEW
=
1
;
ORDER_STATUS_PARTIALLY_FILLED
=
2
;
ORDER_STATUS_FILLED
=
3
;
ORDER_STATUS_CANCELED
=
4
;
ORDER_STATUS_PENDING_CANCEL
=
5
;
ORDER_STATUS_REJECTED
=
6
;
ORDER_STATUS_EXPIRED
=
7
;
}
message
OrderRequest
{
string
symbol
=
1
;
OrderSide
side
=
2
;
OrderType
type
=
3
;
TimeInForce
time_in_force
=
4
;
// 可选
string
quantity
=
5
;
// 可选
string
quote_order_qty
=
6
;
// 可选
string
price
=
7
;
// 可选
string
new_client_order_id
=
8
;
// 可选
string
stop_price
=
9
;
// 可选
string
iceberg_qty
=
10
;
// 可选
OrderResponseType
new_order_resp_type
=
11
;
// 可选
int32
recv_window
=
12
;
// 可选
google.protobuf.Timestamp
timestamp
=
13
;
}
message
Fill
{
string
price
=
1
;
string
qty
=
2
;
string
commission
=
3
;
string
commission_asset
=
4
;
int64
trade_id
=
5
;
}
message
OrderResponse
{
string
symbol
=
1
;
int64
order_id
=
2
;
string
client_order_id
=
3
;
google.protobuf.Timestamp
transact_time
=
4
;
string
price
=
5
;
string
orig_qty
=
6
;
string
executed_qty
=
7
;
string
cummulative_quote_qty
=
8
;
OrderStatus
status
=
9
;
TimeInForce
time_in_force
=
10
;
OrderType
type
=
11
;
OrderSide
side
=
12
;
repeated
Fill
fills
=
13
;
// 仅在FULL响应类型中
}
message
TestOrderRequest
{
string
symbol
=
1
;
OrderSide
side
=
2
;
OrderType
type
=
3
;
TimeInForce
time_in_force
=
4
;
// 可选
string
quantity
=
5
;
// 可选
string
quote_order_qty
=
6
;
// 可选
string
price
=
7
;
// 可选
string
new_client_order_id
=
8
;
// 可选
string
stop_price
=
9
;
// 可选
string
iceberg_qty
=
10
;
// 可选
int32
recv_window
=
11
;
// 可选
google.protobuf.Timestamp
timestamp
=
12
;
}
message
TestOrderResponse
{}
message
OrderStatusRequest
{
string
symbol
=
1
;
int64
order_id
=
2
;
// order_id 和 orig_client_order_id 必须二选一
string
orig_client_order_id
=
3
;
int32
recv_window
=
4
;
// 可选
google.protobuf.Timestamp
timestamp
=
5
;
}
message
OrderStatusResponse
{
string
symbol
=
1
;
int64
order_id
=
2
;
string
client_order_id
=
3
;
string
price
=
4
;
string
orig_qty
=
5
;
string
executed_qty
=
6
;
string
cummulative_quote_qty
=
7
;
OrderStatus
status
=
8
;
TimeInForce
time_in_force
=
9
;
OrderType
type
=
10
;
OrderSide
side
=
11
;
string
stop_price
=
12
;
string
iceberg_qty
=
13
;
google.protobuf.Timestamp
time
=
14
;
google.protobuf.Timestamp
update_time
=
15
;
bool
is_working
=
16
;
string
orig_quote_order_qty
=
17
;
}
message
CancelOrderRequest
{
string
symbol
=
1
;
int64
order_id
=
2
;
// order_id 和 orig_client_order_id 必须二选一
string
orig_client_order_id
=
3
;
string
new_client_order_id
=
4
;
// 可选
int32
recv_window
=
5
;
// 可选
google.protobuf.Timestamp
timestamp
=
6
;
}
message
CancelOrderResponse
{
string
symbol
=
1
;
string
orig_client_order_id
=
2
;
int64
order_id
=
3
;
string
client_order_id
=
4
;
}
message
CancelAllOrdersRequest
{
string
symbol
=
1
;
int32
recv_window
=
2
;
// 可选
google.protobuf.Timestamp
timestamp
=
3
;
}
message
CancelAllOrdersResponse
{
repeated
CancelOrderResponse
orders
=
1
;
}
message
OpenOrdersRequest
{
string
symbol
=
1
;
// 可选
int32
recv_window
=
2
;
// 可选
google.protobuf.Timestamp
timestamp
=
3
;
}
message
OpenOrdersResponse
{
repeated
OrderStatusResponse
orders
=
1
;
}
message
AllOrdersRequest
{
string
symbol
=
1
;
int64
order_id
=
2
;
// 可选
google.protobuf.Timestamp
start_time
=
3
;
// 可选
google.protobuf.Timestamp
end_time
=
4
;
// 可选
int32
limit
=
5
;
// 可选, 默认500
int32
recv_window
=
6
;
// 可选
google.protobuf.Timestamp
timestamp
=
7
;
}
message
AllOrdersResponse
{
repeated
OrderStatusResponse
orders
=
1
;
}
// OCO (One-Cancels-Other) 订单
message
NewOCORequest
{
string
symbol
=
1
;
string
list_client_order_id
=
2
;
// 可选
OrderSide
side
=
3
;
string
quantity
=
4
;
string
limit_client_order_id
=
5
;
// 可选
string
price
=
6
;
string
limit_iceberg_qty
=
7
;
// 可选
string
stop_client_order_id
=
8
;
// 可选
string
stop_price
=
9
;
string
stop_limit_price
=
10
;
// 可选
string
stop_iceberg_qty
=
11
;
// 可选
TimeInForce
stop_limit_time_in_force
=
12
;
// 可选
int32
recv_window
=
13
;
// 可选
google.protobuf.Timestamp
timestamp
=
14
;
}
message
OCOOrder
{
string
symbol
=
1
;
int64
order_id
=
2
;
string
client_order_id
=
3
;
}
message
OCOResponse
{
int64
order_list_id
=
1
;
string
contingency_type
=
2
;
string
list_status_type
=
3
;
string
list_order_status
=
4
;
string
list_client_order_id
=
5
;
google.protobuf.Timestamp
transaction_time
=
6
;
repeated
OCOOrder
orders
=
7
;
}
message
GetOCORequest
{
int64
order_list_id
=
1
;
// order_list_id 和 orig_client_order_id 二选一
string
orig_client_order_id
=
2
;
int32
recv_window
=
3
;
// 可选
google.protobuf.Timestamp
timestamp
=
4
;
}
message
GetOCOResponse
{
OCOResponse
oco
=
1
;
}
message
CancelOCORequest
{
string
symbol
=
1
;
int64
order_list_id
=
2
;
// order_list_id, list_client_order_id, 和 new_client_order_id 三选一
string
list_client_order_id
=
3
;
string
new_client_order_id
=
4
;
// 可选
int32
recv_window
=
5
;
// 可选
google.protobuf.Timestamp
timestamp
=
6
;
}
message
CancelOCOResponse
{
OCOResponse
oco
=
1
;
}
// ======================== 账户API消息定义 ========================
message
AccountInfoRequest
{
int32
recv_window
=
1
;
// 可选
google.protobuf.Timestamp
timestamp
=
2
;
}
message
AssetBalance
{
string
asset
=
1
;
string
free
=
2
;
string
locked
=
3
;
}
message
AccountInfoResponse
{
int32
maker_commission
=
1
;
int32
taker_commission
=
2
;
int32
buyer_commission
=
3
;
int32
seller_commission
=
4
;
bool
can_trade
=
5
;
bool
can_withdraw
=
6
;
bool
can_deposit
=
7
;
google.protobuf.Timestamp
update_time
=
8
;
string
account_type
=
9
;
repeated
AssetBalance
balances
=
10
;
repeated
string
permissions
=
11
;
}
message
MyTradesRequest
{
string
symbol
=
1
;
int64
order_id
=
2
;
// 可选
google.protobuf.Timestamp
start_time
=
3
;
// 可选
google.protobuf.Timestamp
end_time
=
4
;
// 可选
int64
from_id
=
5
;
// 可选
int32
limit
=
6
;
// 可选, 默认500
int32
recv_window
=
7
;
// 可选
google.protobuf.Timestamp
timestamp
=
8
;
}
message
MyTrade
{
string
symbol
=
1
;
int64
id
=
2
;
int64
order_id
=
3
;
string
price
=
4
;
string
qty
=
5
;
string
quote_qty
=
6
;
string
commission
=
7
;
string
commission_asset
=
8
;
google.protobuf.Timestamp
time
=
9
;
bool
is_buyer
=
10
;
bool
is_maker
=
11
;
bool
is_best_match
=
12
;
}
message
MyTradesResponse
{
repeated
MyTrade
trades
=
1
;
}
// ======================== 服务定义 ========================
// 系统API服务 - 提供基本系统功能
service
SystemApi
{
// 测试连接
rpc
Ping
(
PingRequest
)
returns
(
PingResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/ping"
};
}
// 获取服务器时间
rpc
GetServerTime
(
ServerTimeRequest
)
returns
(
ServerTimeResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/time"
};
}
// 获取交易所信息
rpc
GetExchangeInfo
(
ExchangeInfoRequest
)
returns
(
ExchangeInfoResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/exchangeInfo"
};
}
}
// 市场数据API - 提供市场数据查询
service
MarketApi
{
// 获取订单簿
rpc
GetOrderBook
(
GetOrderBookRequest
)
returns
(
GetOrderBookResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/depth"
};
}
// 订阅订单簿更新(流)
rpc
SubscribeOrderBook
(
SubscribeOrderBookRequest
)
returns
(
stream
SubscribeOrderBookResponse
)
{}
// 获取订单簿顶部价格
rpc
GetOrderBookTicker
(
GetOrderBookTickerRequest
)
returns
(
GetOrderBookTickerResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/ticker/bookTicker"
};
}
// 获取最近成交
rpc
GetTrades
(
TradesRequest
)
returns
(
TradesResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/trades"
};
}
// 获取历史成交
rpc
GetHistoricalTrades
(
HistoricalTradesRequest
)
returns
(
HistoricalTradesResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/historicalTrades"
};
}
// 获取聚合成交
rpc
GetAggTrades
(
AggTradesRequest
)
returns
(
AggTradesResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/aggTrades"
};
}
// 获取K线数据
rpc
GetKlines
(
KlinesRequest
)
returns
(
KlinesResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/klines"
};
}
// 获取24小时价格变动统计
rpc
Get24hrTicker
(
Ticker24hrRequest
)
returns
(
Ticker24hrResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/ticker/24hr"
};
}
// 获取最新价格
rpc
GetTickerPrice
(
TickerPriceRequest
)
returns
(
TickerPriceResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/ticker/price"
};
}
}
// 交易API服务 - 提供订单操作
service
TradeApi
{
// 测试下单
rpc
TestOrder
(
TestOrderRequest
)
returns
(
TestOrderResponse
)
{
option
(
google.api.http
)
=
{
post
:
"/exchain/v1/order/test"
body
:
"*"
};
}
// 下单
rpc
CreateOrder
(
OrderRequest
)
returns
(
OrderResponse
)
{
option
(
google.api.http
)
=
{
post
:
"/exchain/v1/order"
body
:
"*"
};
}
// 查询订单
rpc
GetOrder
(
OrderStatusRequest
)
returns
(
OrderStatusResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/order"
};
}
// 撤销订单
rpc
CancelOrder
(
CancelOrderRequest
)
returns
(
CancelOrderResponse
)
{
option
(
google.api.http
)
=
{
delete
:
"/exchain/v1/order"
};
}
// 撤销单一交易对的所有订单
rpc
CancelAllOrders
(
CancelAllOrdersRequest
)
returns
(
CancelAllOrdersResponse
)
{
option
(
google.api.http
)
=
{
delete
:
"/exchain/v1/openOrders"
};
}
// 查询当前挂单
rpc
GetOpenOrders
(
OpenOrdersRequest
)
returns
(
OpenOrdersResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/openOrders"
};
}
// 查询所有订单
rpc
GetAllOrders
(
AllOrdersRequest
)
returns
(
AllOrdersResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/allOrders"
};
}
}
// 账户API服务 - 提供账户信息
service
AccountApi
{
// 获取账户信息
rpc
GetAccountInfo
(
AccountInfoRequest
)
returns
(
AccountInfoResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/account"
};
}
// 获取账户交易历史
rpc
GetMyTrades
(
MyTradesRequest
)
returns
(
MyTradesResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/myTrades"
};
}
}
// 订单簿专用服务 - 提供订单簿功能
service
OrderBookApi
{
// 获取完整订单簿
rpc
GetFullOrderBook
(
GetOrderBookRequest
)
returns
(
GetOrderBookResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/orderbook/depth"
};
}
// 获取聚合订单簿(合并相似价格)
rpc
GetAggregatedOrderBook
(
GetOrderBookRequest
)
returns
(
GetOrderBookResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/orderbook/aggregated"
};
}
// 订阅实时订单簿更新
rpc
StreamOrderBook
(
SubscribeOrderBookRequest
)
returns
(
stream
SubscribeOrderBookResponse
)
{}
// 获取最近订单簿更新记录
rpc
GetOrderBookUpdates
(
GetOrderBookRequest
)
returns
(
stream
OrderBookDelta
)
{
option
(
google.api.http
)
=
{
get
:
"/exchain/v1/orderbook/updates"
};
}
}
go.mod
View file @
c1cf3bd2
...
@@ -59,6 +59,8 @@ require (
...
@@ -59,6 +59,8 @@ require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
go.mongodb.org/mongo-driver v1.17.3 // indirect
)
)
require (
require (
...
...
go.sum
View file @
c1cf3bd2
...
@@ -697,6 +697,8 @@ github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtD
...
@@ -697,6 +697,8 @@ github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtD
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
...
@@ -803,6 +805,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
...
@@ -803,6 +805,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment