This is a project that I initially wrote in Vlang (to learn it), but later rewrote it in Go because the vlang program was frequently segfaulting. In retrospect, I now realize that the vlang code that I wrote is horrible. There’s a lot of redundancy, misuse of pointers, and the code would have been more readable and maintanable if I had used OOP (object oriented programming) design patterns.

Well anyways, this trading bot buys and sells crypto on the Binance exchange. To do so, it interacts with the Binance spot and websocket API.

The bot can trade using different strategies. Right now, only a simple strategy is implemented. This strategy basically involves buying and selling at user specified margins. Also, the behavior of the bot can be customsed through a config file that looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
base: "BTC"
quote: "USDT"
trading-balance: 0.0015
state-file: "state.json"
first-tx: "buy"
percent-change-sell: 0.30
percent-change-buy: 0.25
server-base-endpoint: "testnet.binance.vision"
ws-server-base-endpoint: "testnet.binance.vision"
save-receipt: true
db-backend: "sqlite"
db-url: "receipts.db"
log-level: "info"

Conerning the business logic of the bot, here is an extract of the tryBuy function, which checks if an asset should be bought. The code comments are pretty self-explanatory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// tryBuy tries to place a buy order.
// It checks if the percentage change between the last sell price
// and the current price is greater than the specified percentage change.
func tryBuy(botCfg *BotCfg, state *State, client *binance.Client, price float32) (err error) {
  pChange := percentChange(state.Data.LastSellPrice, price)

  if botCfg.StopEntryPrice != 0 && !shouldBuyStopEntry(botCfg, pChange) {
    log.Debug().Msgf(
     "NOT buying, percent change between price and stop entry price @%.5f",
     pChange,
    )
    return
  } else if !shouldBuy(botCfg, pChange) {
    log.Debug().Msgf(
      "NOT buying price @%.5f %s/%s, percent change @%.5f",
      price,
      botCfg.Base,
      botCfg.Quote,
      pChange,
    )
    return
 }

  return orderMarket(botCfg, state, client, orderSideBuy)
}

// shouldBuy checks if the bot should buy. It compares the percentage change
// between the last sell price and the current price.
func shouldBuy(botCfg *BotCfg, pChange float32) bool {
	return pChange >= botCfg.PercentChangeBuy
}

// shouldBuyStopEntry checks if the bot should buy if a stop entry price is set.
// It compares the percentage change between the stop entry price and the current price.
func shouldBuyStopEntry(botCfg *BotCfg, price float32) bool {
	pChange := percentChange(botCfg.StopEntryPrice, price)
	if pChange < 0 {
		pChange = -pChange
	}

	return pChange <= botCfg.StopEntryPriceMargin
}

That’s all, thank you for reading.