Skip to content
This repository has been archived by the owner on Oct 30, 2024. It is now read-only.

๐Ÿ‘พ my mev solver running arbitrage strategies for CoW protocol (e.g., running nelder mead simplex optimization)

Notifications You must be signed in to change notification settings

autistic-symposium/searcher-cowswap-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

5 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿค–๐Ÿฎ cowsol, a cow arbitrage solver



tl; dr:

๐Ÿค– solvers are a key component in the cow protocol, serving as the matching engines that find the best execution paths for user orders. in this project, i implement a solver from scratch, running certain arbitrage strategies (e.g., running nelder mead simplex optimization).
๐Ÿ“š more details, check my mirror post, on cowsol, an arb solver for CoW protocol.


current strategies


"no market maker" spread arbitrage

Spread trades are the act of purchasing one security and selling another related security (legs) as a unit.


  • One-leg limit price trade.
    • In this type of order (e.g., orders/instance_1.json), we have a limit price and one pool reserve (e.g., A -> C).
  • Two-legged limit price trade for a single execution path.
    • In this type of order (e.g., orders/instance_2.json), we have a two-legged trade (e.g., A -> B -> C), with only one option for each leg and it can be solved without any optimization.
  • Two-legs limit price trade for multiple execution paths.
    • In this type of order (e.g., orders/instance_3.json), we have a two-legged trade (e.g., A -> B -> C) with multiple pool reserves for each leg (e.g., B1, B2, B3), so the order can be completed dividing it through multiple paths and optimizing for maximum total surplus.


implemented features


liquidity sources

  • Support for constant-product AMMs, such as Uniswap v2 (and its forks), where pools are represented by two token balances.

orders types

  • Support for limit price orders for single order instances.
  • Support for limit price orders for multiple orders on a single reserve instance.
  • Support for limit price orders for multiple orders on multiple reserve instances.


execution specs


A limit order is an order to buy or sell with a restriction on the maximum price to be paid or the minimum price to be received (the "limit price").

This limit determines when an order can be executed:

limit_price = sell_amount / buy_amount >= executed_buy_amount / executed_sell_amount

a good rule of thumb is that the price impact of your order is about twice the size of your order relative to the pool.


surplus

For multiple execution paths (liquidity sources), we choose the best solution by maximizing the surplus of an order:

surplus = exec_buy_amount  - exec_sell_amount / limit_price

amounts representation

All amounts are expressed by non-negative integer numbers, represented in atoms (i.e., multiples of $10^{18}$). We add an underline (_) to results to denote decimal position, allowing easier reading.



order specs


User orders describe a trading intent.

User order specs

  • sell_token: token to be sold (added to the amm pool).
  • buy_token: token to be bought (removed from the amm pool).
  • sell_amount: limit amount for tokens to be sold.
  • buy_amount: limit amount for tokens to be bought.
  • exec_sell_amount: how many tokens get sold after order execution.
  • exec_buy_amount: how many tokens get bought after order execution.
  • allow_partial_fill: if False, only fill-or-kill orders are executed.
  • is_sell_order: if it's sell or buy order.

AMM exec specs

  • amm_exec_buy_amount: how many tokens the amm "buys" (gets) from the user, and it's the sum of all exec_sell_amount of each path (leg) in the order execution.
  • amm_exec_sell_amount: how many tokens the amm "sells" (gives) to the user, and it's the sum of all exec_buy_amount of each path (leg) in the order execution.
  • market_price: the price to buy/sell a token through the user order specs.
  • prior_price: the price to buy/sell a token prior to being altered by the order.
  • prior_sell_token_reserve: the initial reserve amount of the "sell" token, prior to being altered by the order.
  • prior_buy_token_reserve: the initial reserve amount of the "buy" token, prior to being altered by the order.
  • updated_sell_token_reserve: the reserve amount of the "sell" token after being altered by the order.
  • updated_buy_token_reserve: the reserve amount of the "buy" token after being altered by the order.


installing


Install Requirements

python3 -m venv venv
source ./venv/bin/activate
make install_deps

create an .env

cp .env.example .env
vim .env

Install cowsol

make install

Test your installation:

cowsol


usage


Listing available pools in an order instance file

cowsol -a <order file>

Example output:

๐Ÿฎ AMMs available for orders/instance_1.json

{   'AC': {   'reserves': {   'A': '10000_000000000000000000',
                              'C': '10000_000000000000000000'}}}

listing orders in an order instance file

cowsol -o <order file>

Example output:

๐Ÿฎ Orders for orders/instance_1.json

{   '0': {   'allow_partial_fill': False,
             'buy_amount': '900_000000000000000000',
             'buy_token': 'C',
             'is_sell_order': True,
             'sell_amount': '1000_000000000000000000',
             'sell_token': 'A'}}

solving a trade for one-leg limit price

cowsol -s orders/instance_1.json 

For example, for this user order instance:


{
    "orders": {
        "0": {
            "sell_token": "A",
            "buy_token": "C",
            "sell_amount": "1000_000000000000000000",
            "buy_amount": "900_000000000000000000",
            "allow_partial_fill": false,
            "is_sell_order": true
        }
    },
    "amms": {
        "AC": {
            "reserves": {
                "A": "10000_000000000000000000",
                "C": "10000_000000000000000000"
            }
        }
    }
}


Generates this output (logging set to DEBUG):


๐Ÿฎ Solving orders/instance_1.json.
๐Ÿฎ Order 0 is a sell order.
๐Ÿฎ One-leg trade overview:
๐Ÿฎ โž– sell 1000_000000000000000000 of A, amm reserve: 10000_000000000000000000
๐Ÿฎ โž• buy 900_000000000000000000 of C, amm reserve: 10000_000000000000000000
๐ŸŸจ   Prior sell reserve: 10000_000000000000000000
๐ŸŸจ   Prior buy reserve: 10000_000000000000000000
๐ŸŸจ   Spot sell price 1.0
๐ŸŸจ   Spot buy price 1.0
๐ŸŸจ   AMM exec sell amount: 1000_000000000000000000
๐ŸŸจ   AMM exec buy amount: 909_090909090909090909
๐ŸŸจ   Updated sell reserve: 11000_000000000000000000
๐ŸŸจ   Updated buy reserve: 9090_909090909090909091
๐ŸŸจ   Market sell price 1.21
๐ŸŸจ   Market buy price 0.8264462809917356
๐ŸŸจ   Can fill: True
๐Ÿฎ TOTAL SURPLUS: 9_090909090909090909
๐Ÿฎ Results saved at solutions/solution_1_cowsol.json.

And this solution:


{
    "amms": {
        "AC": {
            "sell_token": "C",
            "buy_token": "A",
            "exec_buy_amount": "1000_000000000000000000",
            "exec_sell_amount": "909_090909090909090909"
        }
    },
    "orders": {
        "0": {
            "allow_partial_fill": false,
            "is_sell_order": true,
            "buy_amount": "900_000000000000000000",
            "sell_amount": "1000_000000000000000000",
            "buy_token": "C",
            "sell_token": "A",
            "exec_buy_amount": "909_090909090909090909",
            "exec_sell_amount": "1000_000000000000000000"
        }
    }
}

Note:

  • Input orders are located at orders/.
  • Solutions are located at solutions/.

two-legged limit price trade for a single execution path

cowsol -s orders/instance_2.json 

For example, this user order instance:


{
    "orders": {
        "0": {
            "sell_token": "A",
            "buy_token": "C",
            "sell_amount": "1000_000000000000000000",
            "buy_amount": "900_000000000000000000",
            "allow_partial_fill": false,
            "is_sell_order": true
        }
    },
    "amms": {
        "AB2": {
            "reserves": {
                "A": "10000_000000000000000000",
                "B2": "20000_000000000000000000"
            }
        },        
        "B2C": {
            "reserves": {
                "B2": "15000_000000000000000000",
                "C": "10000_000000000000000000"
            }
        }
    }
}

Generates this (DEBUG) output:

๐Ÿฎ Solving orders/instance_2.json.
๐Ÿฎ Order 0 is a sell order.
๐Ÿฎ FIRST LEG trade overview:
๐Ÿฎ โž– sell 1000_000000000000000000 of A
๐Ÿฎ โž• buy some amount of B2
๐ŸŸจ   Prior sell reserve: 10000_000000000000000000
๐ŸŸจ   Prior buy reserve: 20000_000000000000000000
๐ŸŸจ   Spot sell price 0.5
๐ŸŸจ   Spot buy price 2.0
๐ŸŸจ   AMM exec sell amount: 1000_000000000000000000
๐ŸŸจ   AMM exec buy amount: 1818_181818181818181818
๐ŸŸจ   Updated sell reserve: 11000_000000000000000000
๐ŸŸจ   Updated buy reserve: 18181_818181818181818180
๐ŸŸจ   Market sell price 0.605
๐ŸŸจ   Market buy price 1.6528925619834711
๐ŸŸจ   Can fill: True
๐Ÿฎ SECOND LEG trade overview:
๐Ÿฎ โž– sell 1818_181818181818181818 of B2
๐Ÿฎ โž• buy some amount of C
๐ŸŸจ   Prior sell reserve: 15000_000000000000000000
๐ŸŸจ   Prior buy reserve: 10000_000000000000000000
๐ŸŸจ   Spot sell price 1.5
๐ŸŸจ   Spot buy price 0.6666666666666666
๐ŸŸจ   AMM exec sell amount: 1818_181818181818181818
๐ŸŸจ   AMM exec buy amount: 1081_081081081081081081
๐ŸŸจ   Updated sell reserve: 16818_181818181818181820
๐ŸŸจ   Updated buy reserve: 8918_918918918918918919
๐ŸŸจ   Market sell price 1.8856749311294765
๐ŸŸจ   Market buy price 0.5303140978816655
๐ŸŸจ   Can fill: True
๐Ÿฎ TOTAL SURPLUS: 181_081081081081081081
๐Ÿฎ Results saved at solutions/solution_2_cowsol.json.

And this solution:


{
    "amms": {
        "AB2": {
            "sell_token": "B2",
            "buy_token": "A",
            "exec_buy_amount": "1000_000000000000000000",
            "exec_sell_amount": "1818_181818181818181818"
        },
        "B2C": {
            "sell_token": "C",
            "buy_token": "B2",
            "exec_buy_amount": "1818_181818181818181818",
            "exec_sell_amount": "1081_081081081081081081"
        }
    },
    "orders": {
        "0": {
            "allow_partial_fill": false,
            "is_sell_order": true,
            "buy_amount": "900_000000000000000000",
            "sell_amount": "1000_000000000000000000",
            "buy_token": "C",
            "sell_token": "A",
            "exec_buy_amount": "1081_081081081081081081",
            "exec_sell_amount": "1000_000000000000000000"
        }
    }
}

two-legged limit price trade for multiple execution paths


cowsol -s orders/instance_3.json 

For example, this user order instance:


{
    "orders": {
        "0": {
            "sell_token": "A",
            "buy_token": "C",
            "sell_amount": "1000_000000000000000000",
            "buy_amount": "900_000000000000000000",
            "allow_partial_fill": false,
            "is_sell_order": true
        }
    },
    "amms": {
        "AB1": {
            "reserves": {
                "A": "10000_000000000000000000",
                "B1": "20000_000000000000000000"
            }
        },
        "AB2": {
            "reserves": {
                "A": "20000_000000000000000000",
                "B2": "10000_000000000000000000"
            }
        },        
        "AB3": {
            "reserves": {
                "A": "12000_000000000000000000",
                "B3": "12000_000000000000000000"
            }
        },        
        "B1C": {
            "reserves": {
                "B1": "23000_000000000000000000",
                "C": "15000_000000000000000000"
            }
        },
        "B2C": {
            "reserves": {
                "B2": "10000_000000000000000000",
                "C": "15000_000000000000000000"
            }
        },
        "B3C": {
            "reserves": {
                "B3": "10000_000000000000000000",
                "C": "15000_000000000000000000"
            }
        }
    }
}


Generates this (DEBUG) output:


๐Ÿฎ Solving orders/instance_3.json.
๐Ÿฎ Order 0 is a sell order.
๐Ÿฎ Using the best two execution simulations by surplus yield.
๐Ÿฎ FIRST LEG trade overview:
๐Ÿฎ โž– sell 289_073705673240477696 of A
๐Ÿฎ โž• buy some amount of B1
๐ŸŸจ   Prior sell reserve: 10000_000000000000000000
๐ŸŸจ   Prior buy reserve: 20000_000000000000000000
๐ŸŸจ   Spot sell price 0.5
๐ŸŸจ   Spot buy price 2.0
๐ŸŸจ   AMM exec sell amount: 289_073705673240477696
๐ŸŸจ   AMM exec buy amount: 561_904237334502880476
๐ŸŸจ   Updated sell reserve: 10289_073705673240477700
๐ŸŸจ   Updated buy reserve: 19438_095762665497119520
๐ŸŸจ   Market sell price 0.5293251886038823
๐ŸŸจ   Market buy price 1.8891978343927718
๐ŸŸจ   Can fill: True
๐Ÿฎ SECOND LEG trade overview:
๐Ÿฎ โž– sell 561_904237334502880476 of B1
๐Ÿฎ โž• buy some amount of C
๐ŸŸจ   Prior sell reserve: 23000_000000000000000000
๐ŸŸจ   Prior buy reserve: 15000_000000000000000000
๐ŸŸจ   Spot sell price 1.5333333333333334
๐ŸŸจ   Spot buy price 0.6521739130434783
๐ŸŸจ   AMM exec sell amount: 561_904237334502880476
๐ŸŸจ   AMM exec buy amount: 357_719965038404924081
๐ŸŸจ   Updated sell reserve: 23561_904237334502880480
๐ŸŸจ   Updated buy reserve: 14642_280034961595075920
๐ŸŸจ   Market sell price 1.609169076200932
๐ŸŸจ   Market buy price 0.6214387380354636
๐ŸŸจ   Can fill: True
๐Ÿฎ FIRST LEG trade overview:
๐Ÿฎ โž– sell 710_926294326759522304 of A
๐Ÿฎ โž• buy some amount of B3
๐ŸŸจ   Prior sell reserve: 12000_000000000000000000
๐ŸŸจ   Prior buy reserve: 12000_000000000000000000
๐ŸŸจ   Spot sell price 1.0
๐ŸŸจ   Spot buy price 1.0
๐ŸŸจ   AMM exec sell amount: 710_926294326759522304
๐ŸŸจ   AMM exec buy amount: 671_163952522389243203
๐ŸŸจ   Updated sell reserve: 12710_926294326759522300
๐ŸŸจ   Updated buy reserve: 11328_836047477610756800
๐ŸŸจ   Market sell price 1.1219975504153292
๐ŸŸจ   Market buy price 0.8912675429904732
๐ŸŸจ   Can fill: True
๐Ÿฎ SECOND LEG trade overview:
๐Ÿฎ โž– sell 671_163952522389243203 of B3
๐Ÿฎ โž• buy some amount of C
๐ŸŸจ   Prior sell reserve: 10000_000000000000000000
๐ŸŸจ   Prior buy reserve: 15000_000000000000000000
๐ŸŸจ   Spot sell price 0.6666666666666666
๐ŸŸจ   Spot buy price 1.5
๐ŸŸจ   AMM exec sell amount: 671_163952522389243203
๐ŸŸจ   AMM exec buy amount: 943_426540218806186671
๐ŸŸจ   Updated sell reserve: 10671_163952522389243200
๐ŸŸจ   Updated buy reserve: 14056_573459781193813330
๐ŸŸจ   Market sell price 0.7591582673440884
๐ŸŸจ   Market buy price 1.317248382868167
๐ŸŸจ   Can fill: True
๐Ÿฎ TOTAL SURPLUS: 401_146505257211110752
๐Ÿฎ Results saved at solutions/solution_3_cowsol.json.

And this solution:

{
    "amms": {
        "AB1": {
            "sell_token": "B1",
            "buy_token": "A",
            "exec_sell_amount": "561_904237334502880476",
            "exec_buy_amount": "289_073705673240477696"
        },
        "B1C": {
            "sell_token": "C",
            "buy_token": "B1",
            "exec_sell_amount": "357_719965038404924081",
            "exec_buy_amount": "561_904237334502880476"
        },
        "AB3": {
            "sell_token": "B3",
            "buy_token": "A",
            "exec_sell_amount": "671_163952522389243203",
            "exec_buy_amount": "710_926294326759522304"
        },
        "B3C": {
            "sell_token": "C",
            "buy_token": "B3",
            "exec_sell_amount": "943_426540218806186671",
            "exec_buy_amount": "671_163952522389243203"
        }
    },
    "orders": {
        "0": {
            "allow_partial_fill": false,
            "is_sell_order": true,
            "buy_amount": "900_000000000000000000",
            "sell_amount": "1000_000000000000000000",
            "buy_token": "C",
            "sell_token": "A",
            "exec_buy_amount": "1301_146505257211110752",
            "exec_sell_amount": "1000_000000000000000000"
        }
    }
}

Note: the derivation for the optimization equation for this strategy can be seen here.



features to be added some day


strategies

  • Add support for more than two legs.
  • Add support for more than two pools on two-legged trade.
  • Add multiple path graph weighting and cyclic arbitrage detection using the Bellman-Ford algorithm, so that we can optimize by multiple paths without necessarily dividing the order through them. This would allow obtaining arbitrage profit through finding profitable negative cycles (e.g., A -> B -> C -> D -> A).

liquidity sources

  • Add support for Balancer's weighted pools.
  • Add support for Uniswap v3 and forks.
  • Add support for stable pools.

code improvement

  • Implement support for AMM fees.
  • Add support for concurrency (async), so tasks could run in parallel adding temporal advantage to the solver.
  • Benchmark and possibly add other optimization algorithms options.
  • Add an actual execution class (through CoW server or directly to the blockchains).
  • Finish implementing end-to-end BUY limit orders.
  • Add unit tests.
  • add the bot server (on docker).


resources