Use the research environment to analyze the details of triangular hedging and the impact of handling fees on hedgeable price difference

Use the research environment to analyze the details of triangular hedging and the impact of handling fees on hedgeable price difference

·

12 min read

Research version without adjustment of handling fees

Triangular hedging.ipynb

In [1]:

var fmz = require("fmz")                           // Import the talib, TA, and plot libraries automatically after import.
var task = fmz.VCtx({
start: '2019-04-09 17:49:00',
end: '2019-04-09 18:00:00',
period: '1m',
exchanges: [{"eid":"Huobi","currency":"ETH_BTC", "balance":1, "stocks":10},{"eid":"OKEX","currency":"ETH_USDT","balance":10000,"stocks":1}, {"eid":"OKEX","currency":"BTC_USDT","balance":10000,"stocks":1}]
})

Display of initial exchange account information and tickers:

  • Trading pair in Exchange A is: ETH_BTC

  • Trading pair in Exchange B is: ETH_USDT

  • Trading pair in Exchange C is: BTC_USDT

In [2]:

var accA = exchanges[0].GetAccount()
accA

Out[2]:
{ Balance: 1, FrozenBalance: 0, Stocks: 10, FrozenStocks: 0 }

In [3]:

var accB = exchanges[1].GetAccount()
accB

Out[3]:
{ Balance: 10000, FrozenBalance: 0, Stocks: 1, FrozenStocks: 0 }

In [4]:

var accC = exchanges[2].GetAccount()
accC

Out[4]:
{ Balance: 10000, FrozenBalance: 0, Stocks: 1, FrozenStocks: 0 }

In [5]:

var initSumBTC = accA.Balance + accC.Stocks
initSumBTC

Out[5]:
2

In [6]:

var initSumETH = accA.Stocks + accB.Stocks
initSumETH

Out[6]:
11

In [7]:

var initSumUSDT = accB.Balance + accC.Balance
initSumUSDT

Out[7]:
20000

It can be seen that the initial total number of BTC is 2, the total number of ETH is 11, and the total number of USDT is 20000. Now let's get the ticker information of each current trading pair.

In [8]:

var tickerA = exchanges[0].GetTicker()
tickerA

Out[8]:
{ Time: 1554831960000,
High: 0.03396501,
Low: 0.03396499,
Sell: 0.03396501,
Buy: 0.03396499,
Last: 0.033965,
Volume: 4696.555,
OpenInterest: 0 }

In [9]:

var tickerB = exchanges[1].GetTicker()
tickerB

Out[9]:
{ Time: 1554831960000,
High: 175.08000001,
Low: 175.07999999,
Sell: 175.08000001,
Buy: 175.07999999,
Last: 175.08,
Volume: 20730.37,
OpenInterest: 0 }

In [10]:

var tickerC = exchanges[2].GetTicker()
tickerC

Out[10]:
{ Time: 1554831960000,
High: 5161.90000001,
Low: 5161.89999999,
Sell: 5161.90000001,
Buy: 5161.89999999,
Last: 5161.9,
Volume: 2109.9292,
OpenInterest: 0 }

It can be seen that the subject matter of Exchange A, Exchange B and transaction is ETH, that is, exchange A/B can conduct indirect hedging operations, but they cannot hedge directly because the pricing currencies are different. The pricing currency of Exchange A is BTC, while that of Exchange B is USDT.
Assume that the exchange A performs a buy operation of exchanges[0].Buy(price, amount), and the price sold by the counterparty is used as the order price when buying, that is, tickerA.Sell. The exchange B performs a sell operation for hedging, that is, exchanges[1].Sell(price, amount), and the price that the counterparty wants to buy is used as the order price when selling, that is, tickerB.Buy. Because exchange A consumed BTC when buying, and exchange B obtained USDT when selling, exchange A needs to replace USDT with BTC to make up for the BTC consumed by exchange A, that is, exchange C executes exchanges[2].Buy(price, amount), and exchange USDT with BTC. To buy, exchange C needs to see the selling price of the counterparty, that is, tickerC.Sell. The buying price of exchange A must be lower than the selling price of exchange B, then Exchange C can buy back BTC consumed by Exchange A with less USDT. You will earn the difference of USDT amount.
So it is as follows:

In [11]:

var diffB2A = tickerA.Sell - tickerB.Buy / tickerC.Sell    // diffB2A means that Exchange B sells ETH coins and Exchange A buys ETH coins, which is logically equivalent to the transfer of ETH from Exchange B to Exchange A.
diffB2A

Out[11]:
0.000047266535449966285

Obviously, the buying price of Exchange A is higher than that of Exchange B and Exchange C, and hedging is obviously money-losing.
The other hedging direction is the same. Exchange A executes the sell ETH operation, Exchange B executes the buy ETH operation, and Exchange C sells BTC for USDT. Achieving the currency is always balanced, and only USDT increases or decreases (i.e. the difference earned).

In [12]:

var diffA2B = tickerA.Buy - tickerB.Sell / tickerC.Buy
diffA2B

Out[12]:
0.000047246531444007644

Let's perform a tickerA.Buy - tickerB.Sell / tickerC.Buy hedging operation to find out how each of the values changes.

In [13]:

var idA = exchanges[0].Sell(tickerA.Buy, 1)
var nowAccA = exchanges[0].GetAccount()
nowAccA       // We can see that the fee is deducted from the BTC.

Out[13]:
{ Balance: 1.03389706,
FrozenBalance: 0,
Stocks: 9,
FrozenStocks: 0 }

In [14]:

var orderA = exchanges[0].GetOrder(idA)
orderA

Out[14]:
{ Id: 1,
Price: 0.03396499,
Amount: 1,
DealAmount: 1,
AvgPrice: 0.03396499,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'ETH_BTC_Huobi' }

In [15]:

var feeRatioA = 0.002      // The default fee for backtesting is 0.2%, i.e. 0.002.
var feeA = orderA.DealAmount * orderA.AvgPrice * feeRatioA   // Exchange A trading fees, BTC denominated.
feeA

Out[15]:
0.00006792998000000001

In [16]:

var idB = exchanges[1].Buy(tickerB.Sell, 1)
var nowAccB = exchanges[1].GetAccount()
nowAccB

Out[16]:
{ Balance: 9824.56983998,
FrozenBalance: 0,
Stocks: 2,
FrozenStocks: 0 }

In [17]:

var orderB = exchanges[1].GetOrder(idB)
orderB      // We can see that the fee is deducted from the USDT.

Out[17]:
{ Id: 1,
Price: 175.08000001,
Amount: 1,
DealAmount: 1,
AvgPrice: 175.08000001,
Type: 0,
Offset: 0,
Status: 1,
ContractType: 'ETH_USDT_OKEX' }

In [18]:

var feeRatioB = 0.002
var feeB = orderB.DealAmount * orderB.AvgPrice * feeRatioB / tickerC.Last    // B exchange fees, converted to BTC denomination.
feeB

Out[18]:
0.00006783548693698057

In [19]:

var idC = exchanges[2].Sell(tickerC.Buy, nowAccA.Balance - accA.Balance)
var nowAccC = exchanges[2].GetAccount()
nowAccC

Out[19]:
{ Balance: 10174.12327555,
FrozenBalance: 0,
Stocks: 0.9662,
FrozenStocks: 0 }

In [20]:

var orderC = exchanges[2].GetOrder(idC)
orderC     // We can see that the fee is deducted from the USDT.

Out[20]:
{ Id: 1,
Price: 5161.89999999,
Amount: 0.0338,
DealAmount: 0.0338,
AvgPrice: 5161.89999999,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'BTC_USDT_OKEX' }

In [21]:

var feeRatioC = 0.002
var feeC = orderC.DealAmount * orderC.AvgPrice * feeRatioC / tickerC.Last      // Fees for C exchange transactions, BTC denominated.
feeC

Out[21]:
0.00006759999999986903

Calculate the total asset information after hedging:

In [22]:

var nowSumBTC = nowAccA.Balance + nowAccC.Stocks
nowSumBTC

Out[22]:
2.00009706

In [23]:

var nowSumETH = nowAccA.Stocks + nowAccB.Stocks
nowSumETH

Out[23]:
11

In [24]:

var nowSumUSDT = nowAccB.Balance + nowAccC.Balance
nowSumUSDT

Out[24]:
19998.69311553

In [25]:

nowSumBTC - initSumBTC

Out[25]:
0.00009705999999987114

In [26]:

tickerC.Buy * (nowSumBTC - initSumBTC)    // Excess BTC converted to USDT

Out[26]:
0.5010140139983642

In [27]:

nowSumUSDT + tickerC.Buy * (nowSumBTC - initSumBTC) - initSumUSDT    // Profit and loss are calculated based on the movement of the account assets and both are denominated in USDT.

Out[27]:
-0.8058704560025944

In [28]:

(diffA2B - (feeA + feeB + feeC)) * tickerC.Buy     // Profit and loss based on price difference, denominated in USDT.

Out[28]:
-0.8058703331189396

As we can see, the price difference when hedging is diffA2B : 0.000047246531444007644.
The fees for the three hedges, converted to BTC are: feeA + feeB + feeC.

In [29]:

feeA + feeB + feeC

Out[29]:
0.0002033654669368496

It can be seen that for such triangle hedging, the price difference must be greater than feeA+feeB+feeC at least, which means no loss, no gain, and to make a profit must be greater than the price difference.
At present, through account comparison calculation and price difference calculation, it is a loss. The loss is about -0.8058704560025944.
I modified a version, so that the handling fees calculated by the handling fee parameter is exactly equal to the price difference profit of the market tested by this model for comparative observation.

It can be seen that under the default condition of handling fees (2 ‰ by default), when the hedging difference is 0.000047246531444007644 BTC, the hedging is a loss, and the loss is about -0.8058704560025944.

Research version of adjustment of handling fees

Triangular hedging (adjusting handling fees).ipynb

In [1]:

var fmz = require("fmz")                           // Import talib, TA, plot libraries automatically after introduction.
var task = fmz.VCtx({
start: '2019-04-09 17:49:00',
end: '2019-04-09 18:00:00',
period: '1m',
exchanges: [{"eid":"Huobi","currency":"ETH_BTC", "balance":1, "stocks":10,"fee":[0.04,0.04]},{"eid":"OKEX","currency":"ETH_USDT","balance":10000,"stocks":1,"fee":[0.04,0.04]}, {"eid":"OKEX","currency":"BTC_USDT","balance":10000,"stocks":1,"fee":[0.04,0.04]}]
})

Display of initial exchange account information and ticker:

  • Trading pair in Exchange A is: ETH_BTC

  • Trading pair in Exchange B is: ETH_USDT

  • Trading pair in Exchange C is: BTC_USDT

In [2]:

var accA = exchanges[0].GetAccount()
accA

Out[2]:
{ Balance: 1, FrozenBalance: 0, Stocks: 10, FrozenStocks: 0 }

In [3]:

var accB = exchanges[1].GetAccount()
accB

Out[3]:
{ Balance: 10000, FrozenBalance: 0, Stocks: 1, FrozenStocks: 0 }

In [4]:

var accC = exchanges[2].GetAccount()
accC

Out[4]:
{ Balance: 10000, FrozenBalance: 0, Stocks: 1, FrozenStocks: 0 }

In [5]:

var initSumBTC = accA.Balance + accC.Stocks
initSumBTC

Out[5]:
2

In [6]:

var initSumETH = accA.Stocks + accB.Stocks
initSumETH

Out[6]:
11

In [7]:

var initSumUSDT = accB.Balance + accC.Balance
initSumUSDT

Out[7]:
20000

It can be seen that the initial total number of BTC is 2, the total number of ETH is 11, and the total number of USDT is 20000. Now let's get the ticker information of each current trading pair.

In [8]:

var tickerA = exchanges[0].GetTicker()
tickerA

Out[8]:
{ Time: 1554831960000,
High: 0.03396501,
Low: 0.03396499,
Sell: 0.03396501,
Buy: 0.03396499,
Last: 0.033965,
Volume: 4696.555,
OpenInterest: 0 }

In [9]:

var tickerB = exchanges[1].GetTicker()
tickerB

Out[9]:
{ Time: 1554831960000,
High: 175.08000001,
Low: 175.07999999,
Sell: 175.08000001,
Buy: 175.07999999,
Last: 175.08,
Volume: 20730.37,
OpenInterest: 0 }

In [10]:

var tickerC = exchanges[2].GetTicker()
tickerC

Out[10]:
{ Time: 1554831960000,
High: 5161.90000001,
Low: 5161.89999999,
Sell: 5161.90000001,
Buy: 5161.89999999,
Last: 5161.9,
Volume: 2109.9292,
OpenInterest: 0 }

It can be seen that the subject matter of Exchange A, Exchange B and transaction is ETH, that is, exchange A/B can conduct indirect hedging operations, but they cannot hedge directly because the pricing currencies are different. The pricing currency of Exchange A is BTC, while that of Exchange B is USDT.
Assume that the exchange A performs a buy operation of exchanges[0].Buy(price, amount), and the price sold by the counterparty is used as the order price when buying, that is, tickerA.Sell. The exchange B performs a sell operation for hedging, that is, exchanges[1].Sell(price, amount), and the price that the counterparty wants to buy is used as the order price when selling, that is, tickerB.Buy. Because exchange A consumed BTC when buying, and exchange B obtained USDT when selling, exchange A needs to replace USDT with BTC to make up for the BTC consumed by exchange A, that is, exchange C executes exchanges[2].Buy(price, amount), and exchange USDT with BTC. To buy, exchange C needs to see the selling price of the counterparty, that is, tickerC.Sell. The buying price of exchange A must be lower than the selling price of exchange B, then Exchange C can buy back BTC consumed by Exchange A with less USDT. You will earn the difference of USDT amount.
So it is as follows:

In [11]:

var diffB2A = tickerA.Sell - tickerB.Buy / tickerC.Sell    // DiffB2A means that Exchange B sells ETH coins and Exchange A buys ETH coins, which is logically equivalent to the transfer of ETH from Exchange B to Exchange A.
diffB2A

Out[11]:
0.000047266535449966285

Obviously, the buying price of Exchange A is higher than that of Exchange B and Exchange C, and hedging is obviously money-losing.
The other hedging direction is the same. Exchange A executes the sell ETH operation, Exchange B executes the buy ETH operation, and Exchange C sells BTC for USDT. Achieving the currency is always balanced, and only USDT increases or decreases (i.e. the difference earned).

In [12]:

var diffA2B = tickerA.Buy - tickerB.Sell / tickerC.Buy
diffA2B

Out[12]:
0.000047246531444007644

Let's perform a tickerA.Buy - tickerB.Sell / tickerC.Buy hedging operation to find out how each of the values changes.

In [13]:

var idA = exchanges[0].Sell(tickerA.Buy, 1)
var nowAccA = exchanges[0].GetAccount()
nowAccA       // We can see that the handling fee is deducted from the BTC.

Out[13]:
{ Balance: 1.0339514,
FrozenBalance: 0,
Stocks: 9,
FrozenStocks: 0 }

In [14]:

var orderA = exchanges[0].GetOrder(idA)
orderA

Out[14]:
{ Id: 1,
Price: 0.03396499,
Amount: 1,
DealAmount: 1,
AvgPrice: 0.03396499,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'ETH_BTC_Huobi' }

In [15]:

var feeRatioA = 0.0004      // The default handling fee for backtesting is 0.2%, i.e. 0.002.
var feeA = orderA.DealAmount * orderA.AvgPrice * feeRatioA   // Exchange A trading handling fees, BTC denominated.
feeA

Out[15]:
0.000013585996

In [16]:

var idB = exchanges[1].Buy(tickerB.Sell, 1)
var nowAccB = exchanges[1].GetAccount()
nowAccB

Out[16]:
{ Balance: 9824.84996798,
FrozenBalance: 0,
Stocks: 2,
FrozenStocks: 0 }

In [17]:

var orderB = exchanges[1].GetOrder(idB)
orderB      // We can see that the handling fee is deducted from the USDT.

Out[17]:
{ Id: 1,
Price: 175.08000001,
Amount: 1,
DealAmount: 1,
AvgPrice: 175.08000001,
Type: 0,
Offset: 0,
Status: 1,
ContractType: 'ETH_USDT_OKEX' }

In [18]:

var feeRatioB = 0.0004
var feeB = orderB.DealAmount * orderB.AvgPrice * feeRatioB / tickerC.Last    // Exchange B handling fees, converted to BTC denomination.
feeB

Out[18]:
0.000013567097387396117

In [19]:

var idC = exchanges[2].Sell(tickerC.Buy, nowAccA.Balance - accA.Balance)
var nowAccC = exchanges[2].GetAccount()
nowAccC

Out[19]:
{ Balance: 10174.91841463,
FrozenBalance: 0,
Stocks: 0.9661,
FrozenStocks: 0 }

In [20]:

var orderC = exchanges[2].GetOrder(idC)
orderC     // We can see that the handling fee is deducted from the USDT.

Out[20]:
{ Id: 1,
Price: 5161.89999999,
Amount: 0.0339,
DealAmount: 0.0339,
AvgPrice: 5161.89999999,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'BTC_USDT_OKEX' }

In [21]:

var feeRatioC = 0.0004
var feeC = orderC.DealAmount * orderC.AvgPrice * feeRatioC / tickerC.Last      // Exchange C trading handling fees, BTC denominated.
feeC

Out[21]:
0.000013559999999973732

Calculate the total asset information after hedging.

In [22]:

var nowSumBTC = nowAccA.Balance + nowAccC.Stocks
nowSumBTC

Out[22]:
2.0000514000000003

In [23]:

var nowSumETH = nowAccA.Stocks + nowAccB.Stocks
nowSumETH

Out[23]:
11

In [24]:

var nowSumUSDT = nowAccB.Balance + nowAccC.Balance
nowSumUSDT

Out[24]:
19999.76838261

In [25]:

nowSumBTC - initSumBTC

Out[25]:
0.000051400000000256796

In [26]:

tickerC.Buy * (nowSumBTC - initSumBTC)    // The extra BTC is converted to USDT.

Out[26]:
0.26532166000081153

In [27]:

nowSumUSDT + tickerC.Buy * (nowSumBTC - initSumBTC) - initSumUSDT    // Profit and loss is calculated based on the changes of the account assets and is denominated in USDT.

Out[27]:
0.0337042700011807

In [28]:

(diffA2B - (feeA + feeB + feeC)) * tickerC.Buy     // Profit and loss based on price difference, denominated in USDT.

Out[28]:
0.03372495390449328

It can be seen that the price difference during hedging is diffA2B: 0.000047246531444007644.
The handling fee for the three hedging is converted to BTC as: feeA+feeB+feeC.

In [29]:

feeA + feeB + feeC

Out[29]:
0.00004071309338736985

It can be seen that in the backtest configuration, the handling fee is changed to "fee":[0.04,0.04], that is, 0.04%. At this time, feeA+feeB+feeC is equal to 0.00004071309338736985, which is basically close to the price difference diffA2B at the time of hedging. Finally, the calculated profit and loss is very small, only 0.03372495390449328, and the profit and loss calculated through account change is basically the same as that calculated through price difference. This research document can be used to calculate how much difference can be hedged for triangle hedging.

From: https://blog.mathquant.com/2022/12/29/use-the-research-environment-to-analyze-the-details-of-triangular-hedging-and-the-impact-of-handling-fees-on-hedgeable-price-difference.html