Simple Arbitrage Keeper
Overview
Typically in the form of an automated bot, Keepers are external actors that participate in market making, auctions, market arbitrage, and system upkeep within the Maker protocol and the greater Ethereum ecosystem. Under a pure economic assumption, they are solely incentivized by profits or a vested interest. However, their activity provides indirect services such as increased liquidity and price consistency across various financial markets. This guide presents a simple arbitrage keeper, and structure thereof, that can be used out of the box to participate in wildly volatile markets. The purpose of the guide is to motivate the user in improving its functionality to participate in less volatile markets, leading to a more efficient financial ecosystem.
Learning objectives
By reading this guide, you’ll gain:
Better understanding of Arbitrage, how it is achieved and the role it plays in financial markets
An overview of the simple arbitrage keeper and common structure across the Maker Keeper framework
Insight into the operation of the simple arbitrage keeper and ways to improve its code
Pre-requisites
At least Python 3.6.2 and some python experience
Linux, macOS, or Cygwin (on Windows)
Some general experience with Ethereum Development (account management, contract interaction, node hosting)
Guide
Introduction to Arbitrage
Arbitrage is the process of simultaneously purchasing an asset on one exchange and selling a similar, if not identical, asset on another exchange at a higher price. Conceptually, this trading strategy can be broken down into two legs, the first buying leg and the second selling leg. As the timestamp of these legs converge, the risk of asset exposure approaches zero. For example, if both trades are not executed in perfect synchrony, there is a chance that the second trade is completed by another actor, forcing the arbitrageur to hold an asset until another opportunity arises.
With advancement in technology, this trading strategy is typically automated and can detect, as well as, profit from price deviations in a matter or seconds. Arbitrage opportunities exist because of market inefficiencies, such as a temporary shortage of liquidity. As a result, arbitrage provides a mechanism to ensure prices on one exchange/market do not deviate far from the fair value. 1
System Process Structure
Operation of the Simple Arbitrage Keeper
The simple-arbitrage-keeper is an arbitrage keeper for OasisDex and Uniswap. Given a minimum profit per trade and maximum trade size, as defined by the user and in units of entry_token
, the Keeper will scan for arbitrage opportunities. When running, it monitors the arb_token/entry_token
price on both exchanges, and when a discrepancy is detected, it executes an atomic multi-step trade in a single Ethereum transaction. The multi-step trade is made up of two legs: the first leg is the purchasing of arb_token
with the entry_token
on the start_exchange
, while the second leg is the selling of the arb_token
with the entry_token
on the end_exchange
. The type of entry_token
and arb_token
is typically of a stable and volatile type, respectively; since arbitrage opportunities can be scarce, the Keeper sometimes rests in a default state, holding the entry_token
for extended periods of time as it looks for new opportunities. Exposure only to a stable token is especially important in this default state. If any leg of the trade fails, the entire trade reverts, thereby upholding the "risk-free" attribute of true arbitrage.
It inherits a system structure that is consistent with other keepers in the Maker Keeper Framework. Apart from libraries typical to python projects, it relies on pymaker and pyexchange, both of which are highly applicable Python APIs for the Maker Protocol Contracts and cryptocurrency exchanges, respectively. As can be seen in the flowchart, the ‘Lifecycle box’ is a pymaker utility class that helps define the proper life cycle of a keeper; it consists of keeper startup, timers and/or subscriptions to Web3 events, and a keeper shutdown phase. The ‘Process block’ box is executed when the Keeper's node receives a new block. It generally contains the logic to query the blockchain state, evaluate any profit opportunities, and when applicable, publish a transaction.
The step for each process in simple-arbitrage-keeper is as follows:
Keeper Entry -> enter into SimpleArbitrageKeeper class
Startup -> make all necessary approvals between tokens, exchanges, and tx-manager
Evaluate Market -> pull price data from Oasis REST API and Uniswap exchange contracts
Profitable Opportunity Found? ->check if profit opportunity exists with a given max engagement and min profit
Execute multi-step trade -> with use of TxManager, buy asset on exchange A and sell on exchange B within one Ethereum transaction
Documentation
General high level information and comments for each class/method are in the simple-arbitrage-keeper github repository.
Startup on Kovan
The usage of the bot can be found in the Usage section of the README. Here are some preparation steps in running on Kovan:
To operate the keeper, call the /bin/simple-arbitrage-keeper
script with the required arguments. This adds the pyexchange and pymaker modules to the python path and initializes the SimpleArbitrageKeeper class. It's convenient to write a shell script that can be easily rerun after argument changes, but before we begin, let's prepare our Keeper ethereum address, deploy our TxManager, find the Uniswap exchange addresses and MatchingMarket address.
Keeper Installation
This project uses Python 3.6.2. In order to clone the project and install required third-party packages please execute:
For some known Ubuntu and macOS issues see the pymaker README.
Install the DappHub Toolkit
If you are running Linux or macOS you can take advantage of our all in one installer.
If you're having issues with installation, head over to the 'Manual Installation' section
Keeper Ethereum Address
Your Keeper will publish transactions through this address. Your ethereum address needs to be accessible by seth, in order to deploy TxManager through dapp.tools. Furthermore, when the Keeper is deployed and in operation, this address must contain some ETH for transaction costs as well as an amount of entry-tokens equal to the max-engagement set by the User. Follow these instructions to ensure seth can see your keystore and passphrase files.
If you'd like to start from scratch, install geth and use geth account new
, to create a new account, set a passphrase, and place the passphrase into a <address>.txt
file in the ~/Library/Ethereum/passphrase/
directory; the corresponding keystore file can be found in the ~/Library/Ethereum/keystore/
directory. Both the keystore and passphrase files will later be passed into the Keeper as an argument. Make a note of your Keeper Ethereum Address, its keystore file location and passphrase file location.
Deploy TxManager
A TxManager must be deployed and linked with the Keeper ethereum address in order to bundle multiple contract calls in a single ethereum transaction; this is what we call an atomic transaction, where the entire call set is successful, or the state remains unaffected. Once the DappHub Toolkit is installed, execute the following commands to deploy TxManager:
The output of dapp create
is the address to your TxManager
Deploy/Find a parity node
You need access to a JSON-RPC host that’s connected to a parity node. Unfortunately, Infura APIs are not connected to parity nodes, so you will need to find access to a remote endpoint (one example is https://rivet.cloud/) or run a node on your local machine. An example of a remote node could be https://kovan.sampleparitynode.com:8545
Uniswap Exchange Addresses
You'll need to find the address to the Uniswap exchange corresponding to the entry-token as well as arb-token. To do that, call the getExchange(TokenAddress)
at the Uniswap Factory contract to query the exchange addresses. You can do that here. For example, you can use the Kovan WETH token address and call getExchange(0xd0A1E359811322d97991E03f863a0C30C2cF029C)
Shell Script
As was mentioned before, let's make a shell script so that it's easy to run this Bot. For example, we're using Kovan Sai for the entry-token since we won't be exposed to the arb token's price exposure in between trades. Moreover, for kovan testing, we will be using the WETH/DAI pair, since liquidity is thin across the Kovan versions of MatchingMarket and Uniswap exchanges. Any NoneType
errors are likely due to a lack of liquidity on either exchange and can be resolved once liquidity is added. As mentioned earlier, the min-profit and max-engagement arguments define how the keeper will operate. Maximum engagement refers to the amount of entry-tokens you would like to sell within the first leg of the trade, whereas minimum profit refers to the amount of profit gained from executing the arbitrage; both arguments are denominated in entry-tokens and in units of ether (in the below script, 1 SAI of min-profit would be 1 SAI). For example, the below script will trade with a max-engagement of 10 SAI, but only if the returned value from the trade results in at least 11 SAI.
Create a file called like below, insert the arguments relevant to your environment, make it executable, then run it!
In your run-simple-keeper-kovan.sh file:
Make your Keeper executable, and run it!
Initial Startup
During the initial startup, several approval transactions will be sent to the tx-manager, and relevant exchanges. When complete, you'll see a “Watching for new blocks” message that indicates that the bot is now looping through the lifecycle.process_block()
method; following that message and with every new block is a "Best Trade regardless of profit" message that acts as a "heartbeat" to the Keeper.
Nominal Operation
After the initial approval phase the Keeper's startup will look something like the below output and will continue to print "Best Trade ... " messages with every new block witnessed. When the keeper publishes a transaction, all relevant info will be logged and printed to the console. Finally, when you’d like to shut down the keeper, simply click CTRL-C, and it will gracefully shutdown.
Troubleshooting
During keeper operation, if the following error is shown, just CTRL-C and rerun:
Next steps
Improvements
Here are some suggestions to improve the usability, versatility, and profitability of this keeper. We hope that everyone tinkers with the parameters, runs the keeper for their own benefit, and eventually upgrades the bot to meet their risk tolerance and profit appetite.
Develop unit tests for each method
Use the Uniswap Factory contract to query the uniswap-entry-exchange and uniswap-arb-exchange addresses rather than requiring it as an argument
Rather than the default gas price implement a dynamic gas price strategy
Increase scope
Monitor more than one pair
Monitor more than two decentralized exchanges
Implement support for centralized exchanges
Increase number of intermediary arb tokens
(e.x. DAI → WETH → BAT → DAI)
Improve efficiency of TxManager
Do we really need to send all of our token balance to the contract during every atomic transaction?
Read the state of the MatchingMarket (OasisDex) contract rather than using the Oasis REST API
To save gas, get all active orders and take specific orders by ID rather than use
MatchingMarket.offer(...)
and the on-chain matching engine
Send trade updates through a text/email Python API
Resources
Last updated