created at

CosmosアビトラBotの作り方 その4【コード付き】

前回の続きです。前回は一例としてベルマンフォード法を用いれば、アービトラージパスを探索できるよというところまで話しました。 今、手元にはプールの情報があるので実際に探索してみましょう。

AMMで放出されるトークンの量を算出

まずは、AMM上で、あるトークンをスワップしたときに放出されるトークンの量を算出する関数を書いていきます。今回は構造体Poolのメソッドとして定義していきます。

func (p *Pool) CalcTokenOutAmount(TokenInAmount float64, tokenOut uint) float64 {
	if tokenOut == p.Token2.CoinID {
		return p.Ry * (1 - math.Pow((p.Rx/(p.Rx+p.CostFactor*TokenInAmount)), (p.Wx/p.Wy)))
	} else if tokenOut == p.Token1.CoinID {
		return p.Rx * (1 - math.Pow((p.Ry/(p.Ry+p.CostFactor*TokenInAmount)), (p.Wy/p.Wx)))
	} else {  //なんでこういう風に書いたのか思い出せない。
		return 1e10
	}
}

その2の記事で紹介した下の式をそのまま使っています。

tokenBalanceOut[1tokenBalanceIn/(tokenBalanceIn+(1swapFee)tokenAmountIn)(tokenWeightIn/tokenWeightOut)]tokenBalanceOut * [1 - { tokenBalanceIn / (tokenBalanceIn + (1 - swapFee) * tokenAmountIn)} ^ {(tokenWeightIn/tokenWeightOut)}]

また、RxRxRyRy(プール内のトークンの量)を更新するメソッドも用意します。

func (p *Pool) UpdatePool(Rx, Ry float64) {
	p.Rx = Rx
	p.Ry = Ry
}

最適なパスの探索

あるトークンをX量分スワップした時に、上で計算した放出されるトークンの量をdXとします。グラフに設定すべきトークンの交換レートはdX/X(前回説明した通りlog-\logを取ります)となります。これをプール上のすべてのトークンに対して計算し、グラフを完成させていきます。

注意しなければならないのは、AMMではXの値によって交換レートdX/Xの値も大きく変わってくるということです。最適なXの量は何なんだという話はひとまず置いておいて、まずは10ドル分のトークンをスワップした時に導出される交換レートからグラフを完成させてみます。(この時に各トークンのおおよその価格を入れたpriceMapが役立ちます)

package main

import (
	bf "cosmos-arb/algorithms"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"strconv"
)


```-------------関数/構造体の定義部分は長くなるので省略。前回までの記事を参考にしてください-------------```

func main() {
	active_pools := GetActivePools()
	whole_pools := GetPoolAll()

	decimalMap := GetDecimalMap()

	priceMap, instrumentMap, verticles := getInstrumentDictionaries(active_pools)

	pool_information := make(map[int]*Pool)
	poolMap := make(map[[2]uint]int)

     //とりあえプールID800まで使う。それ以降は流動性に欠ける。
	for pool_id := range active_pools {
		if pool_id > 800 {
			delete(active_pools, pool_id)
		}
	}

	for pool_id := range active_pools {
		arranged_pool := arrangePoolInformation(whole_pools.Pools[pool_id-1], instrumentMap, decimalMap)

		if arranged_pool != nil {
			pool_information[pool_id] = arranged_pool
			poolMap[[2]uint{pool_information[pool_id].Token1.CoinID, pool_information[pool_id].Token2.CoinID}] = pool_id
			poolMap[[2]uint{pool_information[pool_id].Token2.CoinID, pool_information[pool_id].Token1.CoinID}] = pool_id
		}
	}

	fmt.Println("------Initialization Completed-----")

	var token1, token2 uint
	var Rx, Ry, X, Y, dX, dY float64
	var edges []*bf.Edge

	const default_amount float64 = 10

 //実際のプールの情報はwhole_poolsに格納されている。アクティブなプールのID毎にプール内での交換レートをグラフのEdgeに渡していく。
	for pool_id := range active_pools {
		new_pool := whole_pools.Pools[pool_id-1]
		if new_pool.Type == "/osmosis.gamm.v1beta1.Pool" {

			Rx, _ = strconv.ParseFloat(new_pool.Pool_assets[0].Token.Amount, 64)
			Ry, _ = strconv.ParseFloat(new_pool.Pool_assets[1].Token.Amount, 64)

             //前回説明分:小数点以下の情報を元に実数値に変換
			Rx = Rx / pool_information[pool_id].Dx
			Ry = Ry / pool_information[pool_id].Dy

			pool_information[pool_id].UpdatePool(Rx, Ry)

			token1 = pool_information[pool_id].Token1.CoinID
			token2 = pool_information[pool_id].Token2.CoinID

               //default_amount = 10ドル分に相当するトークンの量
			X = default_amount / priceMap[pool_information[pool_id].Token1.Denom]
			Y = default_amount / priceMap[pool_information[pool_id].Token2.Denom]

			dX = pool_information[pool_id].CalcTokenOutAmount(X, token2)
			dY = pool_information[pool_id].CalcTokenOutAmount(Y, token1)

			edges = append(edges, bf.NewEdge(token1, token2, -math.Log(dX/X)))
			edges = append(edges, bf.NewEdge(token2, token1, -math.Log(dY/Y)))

		}
	}

	g := bf.NewGraph(edges, verticles)

     //OSMOトークンを始点にして計算。
	path := g.FindArbitrageLoop(instrumentMap["uosmo"])

	fmt.Println("Arbitrage Path: ", path)

}

ここで提示したコードを何回か回してくるとたまに空以外の結果が返ってくるかと思います(またはwhole poolsだけを更新して5秒毎くらいにwhileループを回してみてください)。そのなかで儲かるポテンシャルがあるのは下に挙げた良さそうな例です。

  • ダメな例:Arbitrage Path: [86 86], Arbitrage Path: [86 0 86] ←こういうのは取り除く必要がある。

  • 良さそうな例:Arbitrage Path: [86 119 24 86]

上記のコードでは始点をOSMOトークンにしましたので、始点と終点はOSMOトークンとなっています。(上の例ではトークンIDは86となっているが、このIDは実行毎に変わってきます)

問題となってくるのは、上に挙げたような良さそうなパスを見つけたとしても、ガス代によっては実際は儲からないかもしれないということです。となると気になってくるのは、見つかったパスをもとに実際に注文を行うとどうなるのかということだと思いますので、次回からはOsmosisネットワークにトランザクションを送る準備に移ります。果たして、ちゃんと儲かるのでしょうか?

とりあえず、次回はトランザクションの生成、署名に必要なウォレットを定義していくところから始めたいと思います。


CosmosアビトラBotの作り方 その4【コード付き】
コメント
新着記事
Copyright © 2023 Coin News DAO. All rights reserved.

Site Map

Twitter(X)