Arbitrage Trading

Go up to the CCC HW page (md) | view tabbed version

Overview

In this assignment you are going to create a Python program to perform arbitrage trading on the blockchain. Your trading will be between a number of different DEX instances from the DEX (md) assignment. This program must be written in Python in a file called arbitrage_trading.py. We provide a number of DEXes for you to use on the course blockchain.

Beyond general experience with programming Solidity (which you have at this point it the course), this assignment requires:

In addition to your source code, you will submit an edited version of arbitrage.py (src).

Changelog

Any changes to this page will be put here for easy reference. Typo fixes and minor clarifications are not listed here.

Web3.py

You will need to read the introduction to web3.py (md). While you do not need to have all of that memorized, you do need to understand it all! The intent is that you will use that page as a reference while you write this assignment.

Connecting

The following code will handle the connection based on the values in the arbitrage_config.py file, which is introduced below. After reading this part, you may want to come back to it again once the arbitrage_config.py file has been introduced.

from web3 import Web3
from web3.middleware import geth_poa_middleware
import arbitrage_config
if arbitrage_config.config['connection_is_ipc']:
    w3 = Web3(Web3.IPCProvider(arbitrage_config.config['connection_uri']))
else:
    w3 = Web3(Web3.WebsocketProvider(arbitrage_config.config['connection_uri']))
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
print(w3.is_connected())

The code above will connect either through a local geth.ipc endpoint or a server’s wss:// connection based on the values in the arbitrage_config.py file:

The value for the second one is provided on the Canvas landing page, and in the provided arbitrage_config.py file.

Market Theory

When to make an trade

Your program will need to compute it’s holdings, which is the dollar amount of all the ETH and TC that it has. You will use some fixed price for ETH (say, $100) and for TC (say, $10) for initial testing – this value is provided in the arbitrage_config.py file, which is described below.

You will first need to obtain the various information (prices, x/y/k values at each DEX, etc.). Then you will need to make a profitable trade. A profitable trade is defined here as a trade where the overall value of holdings, in USD, increases. You must account for gas fees when determining this! The formula to determine if you will make a profit is whether:

ethAmountAfter * ethPrice + tcAmountAfter * tcPrice − gasFees > ethAmountBefore * ethPrice + tcAmountBefore * tcPrice

The gasFees is just the amount Ethereum charges for a transaction; DEX fees are not part of that value (the DEX pays out less, to the two amount-after variables will be less as a result, and thus will take DEX fees into account).

Note: there are other reasonable ways to determine “profit”. In particular, if one believes that the price of the currency will grow, then the total amount of that currency (not the total USD value) would be another metric. For our purposes, we will just use the USD value of the holdings.

This formula also accounts for the DEX fees (the amount after is lower as a result of those).

Although the price of our (fake) ether will vary, you don’t know if it will go up or down – thus, the value for ethPrice should be the current price, and thus will be the same on each side of the equation above.

We are going to call this a single trade. This is when you make one transaction at a single DEX to increase your holdings.

Arbitrage goal

For each DEX, and for each of the two directions (ETH -> TC and TC -> ETH), find the (DEX,currency,amount) combination that maximizes your profit.

Consider the most profitable such transaction among all the available DEXes. If that transaction increases your holdings in USD, then take that action. It’s also possible that a double trade would yield a profit, where as a single trade would not (for example, exchanging some ETH for some TC in one DEX, and then trading that TC back for more ETH at a different DEX). We are not considering double trades for this assignment.

How much to trade

We can formulaically determine how much to trade. The full derivation of the formulas in this section is being omitted here, but you can see that full derivation here (md). First we need to define a number of variables:

The above values are all fixed for each time the program runs – either from the config file (described below) or by querying the DEXes. Different DEXes will have different values for xd, yd, and kd, of course. The only values that the program chooses are the amount of ETH that we trade in (we’ll call this δe) or the amount of TC that we trade in (we’ll call this δt). As we are only considering a single trade, only one of them will be non-zero.

The formulas that we need are (derivations here (md)):

For a single trade, want to find the maximum profit for the two hafter formulas for each DEX. We take the derivative, then set it equal to zero to find the roots (details here (md), if you are interested). The roots will give us the maximum and/or minimum points. This gives us:

A few notes on those:

Assignment

Your assignment is to create a program that attempts to make a profit by arbitrage trading. For the purposes of this assignment, a profit means an increase in the value of your holdings in USD; the holdings are computed from the amount of ETH and TC your program controls as well as the current price of each.

You must take gas estimation into account! Otherwise, if you were only to make 0.001 ETH, but it costs 0.002 ETH in gas, you are losing money. How to estimate gas fees is discussed on the introduction to web3.py (md) page – once you create a transaction, you call eth.estimate_gas(). The expected gas values will be in the tens of thousands of gwei – 36,000 to 65,000 is a reasonable guess, but yours may be higher or lower.

When providing a transaction, you also have to supply the gasPrice. For this assignment, you should use the value is in the arbitrage_config.py file (described below), which will typically be set to 10 gwei. This gas estimate times the gas cost (10 gwei) will allow you to compute the cost of gas in ether, which is the g variable in the formulas above.

Your program must be in Python. It must be named arbitrage_trading.py.

In practice, your program would listen for events from any of the DEXes, and any time the exchange rate of any of the DEXes changed, it would re-run the analysis. In order to make this assignment gradable, we are going to ignore events, and your program will consider all trade possibilities each time it is run.

Values to query the DEXes for

Each DEX will have to be queried to get values for x, y, and k; from this, you can determine the exchange rate (y/x or x/y). You will also have to query the DEXes for the fee amount (feeNumerator() and feeDenominator()). Note that different DEXes, even for the same coin, can have different exchange rates and different fee amounts.

Values provided

The program will import an arbitrage_config.py file to provide many of the values, a sample of which is shown below. The correct file for this semester is available on the Canvas landing page.

config = {
    # you will have to change these values for it to work
    'account_address': '0x123456789ABcdEf0123456789aBCDeF123456789',
    'account_private_key': hexbytes.HexBytes('0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
    'connection_uri': '/path/to/geth.ipc',
    'connection_is_ipc': True, # set to false if it's a ws:// connection
    # once set, these should only be changed as necessary as you are testing your code
    'price_eth': 100.00,
    'price_tc': 10.0,
    'max_eth_to_trade': 10.0,
    'max_tc_to_trade': 100.0,
    'gas_price': 10, # in gwei
    # once set, you should not have to change these
    'dex_addrs': [
                  '0x123456789abcdef0123456789abcdef123456789', 
                  '0x123456789abcdef0123456789abcdef123456789', 
                  '0x123456789abcdef0123456789abcdef123456789', 
                  '0x123456789abcdef0123456789abcdef123456789',
                  '0x123456789abcdef0123456789abcdef123456789',
                  '0x123456789abcdef0123456789abcdef123456789'
                  ],
    'tokencc_addr': '0x123456789abcdef0123456789abcdef123456789',
    'chainId': 12345678,
}

The output() function, below, will also be in the arbitrage_config.py (src) file, a getTXNResult() function for you to use, and the relevant ABIs.

You may assume that the arbitrage_config.py will always be present and properly structured, and that all values will be valid. The parts of the config dictionary are:

You will need to edit some those values in arbitrage_config.py for your particular situation.

We provide a few other things in arbitrage_config.py: the ABI for IDEX and ITokenCC for you to use. We also provide a function called getTXNResult() that will attempt to figure out what happened on a transaction function call (the return value or revert reason). You can see these in the arbitrage_config.py (src) file.

Output

Your program will analyze the various values at the different DEXes, and make a change (or not). Your output must be in the exact format shown below. If no profitable trades are possible, then you should output No profitable arbitrage trades available. If an trade is made, then the output should be of the exact form:

Exchanged -123.0000 ETH for 2.3400 TC; fees: 1.23 USD; prices: ETH 12.30 USD, TC: 1.23 USD; holdings: 34.30 USD

To ensure you output in the correct format, we provide a function that will print the appropriate lines in the required format. This function is also provided in the arbitrage_config.py (src) file.

def output(ethDelta, tccDelta, fees, holdings_after):
    if ethDelta == 0 and tccDelta == 0:
        print("No profitable arbitrage trades available")
        return
    assert ethDelta * tccDelta < 0, f"Exactly one of ethDelta {ethDelta} and tccDelta {tccDelta} should be negative, the other positive"
    if ethDelta < 0:
        print("Exchanged %.4f ETH for %.4f TC; fees: %.2f USD; prices: ETH %.2f USD, TC: %.2f USD; holdings: %.2f USD" %
              (abs(ethDelta), abs(tccDelta), fees, config['price_eth'], config['price_tc'], holdings_after))
    else:
        print("Exchanged %.4f TC for %.4f ETH; fees: %.2f USD; prices: ETH %.2f USD, TC: %.2f USD; holdings: %.2f USD" %
              (abs(tccDelta), abs(ethDelta), fees, config['price_eth'], config['price_tc'], holdings_after))

YOUR FINAL PROGRAM SHOULD PRODUCE NO OTHER OUTPUT other than the result of calling the output() function, above.

If there are no profitable transactions available, then pass in 0 for the first two parameters; the values of the last two parameters do not matter in this case. When a transaction is made, then one of ethDelta or tccDelta should be negative – that’s the one that is being sold. The other should be positive, and is how much of the other you received for that exchange. These values should be the amount of coin being bought or sold as a floating-point number, and without all the decimals (so 1.5 TC rather than 15000000000 TC). The prices for ETH and TC are pulled from config dictionary in arbitrage_config.py, so they do not have to be passed into this function. The fees and holdings_after parameters should be in USD.

The fees value that you are reporting is just the USD value of the Ethereum transaction fees. The DEX fees were deducted from the amount obtained.

Testing

To see if your program makes the right decision, you can hard-code the x, y, and k values in your arbitrage_trading.py program and print out the results to see if it computed the correct values to trade.

The geth-config.toml file that you used in the HW S4: Private Ethereum Blockchain (md) assignment opens up a web socket. Thus, you can connect in two ways:

Which one you use must be read from the arbitrage_config.py file so that we can modify them when we test your submission.

Testing setup

To help you in your testing, we have deployed six DEXes that all trade the same coin, but at different rates. The coin is Dragon Dice Coin (DDC), whose image is shown to the right. The six different DEXes all trade at a fixed exchange rate (but still uses CPAMM!) – which means that multiple trades will not change their x, y, or k values. This is not realistic in a real-world situation, of course, but it is useful for testing. The six different DEXes have the icons of different sided dice, which correspond to their exchange rates:

Just to clarify: all six of these DEXes exchange the same DDC coin, but at different exchange rates. The individual dice images are used for the images of the DEXes; the multi-die image to the right is the image of the coin. The DEXes all follow the IDEX.sol (src) interface, and the DDC coin follows the ITokenCC.sol (src) interface.

IMPORTANT NOTE: These DEXes follow CPAMM, so the amount they give you will depend on the x, y, and k values that they report. Thus, you will not get exactly a 1:4 trade from the d4 DEX, but instead will get what a CPAMM trade will provide.

The addresses of all these DEXes, as well as DDC, are on the Canvas landing page and are provided in an arbitrage_config.py file, which is available in Canvas’ Files.

Obtaining DDC

The ITokenCC.sol (src) interface has a requestFunds() function, which you just had revert in the dApp Tokens (md) assignment. For DDC, that function will pay you 100 DDC on each call. This will allow you to obtain DDC for use in your exchange testing.

Of course, you can also exchange ether for DDC with any of the DEXes to obtain DDC.

Limiters

The problem with fixed exchange rates is that it is easy to deplete the DEX of funds – one could exchange 1 ETH for 20 TC via the D20 DEX, then back for 5 ETH via the D4 DEX, and repeat forever. This would deplete the reserves of the DEXes and also cause the blockchain size to balloon. It would also prevent other students from using the DEXes. For this reason, there are three limiters in effect for DDC and these DEXes:

These values can be changed by the course instructor, although that may take some time (i.e., it’s not instantaneous). However, you should use smaller amounts in your testing – don’t start by trading in a huge amount of ether, or all of your DDC, as this will cause you to hit your limits very quickly.

Keep in mind that the difficult part of this assignment is making the calculations. During testing you can have your code make those calculations, print out the result (and lots of intermediate values), but not make the actual trade on the blockchain. Once you think you have it working, then call the code to actually make the trade.

When you finally submit your solution, it should print out exactly one line, as described above, and make the trade.

Lastly, these DEXes have the ability to be “turned off” so that any attempt at a transaction will revert with an appropriate error message (either “trading not currently enabled” or “this DEX has been destructed”). In the former case, it will be turned on again soon. In the latter case, the DEXes were likely replaced – check the Canvas landing page for new DEX (and DDC) addresses.

Usage

The different DEX addresses are available from the Canvas landing page, in the arbitrage_config.py file in Canvas’ Files. The intent is for you to comment out different DEXes in that file so that you can test it with different pairs. The version of arbitrage_config.py (src) in the github repo does not have the addresses of the six DEXes deployed on the course-wide blockchain, but the version linked to from the Canvas landing page does.

Let’s assume a standard price ratio of 1:10 (ETH:DDC). This means that the arbitrage_config.py file might have lines such as:

'price_eth': 100.00,
'price_tc': 10.0, 

This is as is provided in the arbitrage_config.py file on the Canvas landing page. Furthermore, the provided file has the dex_addrs list formatted as follows:

'dex_addrs': [
              '0x123456789abcdef0123456789abcdef123456789', # d4 dex
              '0x123456789abcdef0123456789abcdef123456789', # d6 dex
              '0x123456789abcdef0123456789abcdef123456789', # d8 dex
              '0x123456789abcdef0123456789abcdef123456789', # d10 dex
              '0x123456789abcdef0123456789abcdef123456789', # d12 dex
              '0x123456789abcdef0123456789abcdef123456789', # d20 dex
             ],

You can easily comment out different DEXes for your testing. The following three examples assume the prices listed above ($10 for DDC and $100 for ETH).

Specific Examples

We assume that the prices in arbitrage_config.py are set to the standard 100 for ether and 10 for TC. There are four specific examples below; these are the visible Gradescope submission tests. These examples were from an account that started with 11 ether, but was not allowed to trade for than 10 ether (as per the arbitrage_config.py file).

Real-world profit?

Could you use this program in the real world with real ETH?

Well, sorta.

The concepts are the same. But you would have to make a few changes:

But the concepts are certainly the same!

Notes and Hints

Execution Runs

Below are some sample executions. Note that these runs print a lot of output so that you can understand what is going on. Your program should only print the one line via the output() function.

For the execution runs, do not pay attention to the DEX addresses, as they will be different than what you are dealing with. Also, ignore the starting amount of ether or TCC – it is either sufficient for that transaction or zero, depending on the execution run.

All DEXes, sufficient ETH, no TCC

If all DEXes are enabled, and the account has sufficient ether but no TCC, the following is an example execution run. In particular, given sufficient funds and the availability of the D20 DEX, it will always choose that to exchange.

Connected: True
Account: 0x15b6d9ACa8b6B09745653F36b193c612a824d16D
TokenCC decimals: 10

ETH balance: 601.1629454504019 with price: $100.0
TCC balance: 0.0 with price: $10.0
Holdings: 60,116.29 USD

DEX 0 @ 0x3270c4e2712a094773f360223268737b4f76d4db, symbol: D4C
    k = 40000.0, x = 100.0, y = 400.0, f = 0.995
    trading in TC minima/maxima:          0.00  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1030.87  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -36.91  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -163.09  minima/maxima is not positive, so ignoring
DEX 1 @ 0x08942ee9050f1bfd446298e0a36c45b61df951a3, symbol: D6C
    k = 60000.0, x = 100.0, y = 600.0, f = 0.995
    trading in TC minima/maxima:          0.00  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1372.66  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -22.73  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -177.27  minima/maxima is not positive, so ignoring
DEX 2 @ 0xb48cff2e10bab7a40eed8e795b47abe4031c2057, symbol: D8C
    k = 80000.0, x = 100.0, y = 800.0, f = 0.995
    trading in TC minima/maxima:          0.00  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1692.19  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -10.78  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -189.22  minima/maxima is not positive, so ignoring
DEX 3 @ 0xd53a424d33d254d08dc88d86b7636423941805d7, symbol: D10C
    k = 100000.0, x = 100.0, y = 1000.0, f = 0.995
    trading in TC minima/maxima:         -2.50  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1997.50  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:        -0.25  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -199.75  minima/maxima is not positive, so ignoring
DEX 4 @ 0x897d3fb345ce0a3c54cbba807e7188a8ea1452bd, symbol: D12C
    k = 120000.0, x = 100.0, y = 1200.0, f = 0.995
    trading in TC minima/maxima:       -107.30  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -2292.70  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:         9.27 ETH; holdings after:     60202.22 USD; difference:        85.93
    trading in ETH minima/maxima:      -209.27  minima/maxima is not positive, so ignoring
DEX 5 @ 0x6b2c410bd8c822e5e8ccebd27bf3af4c7cd8409c, symbol: D20C
    k = 200000.0, x = 100.0, y = 2000.0, f = 0.995
    trading in TC minima/maxima:       -589.33  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -3410.67  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:        41.07 ETH; holdings after:     61802.81 USD; difference:      1686.52
    trading in ETH minima/maxima:      -241.07  minima/maxima is not positive, so ignoring

Best amount of 41.06735979665885 exceeds ETH max of 10.0
Best transaction: DEX # 5, trading in ETH: True, ETH transacted: 10.0, TCC transacted: 61802.81188570843

Trading in 10.0 ETH: 10000000000000000000 wei at DEX 0x6b2c410bd8c822e5e8ccebd27bf3af4c7cd8409c symbol D20C

Gas estimate (gwei): 124424
ETH & TCC before: 601.1629454504019 0.0
waiting for eth->tcc txn to be mined; txn 0x10c481d1a45aa9637ecf7cb943a40cc5219bf8b6c496c50b8c2e843549a4b8a0
The TXN was mined successfully
ETH & TCC after: 591.1617580804019 180.9090909092 

Exchanged 10.0000 ETH for 180.9091 TC; fees: 0.12 USD; prices: ETH 100.00 USD, TC: 10.00 USD; holdings: 60925.27 USD

Final ETH balance: 591.1617580804019
Final TC balance: 180.9090909092
Holdings before: $60,116.29 USD
Holdings after: $60,925.27 USD

Lower DEXes, sufficient ETH & TCC

If only the lower three DEXes are available (D4, D6, and D8), and there is sufficient TCC, then the trade will be TCC -> ETH.

Connected: True
Account: 0x15b6d9ACa8b6B09745653F36b193c612a824d16D
TokenCC decimals: 10

ETH balance: 571.1597253404019 with price: $100.0
TCC balance: 542.7272727276 with price: $10.0
Holdings: 62,543.25 USD

DEX 0 @ 0x3270c4e2712a094773f360223268737b4f76d4db, symbol: D4C
    k = 40000.0, x = 100.0, y = 400.0, f = 0.995
    trading in TC minima/maxima:        230.87 TC;  holdings after:     63875.79 USD; difference:      1332.54
    trading in TC minima/maxima:      -1030.87  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -36.91  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -163.09  minima/maxima is not positive, so ignoring
DEX 1 @ 0x08942ee9050f1bfd446298e0a36c45b61df951a3, symbol: D6C
    k = 60000.0, x = 100.0, y = 600.0, f = 0.995
    trading in TC minima/maxima:        172.66 TC;  holdings after:     63040.08 USD; difference:       496.83
    trading in TC minima/maxima:      -1372.66  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -22.73  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -177.27  minima/maxima is not positive, so ignoring
DEX 2 @ 0xb48cff2e10bab7a40eed8e795b47abe4031c2057, symbol: D8C
    k = 80000.0, x = 100.0, y = 800.0, f = 0.995
    trading in TC minima/maxima:         92.19 TC;  holdings after:     62649.47 USD; difference:       106.22
    trading in TC minima/maxima:      -1692.19  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -10.78  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -189.22  minima/maxima is not positive, so ignoring

Best amount of 230.87241182350022 exceeds TC max of 100.0
Best transaction: DEX # 0, trading in ETH: False, ETH transacted: 100.0, TCC transacted: 63875.78532484618

Trading in 100.0 TC: 1000000000000 tcc_wei at DEX 0x3270c4e2712a094773f360223268737b4f76d4db D4C

Gas estimate (gwei): 115873
ETH & TCC before: 571.1597253404019 542.7272727276
waiting for tcc->eth txn to be mined; txn 0xb0e7fc84a26eeee39b312529302ad792deceafdd014e1896913f7e834deb0004
The TXN was mined successfully
ETH & TCC after: 591.0586033704019 442.7272727276 

Exchanged 100.0000 TC for 19.8989 ETH; fees: 0.12 USD; prices: ETH 100.00 USD, TC: 10.00 USD; holdings: 63533.13 USD

Final ETH balance: 591.0586033704019
Final TC balance: 442.7272727276
Holdings before: $62,543.25 USD
Holdings after: $63,533.13 USD

Only D10 DEX, sufficient ETH & TCC

The D10 DEX exchanges the assets at the same amount as the price ratio (1:10 for the DEX, and the default prices are 10:100 USD), so with gas fees, there cannot be a profitable trade.

Connected: True
Account: 0x15b6d9ACa8b6B09745653F36b193c612a824d16D
TokenCC decimals: 10

ETH balance: 591.0586033704019 with price: $100.0
TCC balance: 442.7272727276 with price: $10.0
Holdings: 63,533.13 USD

DEX 0 @ 0xd53a424d33d254d08dc88d86b7636423941805d7, symbol: D10C
    k = 100000.0, x = 100.0, y = 1000.0, f = 0.995
    trading in TC minima/maxima:         -2.50  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1997.50  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:        -0.25  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -199.75  minima/maxima is not positive, so ignoring
Best transaction: DEX # 0, trading in ETH: 0, ETH transacted: 0, TCC transacted: 0

No profitable arbitrage trades available

Middle DEXes, sufficient ETH & TCC

If the middle DEXes are available (D8, D10, and D12), and there is sufficient ETH and TCC, it will exchange TCC for ETH at the D8 DEX. However, the maxima is not at 100 TCC, but a bit less.

Connected: True
Account: 0x15b6d9ACa8b6B09745653F36b193c612a824d16D
TokenCC decimals: 10

ETH balance: 591.0586033704019 with price: $100.0
TCC balance: 442.7272727276 with price: $10.0
Holdings: 63,533.13 USD

DEX 0 @ 0xb48cff2e10bab7a40eed8e795b47abe4031c2057, symbol: D8C
    k = 80000.0, x = 100.0, y = 800.0, f = 0.995
    trading in TC minima/maxima:         92.19 TC;  holdings after:     63639.35 USD; difference:       106.22
    trading in TC minima/maxima:      -1692.19  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:       -10.78  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -189.22  minima/maxima is not positive, so ignoring
DEX 1 @ 0xd53a424d33d254d08dc88d86b7636423941805d7, symbol: D10C
    k = 100000.0, x = 100.0, y = 1000.0, f = 0.995
    trading in TC minima/maxima:         -2.50  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -1997.50  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:        -0.25  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:      -199.75  minima/maxima is not positive, so ignoring
DEX 2 @ 0x897d3fb345ce0a3c54cbba807e7188a8ea1452bd, symbol: D12C
    k = 120000.0, x = 100.0, y = 1200.0, f = 0.995
    trading in TC minima/maxima:       -107.30  minima/maxima is not positive, so ignoring
    trading in TC minima/maxima:      -2292.70  minima/maxima is not positive, so ignoring
    trading in ETH minima/maxima:         9.27 ETH; holdings after:     63619.06 USD; difference:        85.93
    trading in ETH minima/maxima:      -209.27  minima/maxima is not positive, so ignoring
Best transaction: DEX # 0, trading in ETH: False, ETH transacted: 92.1883209278185, TCC transacted: 63639.35494575981

Trading in 92.1883209278185 TC: 921883209278 tcc_wei at DEX 0xb48cff2e10bab7a40eed8e795b47abe4031c2057 D8C

Gas estimate (gwei): 115885
ETH & TCC before: 591.0586033704019 442.7272727276
waiting for tcc->eth txn to be mined; txn 0x4bc3a3c56be6fdb28786eba8c7fde730f0b284d1e2da950a558f029a73b1652d
The TXN was mined successfully
ETH & TCC after: 601.3386491876182 350.5389517998 

Exchanged 92.1883 TC for 10.2800 ETH; fees: 0.12 USD; prices: ETH 100.00 USD, TC: 10.00 USD; holdings: 63639.25 USD

Final ETH balance: 601.3386491876182
Final TC balance: 350.5389517998
Holdings before: $63,533.13 USD
Holdings after: $63,639.25 USD

Submission

You will need to fill in the various values from this assignment into the arbitrage.py (src) file. That file clearly indicates all the values that need to be filled in. That file, along with your Python source code, are the only files that must be submitted. The ‘sanity_checks’ dictionary is intended to be a checklist to ensure that you perform the various other aspects to ensure this assignment is fully submitted.

The submission will take some time to run! It executes a number of runs of your program, and that can take a few minutes to complete.

There is only one submission for this assignment.

Submission 1: Submit your arbitrage_trading.py source code file, along with your completed arbitrage.py file, to Gradescope. You should not submit the arbitrage_config.py file.

Execution runs

The submission will make three execution runs, all on the same account. The account will start with 10 ether and 0 TCC. For all the execution runs, the following value are set:

Each run assumes the state from the previous run. The trades are:

These should happen fairly quickly, as the Gradescope auto-grader can bypass the 60-second DEX waiting period.