first max token market maker

This commit is contained in:
c9s 2020-10-03 11:11:59 +08:00
parent cd80a68e50
commit a8c972dfb1
7 changed files with 189 additions and 16 deletions

View File

@ -32,20 +32,20 @@ type OrderService struct {
type Order struct { type Order struct {
ID uint64 `json:"id,omitempty" db:"exchange_id"` ID uint64 `json:"id,omitempty" db:"exchange_id"`
Side string `json:"side" db:"side"` Side string `json:"side" db:"side"`
OrderType string `json:"ord_type" db:"order_type"` OrderType string `json:"ord_type,omitempty" db:"order_type"`
Price string `json:"price" db:"price"` Price string `json:"price" db:"price"`
AveragePrice string `json:"avg_price,omitempty" db:"average_price"` AveragePrice string `json:"avg_price,omitempty" db:"average_price"`
State string `json:"state,omitempty" db:"state"` State string `json:"state,omitempty" db:"state"`
Market string `json:"market" db:"market"` Market string `json:"market,omitempty" db:"market"`
Volume string `json:"volume" db:"volume"` Volume string `json:"volume" db:"volume"`
RemainingVolume string `json:"remaining_volume,omitempty" db:"remaining_volume"` RemainingVolume string `json:"remaining_volume,omitempty" db:"remaining_volume"`
ExecutedVolume string `json:"executed_volume,omitempty" db:"executed_volume"` ExecutedVolume string `json:"executed_volume,omitempty" db:"executed_volume"`
TradesCount int64 `json:"trades_count,omitempty" db:"trades_count"` TradesCount int64 `json:"trades_count,omitempty" db:"trades_count"`
GroupID int64 `json:"group_id,omitempty" db:"group_id"` GroupID int64 `json:"group_id,omitempty" db:"group_id"`
ClientOID string `json:"client_oid,omitempty" db:"client_oid"` ClientOID string `json:"client_oid,omitempty" db:"client_oid"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `json:"-" db:"created_at"`
CreatedAtMs int64 `json:"created_at_in_ms,omitempty"` CreatedAtMs int64 `json:"created_at_in_ms,omitempty"`
InsertedAt time.Time `db:"inserted_at"` InsertedAt time.Time `json:"-" db:"inserted_at"`
} }
// All returns all orders for the authenticated account. // All returns all orders for the authenticated account.
@ -182,20 +182,22 @@ func (s *OrderService) Get(orderID uint64) (*Order, error) {
// Create multiple order in a single request // Create multiple order in a single request
func (s *OrderService) CreateMulti(market string, orders []Order) ([]Order, error) { func (s *OrderService) CreateMulti(market string, orders []Order) ([]Order, error) {
var returnOrders []Order var returnOrders []Order
req, err := s.client.newAuthenticatedRequest("POST", "v2/orders/multi", map[string]interface{}{ req, err := s.client.newAuthenticatedRequest("POST", "v2/orders/multi/onebyone", map[string]interface{}{
"market": market, "market": market,
"orders": orders, "orders": orders,
}) })
if err != nil { if err != nil {
return returnOrders, errors.Wrapf(err, "failed to create %s orders", market) return returnOrders, errors.Wrapf(err, "failed to create %s orders", market)
} }
response, err := s.client.sendRequest(req) response, err := s.client.sendRequest(req)
if err != nil { if err != nil {
return returnOrders, err return returnOrders, err
} }
if errJson := response.DecodeJSON(&returnOrders); errJson != nil { if errJson := response.DecodeJSON(&returnOrders); errJson != nil {
return returnOrders, errJson return returnOrders, errJson
} }
return returnOrders, err return returnOrders, err
} }

View File

@ -3,6 +3,9 @@ package max
import ( import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/valyala/fastjson" "github.com/valyala/fastjson"
"github.com/c9s/bbgo/pkg/bbgo/types"
"github.com/c9s/bbgo/pkg/util"
) )
type BaseEvent struct { type BaseEvent struct {
@ -165,6 +168,24 @@ type BalanceMessage struct {
Locked string `json:"l"` Locked string `json:"l"`
} }
func (m *BalanceMessage) Balance() (*types.Balance, error) {
available, err := util.ParseFloat(m.Available)
if err != nil {
return nil, err
}
locked, err := util.ParseFloat(m.Locked)
if err != nil {
return nil, err
}
return &types.Balance{
Currency: m.Currency,
Locked: locked,
Available: available,
}, nil
}
func parseBalance(v *fastjson.Value) BalanceMessage { func parseBalance(v *fastjson.Value) BalanceMessage {
return BalanceMessage{ return BalanceMessage{
Currency: string(v.GetStringBytes("cu")), Currency: string(v.GetStringBytes("cu")),

View File

@ -15,6 +15,10 @@ func (v Value) Float64() float64 {
return float64(v) / DefaultPow return float64(v) / DefaultPow
} }
func (v Value) Int64() int64 {
return int64(v)
}
func (v Value) Mul(v2 Value) Value { func (v Value) Mul(v2 Value) Value {
return NewFromFloat(v.Float64() * v2.Float64()) return NewFromFloat(v.Float64() * v2.Float64())
} }
@ -43,3 +47,11 @@ func NewFromString(input string) (Value, error) {
func NewFromFloat(val float64) Value { func NewFromFloat(val float64) Value {
return Value(int64(math.Round(val * DefaultPow))) return Value(int64(math.Round(val * DefaultPow)))
} }
func NewFromInt(val int) Value {
return Value(int64(val * DefaultPow))
}
func NewFromInt64(val int64) Value {
return Value(val * DefaultPow)
}

View File

@ -13,3 +13,13 @@ type Account struct {
AccountType string AccountType string
Balances map[string]Balance Balances map[string]Balance
} }
func (a *Account) UpdateBalance(b Balance) {
a.Balances[b.Currency] = b
}
func NewAccount() *Account {
return &Account{
Balances: make(map[string]Balance),
}
}

View File

@ -3,6 +3,7 @@ package types
import ( import (
"fmt" "fmt"
"sort" "sort"
"sync"
"github.com/c9s/bbgo/pkg/bbgo/fixedpoint" "github.com/c9s/bbgo/pkg/bbgo/fixedpoint"
) )
@ -34,14 +35,27 @@ func (slice PriceVolumeSlice) Trim() (pvs PriceVolumeSlice) {
} }
func (slice PriceVolumeSlice) Copy() PriceVolumeSlice { func (slice PriceVolumeSlice) Copy() PriceVolumeSlice {
// this is faster than make // this is faster than make (however it's only for simple types)
return append(slice[:0:0], slice...) return append(slice[:0:0], slice...)
} }
func (slice PriceVolumeSlice) IndexByVolumeDepth(requiredVolume fixedpoint.Value) int {
var tv int64 = 0
for x, el := range slice {
tv += el.Volume.Int64()
if tv >= requiredVolume.Int64() {
return x
}
}
// not deep enough
return -1
}
func (slice PriceVolumeSlice) InsertAt(idx int, pv PriceVolume) PriceVolumeSlice { func (slice PriceVolumeSlice) InsertAt(idx int, pv PriceVolume) PriceVolumeSlice {
rear := append([]PriceVolume{}, slice[idx:]...) rear := append([]PriceVolume{}, slice[idx:]...)
slice = append(slice[:idx], pv) newSlice := append(slice[:idx], pv)
return append(slice, rear...) return append(newSlice, rear...)
} }
func (slice PriceVolumeSlice) Remove(price fixedpoint.Value, descending bool) PriceVolumeSlice { func (slice PriceVolumeSlice) Remove(price fixedpoint.Value, descending bool) PriceVolumeSlice {
@ -89,13 +103,26 @@ func (slice PriceVolumeSlice) Upsert(pv PriceVolume, descending bool) PriceVolum
return slice return slice
} }
//go:generate callbackgen -type OrderBook
type OrderBook struct { type OrderBook struct {
Symbol string Symbol string
Bids PriceVolumeSlice Bids PriceVolumeSlice
Asks PriceVolumeSlice Asks PriceVolumeSlice
loadCallbacks []func(book *OrderBook)
updateCallbacks []func(book *OrderBook)
bidsChangeCallbacks []func(pvs PriceVolumeSlice)
asksChangeCallbacks []func(pvs PriceVolumeSlice)
} }
func (b *OrderBook) UpdateAsks(pvs PriceVolumeSlice) { func (b *OrderBook) Copy() (book OrderBook) {
book = *b
book.Bids = b.Bids.Copy()
book.Asks = b.Asks.Copy()
return book
}
func (b *OrderBook) updateAsks(pvs PriceVolumeSlice) {
for _, pv := range pvs { for _, pv := range pvs {
if pv.Volume == 0 { if pv.Volume == 0 {
b.Asks = b.Asks.Remove(pv.Price, false) b.Asks = b.Asks.Remove(pv.Price, false)
@ -103,9 +130,11 @@ func (b *OrderBook) UpdateAsks(pvs PriceVolumeSlice) {
b.Asks = b.Asks.Upsert(pv, false) b.Asks = b.Asks.Upsert(pv, false)
} }
} }
b.EmitAsksChange(b.Asks)
} }
func (b *OrderBook) UpdateBids(pvs PriceVolumeSlice) { func (b *OrderBook) updateBids(pvs PriceVolumeSlice) {
for _, pv := range pvs { for _, pv := range pvs {
if pv.Volume == 0 { if pv.Volume == 0 {
b.Bids = b.Bids.Remove(pv.Price, true) b.Bids = b.Bids.Remove(pv.Price, true)
@ -113,17 +142,29 @@ func (b *OrderBook) UpdateBids(pvs PriceVolumeSlice) {
b.Bids = b.Bids.Upsert(pv, true) b.Bids = b.Bids.Upsert(pv, true)
} }
} }
b.EmitBidsChange(b.Bids)
}
func (b *OrderBook) update(book OrderBook) {
b.updateBids(book.Bids)
b.updateAsks(book.Asks)
}
func (b *OrderBook) Reset() {
b.Bids = nil
b.Asks = nil
} }
func (b *OrderBook) Load(book OrderBook) { func (b *OrderBook) Load(book OrderBook) {
b.Bids = nil b.Reset()
b.Asks = nil b.update(book)
b.Update(book) b.EmitLoad(b)
} }
func (b *OrderBook) Update(book OrderBook) { func (b *OrderBook) Update(book OrderBook) {
b.UpdateBids(book.Bids) b.update(book)
b.UpdateAsks(book.Asks) b.EmitUpdate(b)
} }
func (b *OrderBook) Print() { func (b *OrderBook) Print() {
@ -138,3 +179,36 @@ func (b *OrderBook) Print() {
fmt.Printf("- BID: %s\n", bid.String()) fmt.Printf("- BID: %s\n", bid.String())
} }
} }
type MutexOrderBook struct {
sync.Mutex
*OrderBook
}
func NewMutexOrderBook(symbol string) *MutexOrderBook {
return &MutexOrderBook{
OrderBook: &OrderBook{Symbol: symbol},
}
}
func (b *MutexOrderBook) Load(book OrderBook) {
b.Lock()
defer b.Unlock()
b.Reset()
b.update(book)
b.EmitLoad(b.OrderBook)
}
func (b *MutexOrderBook) Get() OrderBook {
return b.OrderBook.Copy()
}
func (b *MutexOrderBook) Update(book OrderBook) {
b.Lock()
defer b.Unlock()
b.update(book)
b.EmitUpdate(b.OrderBook)
}

View File

@ -0,0 +1,45 @@
// Code generated by "callbackgen -type OrderBook"; DO NOT EDIT.
package types
import ()
func (b *OrderBook) OnLoad(cb func(book *OrderBook)) {
b.loadCallbacks = append(b.loadCallbacks, cb)
}
func (b *OrderBook) EmitLoad(book *OrderBook) {
for _, cb := range b.loadCallbacks {
cb(book)
}
}
func (b *OrderBook) OnUpdate(cb func(book *OrderBook)) {
b.updateCallbacks = append(b.updateCallbacks, cb)
}
func (b *OrderBook) EmitUpdate(book *OrderBook) {
for _, cb := range b.updateCallbacks {
cb(book)
}
}
func (b *OrderBook) OnBidsChange(cb func(pvs PriceVolumeSlice)) {
b.bidsChangeCallbacks = append(b.bidsChangeCallbacks, cb)
}
func (b *OrderBook) EmitBidsChange(pvs PriceVolumeSlice) {
for _, cb := range b.bidsChangeCallbacks {
cb(pvs)
}
}
func (b *OrderBook) OnAsksChange(cb func(pvs PriceVolumeSlice)) {
b.asksChangeCallbacks = append(b.asksChangeCallbacks, cb)
}
func (b *OrderBook) EmitAsksChange(pvs PriceVolumeSlice) {
for _, cb := range b.asksChangeCallbacks {
cb(pvs)
}
}

View File

@ -22,6 +22,15 @@ func FormatFloat(val float64, prec int) string {
return strconv.FormatFloat(val, 'f', prec, 64) return strconv.FormatFloat(val, 'f', prec, 64)
} }
func ParseFloat(s string) (float64, error) {
if len(s) == 0 {
return 0.0, nil
}
return strconv.ParseFloat(s, 64)
}
func MustParseFloat(s string) float64 { func MustParseFloat(s string) float64 {
if len(s) == 0 { if len(s) == 0 {
return 0.0 return 0.0