Use the research environment to analyze the details of triangular hedging and the impact of handling fees on hedgeable price difference
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.