Skip to content

Arbitrage of cryptocurrencies: What I have learned from my experiences

Lately, many posts have been talking about how traders get rich because of the arbitrage opportunities in the crypto market. I have tried arbitration for months, but I could never get a profitable strategy. However, I will tell you about the problems I encountered while trying. This can be beneficial for those who are starting so that they do not have to travel the same painful path. In short, this is what I could get specifically:

Most arbitrage opportunities fade after considering exchange rates and transaction fees.

Most arbitrage opportunities appear between exchanges where the wallets are in maintenance, which makes deposits and withdrawals impossible.

The transaction times between the exchanges make the arbitrage become a lottery debit to market volatility.

Keep reading to discover how to build an arbitration monitor with python and obtain valuable information accordingly.

What is arbitration?

Arbitration (by definition), is the practice that takes advantage of a price difference between two or more markets. For example, if you discover that the shares of a company are being traded at less price in one market than in others, you could buy them in that market and sell them in another for a higher price, thus achieving profitability by the price difference. Simple. Unfortunately, very simple.

This simplicity gives traders the opportunity to take advantage of the situation; This increases the demand for shares in a market where prices are low and, at the same time, increases the supply in markets where prices are higher. Therefore, they unintentionally stabilize price inequality. In addition, the more traders the market has, the more efficiently they work to stabilize prices. In fact, the financial model assumes the arbitrage-free condition. Which implies that, inefficient markets, there should be no opportunities to arbitrate.

But let’s be honest, the active crypto markets are relatively immature. New exchanges are constantly appearing with low trading volumes. This market is far from efficient.

How to find arbitrage opportunities in crypto markets?

There are many tools that can help you find arbitration opportunities. Let’s start with the simplest: sites that monitor these opportunities (for example, this one). For HITBTC you can try this one too. The problem with these sites is that you can not control which exchange and which assets are monitored. And, in practice, this is something that we definitely need to do, because to take advantage of an opportunity we need to have the indicated assets, at the right time, in the indicated exchange.

So, let’s build our own monitor from scratch. It will look for opportunities that really interest us. To do so, we will use the Python ccxt library that allows us to connect to various exchanges (115 to be precise) and trade crypto in a standardized way (to ccxt developers: they did an excellent job!). We are going to use these bookstores to consult the purchase / sale prices of different assets in different exchages, compare them and then think about the arbitrage strategy.

Let’s start First of all, import the bookstores:

import numpy as np import ccxt 1 2 import numpy as np import ccxt

We will use numpy later to perform the calculations. 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”] 1 2 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 ask for the data. You can define it like this:

allcoin = ccxt.allcoin () 1 allcoin = ccxt. allcoin ()

Or better yet, they can do it to avoid writing so much:

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

Then clients is a list that contains all the clients we need. Now, let’s define some interest pairs:

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 “] 1 2 3 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 “]

They can modify the previous variables according to their preferences. Then we define other useful variables:

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

Finally, we ask for the customer’s data. We will use the fetch_order_book function in each client, which will return the complete order book, but we will only focus on the latest purchase / sale prices (this is a first proposal, we can explore better options by calculating the average value of our trade ).

Executing the arbitration monitor

Assuming they have not modified the exchanges listing, the next lines may take a while. To accelerate it, let’s define the client list again.

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 1 2 3 4 5 6 7 8 9 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 have defined the order within a try block because some symbols are not traded in all exchanges, and incorrect orders carry annoying errors. Another source of errors could be the rate request limits that, for the moment, we are leaving it aside. We can also implement a delay to reduce the frequency of orders for each exchange. Esee delay should be adjusted depending on which exchanges we are connecting to, because each one has its own limitations. To deepen this, you can check the documentation of each exchange. I have also defined the ask and bid ndarrays in the try block since some orders can return empty arrays, and for now we are trying to avoid that.

In a more efficient approach, we do not want to wait until the entire loop finishes looking for an arbitrage opportunity. But for now, let’s leave it that way and we will continue to explore the data to evaluate what our best options may be.

Having defined the purchase and sale prices with the ask and bid values for each currency in each exchange, we can now define our strategy and calculate our gains and losses. Our strategy to trade the C currency between exchanges E1 and E2 involves buying C in E1, transferring currency C to E2 and selling it there. We want to carry out all these steps as quickly as possible because we do not want someone to go ahead of us. So we are going to execute each trade immediately, where it immediately means buying at the lowest price that someone is willing to sell. So let’s define the steps:

In E1, I place the purchase order for C.

I transfer the currency C to E2

In E2, I put a sales order for C.

I calculate the return on investment (ROI) in BTC:

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

In practice, they should also pay fees at the exchanges. So the ROI would be better defined like this:

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

Then, we will define the variable fee to calculate the ROI. We can define an array or a dictionary of fees for each exchange, but for now, in order to simplify, we are only going to define a general percentage (conservative) of fee.

fee = 0.25 1 fee = 0.25

Finally, we should also discount the commission per transaction. But for now we are going to proceed without taking this into account, because the commissions depend on each currency and the load of the network at that moment, and now we are going to worry about that.

Let’s calculate it, and count how many profit opportunities we have 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)) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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, right? We are going to order them by their origin and observe which are the best:

opportunities = sorted (opportunities, reverse = True, key = lambda x: x [5]) print (opportunities [: 10]) 1 2 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 in Bitfinex and sell them in Qryptos. For each QTUM / BTCtradeado, we can obtain an ROI of 75.45%

The naive approach …

Now that we have found some opportunities, we can begin arbitration. The first opportunities that we have found involve all the QTUM in certain exchanges and selling them all in Qryptos. The return of that arbitration is around 75%. 75% !!! We are going to trade all day. We are going to become rich. So easy…

Just out of curiosity, let’s look for “qryptos + qtum + deposit” in Google to see if everything is going to be so simple when we try to deposit our QTUM there. I found:

Apparently not everything is going to turn out so well. Let’s look at the third link: FAQ of Qryptos explaining that our wallet will not be compatible with the QTUM tokens that we bought there.

Let’s not waste time, we have 101 more possibilities. Take the next one. We can buy some BTG in yobit and sell them in CEX. But … a moment … the two opportunities after this involve selling BTC bought in yobit. Suspect.

I open my Yobit account and try to make a BTG retreat to see if everything works well. When I click on the withdrawl button, it gives me the following notification:

Wallet Status: Maintenance (wallet / holding wallet). OK, we are not moving forward. Let’s keep looking at the other options

Another interesting opportunity on the list: buy some BCH in Binance and sell them later in Yobit. This time with less profit, something around 4% ROI. This could be real. First of all, we need to have BTC available in Binance to be able to buy here. If we fortunately have them available, we start with the right foot. Otherwise, we begin to pray for our BTCs to arrive before the opportunity disappears.

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

Guess what? The wallet is in maintenance. We can see this when we try to make the withdrawal of some coins, which gives us the following notification.

So I wait for about 5 minutes and try again …

And nothing happens. Nothing in 5 minutes, 10 or 30. Even if we had previously had the address of BCH, the deposits would have had a significant delay (believe me, this is not the first time I do). Then, I have no choice but to keep my BCH. Anyway, buy and retain if it has been a good trading strategy in the crypto market, at least until the end of 2017

Let’s try another option on our list, with an ROI similar to the previous one. The last, BTG from Huobi to Cex. What happens when I try to remove the BTG from Huobi? Another wallet in maintenance. This time without warnings, but deposits and withdrawals are disabled.

…. and the reality show

I was trying to find an arbitration with returns for months. I’m not going to lie to you, I never found it. There were some opportunities, but I could not find a consistent way to detect them only with the monitor, and without checking each wallet of each exchange on my own. Regardless of the fact that many of the opportunities are discarded from the beginning for not giving returns (taking into account the fees and commissions of the exchanges), and having to deal with wallets in maintenance.

And consider yourself lucky if your exchanges notify you about maintenance. Some not even that. For example, on one occasion I tried to arbitrate between Exmo and another exchange. I expected the deposit of my BTC in Exmo for 2 hours until I wrote them. After 3 hours, they replied:

Finally, my BTCs took another 2 days to be deposited. You never know if everything will go according to plan when it comes to transferring active crypto.

Even when you find an arbitrage opportunity between exchanges where everything works well, you have to deal with transfer times. Most exchanges do not allow to trade crypto until a good number of confirmations have been received. This usually takes dozens of minutes. And if you expect prices to remain stable during that time, you probably have never traded crypto. When the profitability of the arbitration is close to 2% (yes !, do not expect to find a better opportunity without wallets in maintenance), and, if after the fee and commissions, it decreases close to 1.5%, or even less, expecting that the prices do not change more than that amount in a span as short as 20 minutes is absurd. It is even more preposterous if you also take into account that there will be traders who are aware of that same difference and implement algorithms to take advantage quickly, leaving them this way with empty hands.

Some final recommendations

When you are analyzing arbitrage opportunities, always be attentive to:

A correct calculation of income, considering: purchase fees in the exchange commission of the transaction, sales fees in the exchange

Maintenance of the wallets

Delay of the transaction and the estimated price changes in that time.

Some exchanges do not allow funds to be withdrawn unless full verification has been completed.

Investigate in google about the arbitration that they are prepared to do, to know if there is something they are not taking into account.

Always suspect if you find an arbitrage opportunity involving money fiat to crypto pairs, such as BTC / USD or ETH / USD. For example, BTC / USD today is being traded at $ 7549 in Bitfinex and $ 7721 in Bithumb. That sounds like another good opportunity, is not it? Well, I have bad news for you: once you transfer your BTC to Bithumb, the only currency for which you can exchange it there is KWC (for an equivalent of \ $ 7721 USD). And then, they also have to think of a strategy to transfer their money from South Korea to their country.

Always, always suspect before handing over your money.

Photo by M. B. M. on Unsplash


Also published on Medium.

Published inCryptocurrencies
%d bloggers like this: