Skip to content

Conversation

@karamble
Copy link
Member

@karamble karamble commented Dec 1, 2025

This commit adds full support for Bitget centralized exchange integration to the market maker, including REST API and WebSocket v2 implementations for trading, orderbook synchronization, balance management, and deposit/ withdrawal operations.

Key features:

  • Complete Bitget CEX adapter implementation (client/mm/libxc/bitget.go)
  • WebSocket v2 support for real-time orderbook updates and order status
  • Market data retrieval with proper ticker handling and grouping by Slug
  • Order placement, cancellation, and status tracking
  • Balance fetching and deposit/withdrawal operations
  • Checksum validation for orderbook integrity
  • Order status check before cancellation to prevent unnecessary API calls

Configuration changes:

  • Add APIPassphrase field to CEXConfig for exchanges requiring passphrase authentication (Bitget requires this in addition to API key/secret)
  • Extend CEXConfig interface in libxc to support passphrase
  • Update frontend forms and UI to handle passphrase input for Bitget
  • Add Bitget logo and display information to UI

Bug fixes:

  • Fix omitempty JSON tag behavior for CEXBaseID and CEXQuoteID in BotConfig to properly handle asset ID 0 (BTC). Previously, these fields were omitted from JSON when their value was 0, causing frontend errors when loading bot configurations for BTC markets.
  • Add normalization logic in mm.go to ensure CEXBaseID and CEXQuoteID are always set when CEXName is configured, preventing missing config data for assets with ID 0. Normalization is applied:
    • When loading configs from disk (NewMarketMaker, getMarketMakingConfig)
    • When accessing default config (defaultConfig)
    • When retrieving configs for markets (configsForMarket)
    • Before saving configs (updateDefaultBotConfig)

Order status handling:

  • Implement correct Bitget order status codes (live, partially_filled, filled, cancelled) matching Spot Trading API documentation
  • Add status check in CancelTrade to verify order completion before attempting cancellation, preventing unnecessary API calls and errors
  • Fix TradeStatus to handle array response from Bitget API

Files changed:

  • client/mm/libxc/bitget.go (new file - complete adapter implementation)
  • client/mm/libxc/bgtypes/ (new package - Bitget API types)
  • client/mm/libxc/interface.go (add Bitget constant and passphrase support)
  • client/mm/config.go (add APIPassphrase, fix omitempty tags for CEXBaseID/CEXQuoteID)
  • client/mm/mm.go (add passphrase handling, normalization logic)
  • client/webserver/site/src/js/registry.ts (add passphrase to CEXConfig)
  • client/webserver/site/src/js/forms.ts (add passphrase UI handling)
  • client/webserver/site/src/js/mmutil.ts (add Bitget display info)
  • client/webserver/site/src/html/forms.tmpl (add passphrase input field)
  • client/webserver/locales/*.go (add passphrase translations)
  • client/webserver/site/src/img/bitget.com.png (new file - Bitget logo)

This commit adds full support for Bitget centralized exchange integration
to the market maker, including REST API and WebSocket v2 implementations
for trading, orderbook synchronization, balance management, and deposit/
withdrawal operations.

Key features:
- Complete Bitget CEX adapter implementation (client/mm/libxc/bitget.go)
- WebSocket v2 support for real-time orderbook updates and order status
- Market data retrieval with proper ticker handling and grouping by Slug
- Order placement, cancellation, and status tracking
- Balance fetching and deposit/withdrawal operations
- Checksum validation for orderbook integrity
- Order status check before cancellation to prevent unnecessary API calls

Configuration changes:
- Add APIPassphrase field to CEXConfig for exchanges requiring passphrase
  authentication (Bitget requires this in addition to API key/secret)
- Extend CEXConfig interface in libxc to support passphrase
- Update frontend forms and UI to handle passphrase input for Bitget
- Add Bitget logo and display information to UI

Bug fixes:
- Fix omitempty JSON tag behavior for CEXBaseID and CEXQuoteID in BotConfig
  to properly handle asset ID 0 (BTC). Previously, these fields were omitted
  from JSON when their value was 0, causing frontend errors when loading
  bot configurations for BTC markets.
- Add normalization logic in mm.go to ensure CEXBaseID and CEXQuoteID are
  always set when CEXName is configured, preventing missing config data
  for assets with ID 0. Normalization is applied:
  * When loading configs from disk (NewMarketMaker, getMarketMakingConfig)
  * When accessing default config (defaultConfig)
  * When retrieving configs for markets (configsForMarket)
  * Before saving configs (updateDefaultBotConfig)

Order status handling:
- Implement correct Bitget order status codes (live, partially_filled, filled,
  cancelled) matching Spot Trading API documentation
- Add status check in CancelTrade to verify order completion before
  attempting cancellation, preventing unnecessary API calls and errors
- Fix TradeStatus to handle array response from Bitget API

Files changed:
- client/mm/libxc/bitget.go (new file - complete adapter implementation)
- client/mm/libxc/bgtypes/ (new package - Bitget API types)
- client/mm/libxc/interface.go (add Bitget constant and passphrase support)
- client/mm/config.go (add APIPassphrase, fix omitempty tags for CEXBaseID/CEXQuoteID)
- client/mm/mm.go (add passphrase handling, normalization logic)
- client/webserver/site/src/js/registry.ts (add passphrase to CEXConfig)
- client/webserver/site/src/js/forms.ts (add passphrase UI handling)
- client/webserver/site/src/js/mmutil.ts (add Bitget display info)
- client/webserver/site/src/html/forms.tmpl (add passphrase input field)
- client/webserver/locales/*.go (add passphrase translations)
- client/webserver/site/src/img/bitget.com.png (new file - Bitget logo)
Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of code to review but looks good to me. A few things that stand out.

  1. Saving the book as strings and then parsing many of them many times every update does not seem optimal.

  2. I don't think it is necessary to check the checksum. Am I wrong?

  3. There are many ignored errors, especially for parsing floats. Please check and log all errors. It prevents alot of headaches later to just stick to that rule, unless absolutely not necessary. Even then best to comment why it is absolutely not needed.

if !found && qty != "0" {
sob.asks = sob.insertAsk(price, qty)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two could be combined for less code but it's fine not to change.

Comment on lines +196 to +210
// insertBid inserts a bid maintaining descending price order
func (sob *stringOrderbook) insertBid(price, qty string) [][]string {
priceFloat, _ := strconv.ParseFloat(price, 64)
newBid := []string{price, qty}

for i, level := range sob.bids {
levelPrice, _ := strconv.ParseFloat(level[0], 64)
if priceFloat > levelPrice {
// Insert here
return append(sob.bids[:i], append([][]string{newBid}, sob.bids[i:]...)...)
}
}
// Append at end
return append(sob.bids, newBid)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems really inefficient to parse everything every time rather than just converting once and saving as a float.

There's probably a better structure to save these in as well. Unsure atm though. I guess can optimize later.

Comment on lines +411 to +429
bids, asks, err := b.convertBookUpdate(update)
if err != nil {
b.log.Errorf("%s: error converting book update: %v", b.symbol, err)
return false
}

// Sanity check: Validate rate > 0 (qty=0 is valid for removals)
for _, bid := range bids {
if bid.rate == 0 {
b.log.Errorf("%s: Invalid bid with zero rate: rate=%d, qty=%d", b.symbol, bid.rate, bid.qty)
return false
}
}
for _, ask := range asks {
if ask.rate == 0 {
b.log.Errorf("%s: Invalid ask with zero rate: rate=%d, qty=%d", b.symbol, ask.rate, ask.qty)
return false
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting all the books just to look at the rate and then throwing those away seems wild.

// Mark uint64 cache as dirty (will rebuild on next read)
b.bookDirty.Store(true)

// Validate checksum (if provided and non-zero)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to validate the checksum? Doesn't seem to serve a purpose. I think this should be removed. Can leave some comments noting it is possible... Do their docs say you should do this?

processUpdate := func(update *bgtypes.BookUpdate) bool {
syncMtx.Lock()
defer syncMtx.Unlock()
return acceptUpdate(update, update.IsSnapshot)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use two arguments if one is part of the update struct.

Comment on lines +2122 to +2123
price, _ := strconv.ParseFloat(item[0], 64)
qty, _ := strconv.ParseFloat(item[1], 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please log errors.

Comment on lines +2239 to +2241
if err != nil {
continue
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can log error?

Comment on lines +2365 to +2371
high24h, _ := strconv.ParseFloat(ticker.High24h, 64)
low24h, _ := strconv.ParseFloat(ticker.Low24h, 64)
close, _ := strconv.ParseFloat(ticker.Close, 64)
quoteVol, _ := strconv.ParseFloat(ticker.QuoteVol, 64)
baseVol, _ := strconv.ParseFloat(ticker.BaseVol, 64)
openUtc, _ := strconv.ParseFloat(ticker.OpenUtc, 64)
changeUtc24h, _ := strconv.ParseFloat(ticker.ChangeUtc24h, 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather not ignore errors.

Comment on lines +2806 to +2809
size, _ := strconv.ParseFloat(orderDetail.Size, 64)
baseFilled, _ := strconv.ParseFloat(orderDetail.BaseVolume, 64)
quoteFilled, _ := strconv.ParseFloat(orderDetail.QuoteVolume, 64)
priceAvg, _ := strconv.ParseFloat(orderDetail.PriceAvg, 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather not ignore errors.

return 0, "", ErrWithdrawalPending
}

sizeFloat, _ := strconv.ParseFloat(status.Size, 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather not ignore error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants