Apro questo thread principalmente per 2 motivi:
- In giro c' è veramente poca documentazione riguardo questo argomento
- Volevo condividere la mia esperienza
Partiamo con la documentazione che ho trovato nell' internet:
Cos' è l' arbitraggioTriangular ArbitrageArbitrage on DexModello economico di UniswapV2 e relativi contratti e funzioniUniswapV2Price ImpactDocumentazione un po più avanzataDex Arbitrage SolidityCyclic ArbitrageGraph on ArbitragePer avere un sistema di arbitraggio funzionante quindi, è necessario sviluppare 2 blocchi principali
- Uno strumento che cerca le opportunità di arbitraggio
- Uno strumento che, attivato dal precedente, processa le operazioni
La parte interessante dell' arbitraggio in Defi è che il secondo strumento può essere sviluppato interamente come uno smart contract con tutti i benefici che ne conseguono.
Inutile dire che l' arbitraggio manuale è un' opzione poco perseguibile quindi per entrambi gli strumenti si dovrà ricorrere a linguaggi di programmazione (personalmente ho utilizzato python con web3py e solidity), inoltre è possibile dividere le classi di arbitraggio in 2 categorie in base al numero di coin scambiate:
- A 2 vie: A->B B->A
- A 3 vie: A->B B->C C->A
Infine è necessario precisare che il resto della trattazione assume come "unico" Dex di riferimento i derivati di UniswapV2. Nulla vieta di estendere anche ad altri Dex non uniswaplike ma i meccanismi potrebbero essere diversi.
Ricerca di opportunità di arbitraggioCi sono diversi metodi per eseguire questa operazione, personalmente ne ho testati 2.
Il primo consiste nell' identificare gli address dei pair corrispondenti ai token di interesse e successivamente sfruttare la function
balanceOf dei token ERC20 inserendo l' address del pair (o pool). Quindi nel codice avremo una costruzione del tipo:
Contract=web3.eth.Contract(address="tokenA",abi="L' abi ERC20") //Inizializzo il contratto del tokenA
Balance1=Contract.functions.balanceOf("pair").call() // "Contract" è un oggetto con dei metodi (functions) definite dall' abi e chiamiamo (call) la function balanceOf per ottenere l' ammontare di tokenA presenti nella pool tokenA-tokenB
Nota: Pair e Pool sono la stessa cosa: in UniswapV2 vengono chiamati Pair, in uniswapV3 Pool
L' altro metodo invece sfrutta la function getReserves del contratto del pair. Questa function non ha input e ritorna l' ammontare dei token e l' ultimo blocco di aggiornamento della pair.
Attenzione: questa function ritorna le riserve R0 e R1 dei token0 e token1 del pair che sono hardcoded nel contratto del pair. Serve dunque mappare correttamente (token0,token1) con (tokenA,tokenB) per avere le riserve corrette RA e RB. Nel codice:
//Inizializzo il contratto
Contract=web3.eth.Contract(address=pair,abi=abi_pair)
// Ottengo i token0 e token1
token0=Contract.functions.token0().call()
token1=Contract.functions.token1().call()
//Prendo le riserve
(R0,R1,BLK)=Contract.functions.getReserves().call()
//indicizzo correttamente le riserve
if token0==tokenA
RA=R0; RB=R1
else
RA=R1; RB=R0
Indipendentemente dal procedimento scelto alla fine avremo le riserve (o balance) della pool e quindi è necessario calcolare il prezzo. Purtroppo entrambi i metodi ritorano un intero ma i token ERC20 possono avere decimali diversi quindi prima di calcolare il prezzo è necessario scalare le riserve:
Contract=web3.eth.Contract(address="tokenA",abi="L' abi ERC20") //Inizializzo il contratto del tokenA
decimale=Contract.functions.decimals().call()
RA_=RA/10^decimale
Fatta la stessa procedura anche per il token B è possibile calcolare il prezzo dei token della pool come (RA_/RB_) oppure come (RB_/RA_)
Iterando per diversi Pair (ovvero per diversi dex) con gli stessi tokenA/tokenB è possibile avere i diversi prezzi ovvero valutare l' opportunità.
Adesso, per semplicità concentriamoci sull'
arbitraggio a 2 vie in cui ricapitolando, è possibile costruirsi un insieme di prezzi semplicemente vedendo le riserve delle pool tokenA-tokenB sui vari dex.
In questo insieme estraiamo banalmente il massimo e il minimo e assumiamo di partire dal tokenA e che i prezzi siano calcolati secondo RB_/RA_.
In questo modo è abbastanza chiaro che il primo swap dovrà essere fatto sul dex con prezzo massimo (in quanto è quello che dà più tokenB) mentre lo swap di chiusura sarà fatto su quello con prezzo minimo (a parità di tokenB è quello che mi dà più tokenA)
In questo modo si riesce ad identificare un percorso da seguire in cui valutare se è possibile fare l' arbitraggio o meno.
Opportunità di arbitraggio a 2 vieDalla documentazione di uniswap è chiaro che tutti i dex in questione hanno il modello x*y=k. Questo significa che, identificate come Lx e Ly le riserve rispettivamente dei token x e y. in uno swap, la quantità Lx*Ly deve rimanere costante e uguale a k. Questo significa che è possibile calcolare la quantità Y di token y che si ricevono assumendo di swappare una quantità X di token x. Infatti la nuova liquidità sarà Lx+X -> Ly_new=k/(Lx+X) -> Y=Ly-Ly_new
in definitiva è possibile esprimere Y come Y=X*Ly/(Lx+X)
Tramite questa formula è possibile proseguire con lo swap di ritorno (ovvero X'=Y*Lx'/(Ly'+Y)) e l' arbitraggio sarà profittevole solo se P=X'-X-fee>0
Questo è il primo traguardo: quindi ora vi basta impostare un ammontare X -> ottenere le liquidità -> prendere massimo e minimo -> calcolare il profitto e vedere se è maggiore di 0. In caso di esito positivo farete eseguire gli swap dallo smart contract.
Per chi è un po più perfezionista, è possibile calcolare l' ammontare di X che massimizza il profitto P=X'-X-fee:Infatti P è una frazione nella forma (-aX^2+bX+c)/(aX+c/fee) dove a,b,c sono quantità che dipendono dalle liquidità delle 2 pool e dalle fee.
E' dunque possibile calcolare la derivata di P rispetto ad X -> porla uguale a 0 ed ottenere dunque il valore di X che massimizza la funzione P.
Opportunità di arbitraggio a 3 viePer quanto riguarda l' arbitraggio a 3 vie il procedimento è molto più complesso in quanto la discriminante max min non può essere usata nello stesso modo quindi sono necessarie altre tecniche che però non ho ancora personalmente sperimentato
Esecuzione degli swapCome accennato precedentemente una volta trovato un percorso profittevole, è necessario eseguire le operazioni e quindi mandare le transazioni. L' idea di base è incorporare tutte le transazioni in una utilizzando uno smart contract.
Per farlo è necessario importare l' interfaccia del router di UniswapV2 e creare una function nel contratto che esegua le operazioni.
Questa parte è spiegata molto bene (per l' arbitraggio a 2 vie) in questo link presente anche nella documentazione. In caso di arbitraggio a 3 vie basta aggiungere il terzo swap.
MiglioramentiIl procedimento sopra vuole dare delle linee guida su come approcciare la creazione di un sistema di arbitraggio automatizzato ma chiaramente ha ampi margini di miglioramento. Per definizione, l' arbitraggio è profittevole solo ai primi (o al primo) che eseguono gli swap, quindi la velocità è tutto: è necessario ridurre i tempi di raccolta delle info, del processamento e del relativo input. In Defi la velocità è misurata in blocchi convalidati infatti generalmente, in situazione di equilibrio, non c' è un percorso profittevole -> serve dunque una transazione che sbilancia il mercato che sarà convalidatà la blocco B1 quindi più si è vicini al blocco B1 (
incluso) e più è probabile che l' arbitraggio abbia successo.
- Riuscire a convalidare la transazione di arbitraggio lo stesso blocco di quella che sbilancia il mercato è dunque il miglior modo possibile, ma per farlo è necessario valutare le pending transactions -> simulare le nuove riserve -> da quelle calcolare la profittabilità. Il tutto in un lasso di tempo che va da quando la transazione è in pending a quando viene confermata.
- Utilizzare la teoria dei grafi per trovare percorsi profittevoli. In rete è presente diversa documentazione a riguardo (nessuna pappa pronta ma sempre linee guida) e se riesco a ritagliare un po di spazio vorrei dedicargli del tempo
- Riuscire ad indentificare i token con fee sugli swap (tipo buyback,transfer fee ecc..) e valutarne la profittabilità
Parere personaleDa quello che ho visto, ce ne sono parecchi di bot di arbitraggio che operano tra i vari dex. Utilizzano metodi anche a volte diversi, per esempio alcuni utilizzano aggregatori tipo 1inch con i chitoken, altri invece non fanno un arbitraggio in senso stretto ma appena vedono in pending una transazione "grossa" rispetto la liquidità della pool, si muovono sia prima che dopo la transazione (nello stesso blocco).
Quindi è un mondo molto competitivo dove però i margini possono essere anche molto molto alti.