In the first article, we introduced how to use the Lead-Lag phenomenon for arbitrage briefly. In this article, we will mainly introduce the cross-exchange "brick-moving" arbitrage. The basic principle is to take advantage of the price lag between different exchanges (Lead-Lag effect). Because the market liquidity, transaction speed and network delay of each exchange are different, the prices of the same currency on different exchanges are often out of sync. Arbitrageurs can arbitrage quickly by monitoring the lagging price change, thereby obtaining risk-free profits.
First, arbitrageurs need to monitor the price differences between different exchanges in real time, especially the ask price and the bid price. By tracking the ask price of exchange A and the bid price of exchange B, if the ask price of exchange A is lower than the bid price of exchange B, it can be considered that there is an arbitrage opportunity. For example, the ask price of exchange A is 10,000 USDT, and the bid price of exchange B is 10,100 USDT, the price difference is 100 USDT, then there is a potential arbitrage opportunity. Of course, the recent historical price difference range should also be taken into account as a reference for the opening and closing price difference, and the waiting time is also one of the costs.
Once an arbitrage opportunity is found, the arbitrageur should buy the asset on an exchange with a lower ask price (such as Exchange A) and sell it on an exchange with a higher bid price (such as Exchange B). This operation can be automated through the API to ensure fast execution and maximize the price difference. However, when executing a trade, transaction costs (such as fees and slippage) as well as price shocks must be considered. Assume that the fee of Exchange A is 0.1%, while the fee of Exchange B is 0.2%, and there is slippage in the market. For example, when buying 1 Bitcoin on Exchange A, the actual transaction price may increase due to the execution of large orders, assuming the slippage is 0.1%. Then, the actual transaction price will be 0.1% higher than expected, resulting in an increase in the purchase cost.
If slippage and fees are taken into account, the actual buying cost and selling income will be different from expectations.
The last step of arbitrage is to close the position. For example, after a period of time, the buy price of exchange A is 10,100 USDT, and the sell price of exchange B is 10,150 USDT. When the price difference narrows from 100 USDT to 50 USDT, the program will close the position automatically and take profits. Of course, in some cases, the price difference may continue to widen, and you can continue to open positions until the funds are exhausted. The reason why the price difference of the exchange cannot be maintained is that arbitrageurs contribute the vast majority of the power.
Due to the existence of a large number of arbitrageurs and market makers, the price differences between different exchanges will not be very large, otherwise they will be leveled out quickly. This is the biggest problem facing arbitrage trading.
Solutions:
- Wait for price differences to form naturally: The cryptocurrency market is highly volatile, and short-term price differences usually occur. Patiently waiting is the best way to solve this problem.
- Use the maker strategy: buy and sell orders on an exchange order book actively, and cancel orders and adjust them as prices change. If one party completes the transaction, the other party will take the order. This will result in lower transaction fees, a smaller spread, and ensure immediate transaction.
- Monitor more trading pairs: Don't just focus on mainstream currencies, as their arbitrage opportunities have usually been captured by a large number of arbitrageurs, causing price differences to narrow. Unpopular currencies and newly listed currencies may have a larger price difference due to poor liquidity and less competition, and are arbitrage opportunities worth paying attention to.
- Choose small exchanges: Small exchanges usually have poor liquidity and slow price adjustments, which makes it easier for large price differences to occur. At this time, arbitrageurs can choose to place orders and execute them preemptively to make profits.
- Choose high-threshold exchanges: Some exchanges require strict KYC certification, such as the Korean exchange Upbit. These places are difficult for ordinary traders to enter, and there are more arbitrage opportunities. Of course, you need to find ways to overcome the difficulties.
Failure to grab orders is a common problem. When the program finds the price difference and places an order for arbitrage, the actual price difference is not that large, and there is usually a loss. At this time, the fastest response and execution speed are crucial.
Solutions:
- Optimize network and server location: Choose a node close to the exchange server to reduce latency. For example, choosing a small exchange with poor liquidity to operate can reduce the market reaction speed and seize the opportunity.
- Asynchronous processing and WebSocket: Using asynchronous code and WebSocket to connect to market conditions can receive price information in real time and respond quickly to avoid missing opportunities due to information lags.
A single-direction transaction is when one side of the order is completed but the other side fails to complete the transaction, which usually occurs in a rapidly fluctuating market. If only one side of the order is successful, the arbitrageur will face exposure risk.
Solutions:
- Set up a reasonable stop-loss mechanism: When a single-direction transaction occurs, you can set a stop loss. Timely closing of positions is an effective way to reduce risks.
- Use market price to place an order: The market price can guarantee the transaction, but the problem is that the difference in the transaction price is uncontrollable and may result in a loss.
When the price difference exists for a long time, the funds of a certain exchange will be bought up quickly, and arbitrageurs may not be able to continue arbitrage operations. At this time, arbitrageurs need to transfer funds quickly or adjust their positions.
Solutions:
- Coin transfer operation: Use cross-exchange currency transfer to transfer funds and continue arbitrage. In this way, you can avoid the accumulation of funds in a single market and increase the liquidity of funds.
- Waiting for the price difference to turn around: Considering the time cost of withdrawal, waiting for the price difference to return is also an option.
The code is not live trading code and is for demonstration purposes only. It does not take into account issues, such as the number of markets, API access failures, asynchronous order speed-up, etc.
// Symbol is the arbitrage trading pair, such as BTC/USDT
let symbol = "BTC_USDT";
// Set commissions, slippage, profit margins for opening and closing positions
let fee = 0.1 / 100; // 0.1% Fee
let slippage = 0.1 / 100; // 0.1% slippage
let entryThreshold = 0.005; // Opening threshold: Opening a position when the price difference is greater than 0.5%
let exitThreshold = 0.001; // Closing threshold: Closing when the price difference returns to 0.1%
// The specific operations performed in each loop
function OnTick() {
// Get ticker data from various exchanges
let tickers = exchanges.map(exchange => exchange.GetTicker(symbol));
// Calculate arbitrage opportunities (based on profit margins)
// profitAB: Buy from exchange 0, sell from exchange 1
const profitAB = (tickers[1].bid - tickers[0].ask) / tickers[0].ask - fee * 2 - slippage * 2;
// profitBA: Buy from exchange 1, sell from exchange 0
const profitBA = (tickers[0].bid - tickers[1].ask) / tickers[1].ask - fee * 2 - slippage * 2;
// Print log
Log(`Tickers: Exchange0 Buy: ${tickers[0].ask}, Exchange1 Sell: ${tickers[1].bid}, Profit AB: ${profitAB} USDT`);
Log(`Tickers: Exchange1 Buy: ${tickers[1].ask}, Exchange0 Sell: ${tickers[0].bid}, Profit BA: ${profitBA} USDT`);
// Determine whether to perform arbitrage operations based on profits
if (profitAB > entryThreshold) { // Open a position when the profit is greater than the opening threshold
Log(`Arbitrage opportunity: Buy BTC from exchange 0, sell from exchange 1, profit: ${profitAB} USDT`);
executeArbitrage(0, 1, tickers[0].ask, tickers[1].bid, profitAB); // Buy from exchange 0 and sell from exchange 1
} else if (profitBA > entryThreshold) {
Log(`Arbitrage opportunity: Buy BTC from exchange 1, sell from exchange 0, profit: ${profitBA} USDT`);
executeArbitrage(1, 0, tickers[1].ask, tickers[0].bid, profitBA); // Buy from exchange 1 and sell from exchange 0
} else if (profitAB < exitThreshold) { // If the spread reverts, close the position
Log(`Close position: Arbitrage opportunity bought from exchange 0 and sold on exchange 1, profit has returned to the close threshold`);
closeArbitrage(0, 1, tickers[0].ask, tickers[1].bid); // Execute the closing operation
} else if (profitBA < exitThreshold) {
Log(`Close position: Arbitrage opportunity bought from exchange 1 and sold on exchange 0, profit has returned to the closing threshold`);
closeArbitrage(1, 0, tickers[1].ask, tickers[0].bid); // Execute the closing operation
} else {
Log("Not enough profit to take profit or close the position");
}
}
// Executing arbitrage trades
function executeArbitrage(buyExchangeIndex, sellExchangeIndex, buyPrice, sellPrice) {
let buyExchange = exchanges[buyExchangeIndex];
let sellExchange = exchanges[sellExchangeIndex];
// Get account balance (assuming BTC balance)
let accountBuy = buyExchange.GetAccount();
let accountSell = sellExchange.GetAccount();
let amountBTC = Math.min(accountBuy.Balance / buyPrice, accountSell.Amount);
// Assume that the transaction volume is 0.1 BTC per transaction
let amount = Math.min(amountBTC, 0.1);
// Ensure sufficient trading volume
if (amount <= 0) {
Log("Insufficient balance to conduct arbitrage");
return;
}
// Place a buy order on the buying exchange
Log(`Place an order to buy ${amount} BTC @ ${buyPrice} on exchange ${buyExchangeIndex}`);
buyExchange.Buy(symbol, buyPrice * (1 + slippage), amount);
// Place a sell order on the selling exchange
Log(`Place an order to sell ${amount} BTC @ ${sellPrice} on exchange ${sellExchangeIndex}`);
sellExchange.Sell(symbol, sellPrice * (1 - slippage), amount);
}
// Closing position operation
function closeArbitrage(buyExchangeIndex, sellExchangeIndex, buyPrice, sellPrice) {
let buyExchange = exchanges[buyExchangeIndex];
let sellExchange = exchanges[sellExchangeIndex];
// Get account balance (assuming BTC balance)
let accountBuy = buyExchange.GetAccount();
let accountSell = sellExchange.GetAccount();
let amountBTC = Math.min(accountBuy.Balance / buyPrice, accountSell.Amount);
let amount = Math.min(amountBTC, 0.1);
// Place a sell order on the buying exchange
Log(`Close and sell ${amount} BTC @ ${buyPrice} on exchange ${buyExchangeIndex}`);
buyExchange.Sell(symbol, buyPrice * (1 - slippage), amount);
// Place a buy order on the selling exchange
Log(`Close buy ${amount} BTC @ ${sellPrice} on exchange ${sellExchangeIndex}`);
sellExchange.Buy(symbol, sellPrice * (1 + slippage), amount);
}
// Main loop
function main() {
while (true) {
OnTick();
Sleep(1000); // Execute once every second
}
}
Lead-Lag brick-moving arbitrage is a cross-exchange arbitrage strategy based on the market lag reaction. By analyzing the price differences in the market accurately and executing transactions quickly, arbitrageurs can obtain stable profits in the cryptocurrency market. However, the success of the strategy depends not only on the design of the strategy itself, but also on good execution and sensitive grasp of market timing. As market competition intensifies, arbitrageurs need to optimize strategies and techniques continuously, improve speed and responsiveness, in order to maintain the continued effectiveness of arbitrage opportunities.