ArbitrageCryptocurrencies
created at
Cosmos Arbitrage Bot 2
This is a continuation of the previous article.
In the previous article, we came to the point where we used the following code to get the price information from the API on the network.
func main() {
active_pools := GetActivePools()
whole_pools := GetPoolAll()
}
Define the obtained data using a structure in Go language.
First, define the type of each coin.
type Coin struct { Denom string CoinID uint }
Each coin has Denom (like the name of the coin) information and CoinID as constants. The coins then exist in pairs in a pool that exists on the network.
For example, if you want to exchange ATOMs and OSMOs on the Dex, you would use "Pool 1" on the Osmosis DEX. The exchange rate is determined by the amount of ATOM and OSMO in "Pool 1" through a mechanism called AMM. Please refer to official documentation for the detailed calculation method. We will use it later in our calculations.
GAMM on Osmosis
Now let's define that pool in a structure. But first, let's take a quick look at the basic AMM mechanism.
Let's quote from the official documentation When a token swap occurs within a pool, the amount of tokens released from the pool = the amount of tokens obtained by the swapped user is defined by the following formula.
Where tokenBalanceIn refers to the total amount of tokens in the pool that the user is trying to Swap, and conversely tokenBalanceIn refers to the total amount of tokens in the pool that the user is trying to acquire. tokenAmountIn,tokenAmountOut and are the pure amount of tokens that are about to be swapped/acquired.
tokenWeightIn and tokenWeightOut may not be defined in many common Dexes such as Uniswap. It is a factor that is applied to the amount of each token pool when calculating tokens and is used to define the spot price of a token as
(tokenBalanceIn / tokenWeightIn) / (tokenBalanceOut / tokenWeightOut)
This weighting mechanism provides the advantage that even if a given token is not liquid enough, a larger weighting ensures that the spot price will not deviate too far from the market's fair price. **However, this mechanism makes the actual calculation of arbitrage very complicated. **
Let's put that complicated calculation aside for now and move on to the pool type definition.
Let's assume that tokenBalanceOut is Ry, tokenBalanceIn is Rx, and tokenAmountOut and tokenAmountIn are Dy and Dx, respectively. The weights are Wx and Wy, respectively. In addition to these six parameters, each pool has the token (Coin) information, the pool ID, and the cost factor for using the pool (1 - swapFee section of the document).
type Pool struct { ID int PoolType string Token1 Coin Token2 Coin Rx float64 Ry float64 Wx float64 Wy float64 Dx float64 Dy float64 CostFactor float64 }
We will create a function like arrangePoolInformation() to define the information obtained in previous article in this structure. Let's look again at the information obtained here.
type PoolAssets struct { Type string `json:"@type"` Id string Pool_params struct { Swap_fee string Exit_fee string } Pool_assets [2]struct { Token struct { Denom string Amount string } Weight string } }
First, Swap_fee and Exit_fee on Pool_params correspond to Cost Factor. Then, in the Pool_assets array, there are two types of tokens, each of which has information on Denom and Amount. The Pool definition seems to be okay since it also contains the Weight information. The good thing about the Go language is that the type definitions are well defined.
Now, let's take a look at the first parameter present, Type, and if you use Curl to briefly check the contents, you will see that there is a parameter that you do not understand. This is the pool type defined on the Cosmos SDK, and it changes depending on the type of pool, such as stabled swap or multicurrency swap. We will focus on a basic pool called /osmosis.gamm.v1beta1.Pool.
Now, the actual code will look like this
import(
"math"
"strconv"
)
func arrangePoolInformation(pool PoolAssets, instrumentMap map[string]uint, decimalMap map[string]int) *Pool {
if pool.Type == "/osmosis.gamm.v1beta1.Pool" {
ins_x := pool.Pool_assets[0].Token.Denom
ins_y := pool.Pool_assets[1].Token.Denom
Rx, _ := strconv.ParseFloat(pool.Pool_assets[0].Token.Amount, 64)
Ry, _ := strconv.ParseFloat(pool.Pool_assets[1].Token.Amount, 64)
Rx = Rx / math.Pow10(decimalMap[ins_x])
Ry = Ry / math.Pow10(decimalMap[ins_y])
_Wx, _ := strconv.ParseFloat(pool.Pool_assets[0].Weight, 64)
_Wy, _ := strconv.ParseFloat(pool.Pool_assets[1].Weight, 64)
Wx := _Wx / (_Wx + _Wy)
Wy := _Wy / (_Wx + _Wy)
fee, _ := strconv.ParseFloat(pool.Pool_params.Swap_fee, 64)
token1 := Coin{Denom: ins_x, CoinID: instrumentMap[ins_x]}
token2 := Coin{Denom: ins_y, CoinID: instrumentMap[ins_y]}
return &Pool{
Token1: token1,
Token2: token2,
Rx: Rx,
Ry: Ry,
Wx: Wx,
Wy: Wy,
Dx: math.Pow10(decimalMap[ins_x]),
Dy: math.Pow10(decimalMap[ins_y]),
CostFactor: 1 - fee,
}
} else {
return nil
}
}
Actually, the code will not work as is because we are still missing two important pieces of information here: the instrumentMap and the decimalMap.
In the next article, I will explain these two about Map (like a dictionary), and then this time I will move on to the implementation of the Bellman-Ford algorithm.