Skip links
arbitrage cryptocurrency

What I Have Learned From My Arbitrage Experiences with Cryptoassets

[latexpage]

Lately, lots of posts have been talking about how traders get rich from arbitrage opportunities in the crypto market. I have been trying to do arbitrage for months, but I could never obtain a profitable strategy. Therefore, I will tell you here the problems I found while attempting to do it. This might be helpful for those who are starting with it, so they don’t have to travel through the same painful path. Summarizing, here is what I found:

  • Most of the arbitrage opportunities disappear after considering exchange fees and transaction commissions.
  • Most of the arbitrage opportunities appear between exchanges where wallets are on maintenance, which makes deposits and withdrawals impossible.
  • Transaction times between exchanges cause arbitrage to become a lottery due to market volatility.

Keep reading to find out how to build an arbitrage monitor from scratch with python and related useful information.

What is an arbitrage?

Arbitrage (by definition) is the practice of taking advantage of a price difference between two or more markets. For example, if you find that stocks from some company are traded at a lower price in one market than in others, then you can buy those stocks at that market and sell them in the other markets at a higher price, thus making a profit from the price difference. Simple. Unfortunately, too simple.

This simplicity gives many traders the opportunity to take advantage of the situation; this increases the demand of the stocks in markets where prices are lower and, at the same time, increases the offer in markets with higher prices. Hence, they unintentionally stabilize price inequality, which decreases opportunities. Besides, the more traders a market has, the more efficiently they work to stabilize the prices. In fact, finance models assume an arbitrage-free condition, which implies that, in efficient markets, there are no arbitrage opportunities right there waiting for you.

But, let’s be honest, crypto assets markets are relatively green. New exchanges with low trading volumes appear constantly. This market is far from being efficient.

How to find an arbitrage opportunity in the crypto market?

There are many tools that can help you find an arbitrage opportunity. Let’s start with the simplest: websites that monitor these opportunities (for example, this). The problem with these websites is that you can’t control which exchanges and which assets you monitor. And, in practice, this is definitely something you need to do, because in order to take an advantage of an opportunity, you have to have your investment assets in the right moment, in the right exchange.

So, let’s build our own monitor from scratch.It will look for the opportunities that we really care for. To do this, we will use the ccxt Python library, which allows us to connect to several exchanges (currently 115) and trade cryptos in an standarized way (ccxt developers, you have really done a great job). We are going to use this library to request ask/bid prices for different assets in different exchanges, compare them and think about an arbitrage strategy.

Now let’s get started. First of all, import the libraries:

import numpy as np
import ccxt

We are going to use numpy later on to make computations. Now, let’s define some exchanges:

exchanges = ["Allcoin", "Binance", "Bitfinex", "Bittrex", "Cex", "Cryptopia", "Exmo", "Gatecoin", "Hitbtc",
"Huobipro", "Kraken", "Kucoin", "Livecoin", "Okex", "Poloniex", "Qryptos", "Quadrigacx", "Southxchange", "Yobit"]

With exchanges  defined, we can now initialize the corresponding clients to request data from them. You can define them like this:

allcoin = ccxt.allcoin()

Or better, you can do this and avoid writing a lot:

clients = [getattr(ccxt, e.lower())() for e in exchanges]

So clients  is a list containing all the clients we need. Now, let’s define some pairs of interest:

symbols = ["ADA/BTC", "BCH/BTC", "BTG/BTC", "BTS/BTC", "CLAIM/BTC", "DASH/BTC", "DOGE/BTC", "EDO/BTC", "EOS/BTC",
           "ETC/BTC","ETH/BTC", "FCT/BTC", "ICX/BTC", "IOTA/BTC", "LSK/BTC", "LTC/BTC", "MAID/BTC", "NEO/BTC",
           "OMG/BTC", "QTUM/BTC", "STR/BTC", "TRX/BTC","VEN/BTC", "XEM/BTC", "XLM/BTC", "XMR/BTC", "XRP/BTC", "ZEC/BTC"]

You can modify the previous variables with your preferred choices. Then we define some useful variables:

ask = np.zeros((len(symbols), len(clients)))
bid = np.zeros((len(symbols), len(clients)))

Finally, request the data from the clients. We are going to use the fetch_order_book  function in each client, which returns the full order book, but we are only going to care about the bid and ask values at the top (this is a first approach, we could explore better options computing the mean value of our trading investment).

Running the arbitrage monitor

Assuming you have not modified the exchanges  list, the following lines could take a while. To speed them up, go back and define less clients.

for row, symbol in enumerate(symbols):
    for col, client in enumerate(clients):

        try:
            book = client.fetch_order_book(symbol)
            ask[row, col] = book['asks'][0][0]
            bid[row, col] = book['bids'][0][0]
        except:
            pass

I’ve defined the request inside a try  block because some symbols are not traded in all exchanges, and bad requests raise errors, which are bothersome. Another source of error could be rate request limits, which we are just passing now. We can also implement a delay to decrease the request rate for each exchange. That delay should be adjusted depending on which exchanges we are connecting to, because each one has its own limit. To dig deeper into it you can check out each exchange’s documentation later. I’ve also defined the ask and bid  ndarrays inside the try  block because some requests could return empty arrays, and for now we are trying to avoid dealing with this.

In a more efficient approach, we don’t want to wait until the whole loop finishes to look for an arbitrage opportunity, but for now, we’ll leave it as it is and continue exploring the data to further understand what our best options could be.

Having defined the bid and ask values for each coin in each exchange, we can now define our strategy and compute the profits and losses. Our strategy to trade coin C between exchanges E1 and E2 implies buying C at E1, transferring C coins to E2 and selling them there. We want to perform all these steps as quickly as we can because we don’t want somebody taking advantage of us, so we are going to execute each trade immediately, where immediately means buying at the lowest price somebody is willing to sell, i.e. lowest ask value, and selling at the highest price somebody is willing to buy, i.e. highest bid value. So, let’s define the steps:

  • At E1, set a buy market order for C.
  • Transfer the C coins to E2.
  • At E2, set a sell market order for C.
  • Compute the BTC Return On Investment (ROI) profit as:

$ROI =\left\{ \frac {bid_{E2} }{ask_{E1}} – 1 \right\} \cdot 100 \%$

In practice, you should also pay for fees in exchanges. So ROI should be better defined as:

$ROI =\left\{ \frac {bid_{E2} \cdot (1-fee)}{ask_{E1} \cdot (1+fee)} – 1 \right\} \cdot 100 \%$

Then, let’s define a fee  variable to compute ROI this way. We can define an array or a dictionary of fees for each exchange, but right now, for the sake of simplicity, we are just going to define a general (conservative) percentage fee:

fee = 0.25

Finally, we should also discount the transaction commission. But for now we are going to proceed without taking this into account, because commissions depend on coins and network loads, and we’re not going to bother with that right now.

Let’s compute this, and count how many profitable opportunities we’ve found:

opportunities = []

for i, symbol in enumerate(symbols):
    for j1, exchange1 in enumerate(exchanges):
        for j2, exchange2 in enumerate(exchanges):

            roi = 0
            if j1 != j2 and ask[i, j1]>0:
                roi = ((bid[i, j2]*(1-fee/100)) / (ask[i, j1]*(1+fee/100)) - 1) * 100

                if roi>0:
                    opportunities.append([symbol, exchange1, ask[i, j1], exchange2, bid[i, j2], round(roi,2)])

print("Number of profitable opportunities:", len(opportunities))

We have found 108 opportunities! Promising, isn’t it? Let’s sort them by origin and take a look at the best ones:

opportunities = sorted(opportunities, reverse=True, key=lambda x: x[5])
print(opportunities[:10])

At the time of writing this article the best option is to buy QTUM/BTC at Bitfinex and sell it at Qryptos. For each QTUM/BTC traded, we could get a ROI of 75.45%.

The naive approach…

Now that we’ve found some opportunities, we can start arbitraging. The first opportunities we’ve found all have to do with buying some QTUM at certain exchanges and sellng them at Qryptos. The return of that arbitrage is something about 75%. 75%!! We’re going to be trading all day. We’re going to became rich. So easy.

Just for curiosity’s sake, let’s search “qryptos+qtum+deposit” on Google to see if everything is going to be this easy when we try to deposit our QTUM coins there. I found:

It seems like not everything is going so well. Let’s look at the third link: FAQ Qryptos explaining why out wallet is not going to be compatible with the QTUM tokens that we bought there.

So let’s not waste our time here, we have 101 more possibilities. Let’s take the next one. We can buy some BTG coins at Yobit and sell them on Cex. Wait a moment… the two opportunities after this one also imply selling BTG coins bought at Yobit. This is suspicious.

I open my Yobit account and I try to withdraw some BTG to see if everything works fine. When I click on the withdrawl button, I find this notification:

Wallet Status: Maintenance. OK, we are not going anywhere around here. Let’s look for another choices.

Another interesting opportunity on the list: buy some BCH coins on Binance and sell them on Yobit. This time with less profit, something like a ROI of 4%. So this one could be real. First of all, we need some available BTC in Binance in order to buy there. If we fortunately have them, we are starting with the right foot. Otherwise, start praying for our BTC to arrive before the opportunity disappears.

Fortunately, I have some BTC in Binance, so I buy 1 BCH there and then I look for my BCH address on my Yobit account in order to send my new coins there. When I click on the deposit button, I find this notification:

Guess what? Wallet is on maintenance. We can see this when we try to withdraw some coins, which gives us this notification:

So, I wait for 5 minutes, and I try again:

And nothing happens. Nothing in 5 minutes, 10 minutes or 30 minutes. Even if I had had a previous BCH address, deposits would have a significant delay (trust me, this is not my first time doing this). So, I have no other choice than to keep my BCH. Anyway, buy and hold has always been a good trading strategy in crypto markets, at least until the last quarter of 2017.

So, let’s try with another option in our list with a similar ROI. The last one, BTG from Huobi to Cex. What happens when I try to withdraw BTG from Huobi? Another wallet on maintenance. This time there are not warnings, but deposit and withdraw are just disabled.

…and the reality show

I’ve been trying to find a profitable arbitrage approach for months. And I’m not going to lie, I’ve never found it. There were some opportunities, but I could never find a consistent way to spot them with just a bot monitor, and without checking each wallet on each exchange on my own. Aside from the fact that a lot of opportunities are discarded right at the beginning due to not being profitable (on account of exchange fees and transference commissions), we have to deal with wallet maintenance.

And you are lucky if your exchange even warns you about that maintenance. Some exchanges don’t do it at all. For example, I once tried an arbitrage between Exmo and some other exchange. I waited for my BTC deposit on Exmo for 2 hours until I wrote to them. 3 hours later, they answered me:

Finally, my BTC took 2 more days to be deposited. So you never know if everything is going to be as planned when your transfer crypto assets.

And even when you find an arbitrage opportunity between exchanges where everything works fine, you have to deal with transfer time. Most exchanges don’t allow you to trade cryptos until they’ve been through a good number of confirmations. This almost always takes several dozen minutes. And if you expect prices to remain stable after that time, you have probably never traded cryptos before. When the arbitrage profit is about 2% (yes, don’t expect to find a better opportunity without a wallet under maintenance), and, after exchange fees and transaction commissions, goes down to about 1.5%, or even lower, expecting prices not to shift more than that amount in as little as 20 minutes is absurd. It’s even crazier if you also take into account that there will be traders who are aware of that difference and will implement algorithms that take faster advantage of the situation, thus leaving you empty handed.

Some final recommendations

When you are analyzing arbitrage opportunities, always be aware of:

  • Correct profit calculations considering:
    • maker/taker fees at the purchase exchange
    • transaction commissions
    • maker/taker fees at the sale exchange.
  • Maintenance of wallets.
  • Time of transactions and estimated changing value in prices during this time.
  • Some exchanges don’t allow you to withdraw your funds unless you have done the full verification.
  • Do some research on google about that arbitrage, to know if there is something you are missing.
  • Always suspect if you find an arbitrage opportunity involving fiat to crypto pairs, like BTC/USD or ETH/USD. For example, BTC/USD today is being traded at \$7549 at Bitfinex and at \$7721 at Bithumb. That sounds like another good opportunity, doesn’t it? Well, I have bad news for you: once you transfer your BTC to Bithumb, the only currency you can exchange them with over there is KWC (at an equivalent price of \$7721 USD). Only if you have a validated account and if you have a bank account in South Korea are you going to be able to withdraw your fiat money. And then, you should also think about a strategy to transfer your money from South Korea to your country.

Always, always suspect before giving away your money.
And, before I leave, I’m going to try once again to deposit my BCH coins on Yobit:

Mmm no, not my day.

In our next article, we are going to analyze a better way to approach arbitrage opportunities. I mean, this time for real.

If you liked this article, you might also like:

Disclaimer: CoinFabrik does not provide financial advice. This material has been prepared for educational purposes only, and is not intended to provide, and should not be relied on for, financial advice. Consult your own financial advisors before engaging in any investment.