Better tools make good work -- learn to use the research environment to analyze trading principles

FMZ QuantFMZ Quant
15 min read

The term "hedging" is a very basic concept in the field of quantitative trading and program trading. In the quantitative trading of digital currency, the hedging strategies often used are: futures-spot hedging, cross-period hedging and spot hedging, which are essentially transactions for price differences. Maybe when it comes to the concept, principle and details of hedging, many students who have just entered the field of quantitative trading are still not very clear. It doesn't matter. Let's use the "Analyze" tool provided by the FMZ Quant Trading platform to learn and master this knowledge easily.

In the Dashboard of FMZ Quant platform, click "Analyze" to jump to the tool's page:

Here I upload the analysis file directly:
This analysis document is an analysis of the process of opening and closing a futures-spot hedging position during the backtesting. The futures exchange is OKX futures, and the contract is a quarter contract. The spot exchange is OKX currency-currency transaction, and the trading pair is BTC_USDT. To analyze the operation process of the futures-spot hedging, you can see the following specific research environment file, written in two versions: a Python language version, a JavaScript language version.

Research environment Python language file

Analysis on the principle of futures-spot hedging.ipynb
In [1]:

from fmz import *
task = VCtx('''backtest
start: 2019-09-19 00:00:00
end: 2019-09-28 12:00:00
period: 15m
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD", "stocks":1}, {"eid":"OKX","currency":"BTC_USDT","balance":10000,"stocks":0}]
''')
# Create backtesting environment
import matplotlib.pyplot as plt
import numpy as np
# Import the plot library matplotlib and library numpy

In [2]:

exchanges[0].SetContractType("quarter")    # The first exchange object OKX Futures (eid: Futures_OKCoin) calls the function to set the current contract as a quarterly contract
initQuarterAcc = exchanges[0].GetAccount() # The initial account information of OKX Futures Exchange is recorded in the variable initQuarterAcc
initQuarterAcc

Out[2]:
{'Balance': 0.0, 'FrozenBalance': 0.0, 'Stocks': 1.0, 'FrozenStocks': 0.0}

In [3]:

initSpotAcc = exchanges[1].GetAccount()    # The initial account information of the OKX Spot Exchange is recorded in the variable initSpotAcc
initSpotAcc

Out[3]:
{'Balance': 10000.0, 'FrozenBalance': 0.0, 'Stocks': 0.0, 'FrozenStocks': 0.0}

In [4]:

quarterTicker1 = exchanges[0].GetTicker()  # Get the futures exchange ticker, recorded in the variable quarterTicker1
quarterTicker1

Out[4]:
{'Time': 1568851210000,
'High': 10441.25002,
'Low': 10441.25,
'Sell': 10441.25002,
'Buy': 10441.25,
'Last': 10441.25001,
'Volume': 1772.0,
'OpenInterest': 0.0}

In [5]:

spotTicker1 = exchanges[1].GetTicker()     # Get the spot exchange ticker, recorded in the variable spotTicker1
spotTicker1

Out[5]:
{'Time': 1568851210000,
'High': 10156.60000002,
'Low': 10156.6,
'Sell': 10156.60000002,
'Buy': 10156.6,
'Last': 10156.60000001,
'Volume': 7.4443,
'OpenInterest': 0.0}

In [6]:

quarterTicker1.Buy - spotTicker1.Sell      # The price difference between going short on futures and going long on spot.

Out[6]:
284.64999997999985

In [7]:

exchanges[0].SetDirection("sell")                       # Set up a futures exchange and trade in the direction of going short
quarterId1 = exchanges[0].Sell(quarterTicker1.Buy, 10)  # Futures go short to place orders. The order quantity is 10 contracts. The returned order ID is recorded in the variable quarterId1.
exchanges[0].GetOrder(quarterId1)                       # Check the details of the order with futures order ID quarterId1.

Out[7]:
{'Id': 1,
'Price': 10441.25,
'Amount': 10.0,
'DealAmount': 10.0,
'AvgPrice': 10441.25,
'Type': 1,
'Offset': 0,
'Status': 1,
'ContractType': b'quarter'}

In [8]:

spotAmount = 10 * 100 / quarterTicker1.Buy                 # Calculate the currency equivalent of 10 contracts as the order quantity of the spot.
spotId1 = exchanges[1].Buy(spotTicker1.Sell, spotAmount)   # Place orders on the spot exchange
exchanges[1].GetOrder(spotId1)                             # Check the order details of the spot order ID of spotId1

Out[8]:
{'Id': 1,
'Price': 10156.60000002,
'Amount': 0.0957,
'DealAmount': 0.0957,
'AvgPrice': 10156.60000002,
'Type': 0,
'Offset': 0,
'Status': 1,
'ContractType': b'BTC_USDT_OKX'}

You can see that the orders quarterId1 and spotId1 orders are fully filled, i.e. the hedging of open positions is completed.

In [9]:

Sleep(1000 * 60 * 60 * 24 * 7)       # Hold the position for a while and wait for the price difference to become smaller to close the position.

After the waiting time, prepare to close the position. Get the current ticker quarterTicker2, spotTicker2 and print them.
The transaction direction of the futures exchange object is set to close the short position: exchanges[0].SetDirection("closesell") place an order to close the position.
Print the details of the closing position order, showing that the closing order has been completed, the closing position finished.

In [10]:

quarterTicker2 = exchanges[0].GetTicker()     # Get the current futures exchange ticker, recorded in the variable quarterTicker2
quarterTicker2

Out[10]:
{'Time': 1569456010000,
'High': 8497.20002,
'Low': 8497.2,
'Sell': 8497.20002,
'Buy': 8497.2,
'Last': 8497.20001,
'Volume': 4311.0,
'OpenInterest': 0.0}

In [11]:

spotTicker2 = exchanges[1].GetTicker()       # Get the current ticker of the spot exchange, recorded in the variable spotTicker2
spotTicker2

Out[11]:
{'Time': 1569456114600,
'High': 8444.70000001,
'Low': 8444.69999999,
'Sell': 8444.70000001,
'Buy': 8444.69999999,
'Last': 8444.7,
'Volume': 78.6273,
'OpenInterest': 0.0}

In [12]:

quarterTicker2.Sell - spotTicker2.Buy        # The price difference between closing a short futures position and closing a long spot position.

Out[12]:
52.5000200100003

In [13]:

exchanges[0].SetDirection("closesell")                   # Set the current trading direction of the futures exchange to close short positions.
quarterId2 = exchanges[0].Buy(quarterTicker2.Sell, 10)   # The futures exchange places an order to close a position and records the order ID to the variable quarterId2.
exchanges[0].GetOrder(quarterId2)                        # Check futures close out order details

Out[13]:
{'Id': 2,
'Price': 8497.20002,
'Amount': 10.0,
'DealAmount': 10.0,
'AvgPrice': 8493.95335,
'Type': 0,
'Offset': 1,
'Status': 1,
'ContractType': b'quarter'}

In [14]:

spotId2 = exchanges[1].Sell(spotTicker2.Buy, spotAmount) # The spot exchange places an order to close a position and records the order ID, which is recorded to the variable spotId2.
exchanges[1].GetOrder(spotId2)      # Check spot close out order details

Out[14]:
{'Id': 2,
'Price': 8444.69999999,
'Amount': 0.0957,
'DealAmount': 0.0957,
'AvgPrice': 8444.69999999,
'Type': 1,
'Offset': 0,
'Status': 1,
'ContractType': b'BTC_USDT_OKX'}

In [15]:

nowQuarterAcc = exchanges[0].GetAccount()   # Get the current futures exchange account information, recorded in the variable nowQuarterAcc.
nowQuarterAcc

Out[15]:
{'Balance': 0.0,
'FrozenBalance': 0.0,
'Stocks': 1.021786026184,
'FrozenStocks': 0.0}

In [16]:

nowSpotAcc = exchanges[1].GetAccount()      # Get the current spot exchange account information, recorded in the variable nowSpotAcc.
nowSpotAcc

Out[16]:
{'Balance': 9834.74705446,
'FrozenBalance': 0.0,
'Stocks': 0.0,
'FrozenStocks': 0.0}

By comparing the initial account with the current account, the profit and loss of the hedging operation is calculated.

In [17]:

diffStocks = abs(nowQuarterAcc.Stocks - initQuarterAcc.Stocks)
diffBalance = nowSpotAcc.Balance - initSpotAcc.Balance
if nowQuarterAcc.Stocks - initQuarterAcc.Stocks > 0 :
    print("profits:", diffStocks * spotTicker2.Buy + diffBalance)
else :
    print("profits:", diffBalance - diffStocks * spotTicker2.Buy)

Out[17]:
Profits: 18.72350977580652

Now let's see why the hedging is profitable. We can see the chart drawn. The futures price is a blue line and the spot price is an orange line. Both prices are decreasing. The futures price is decreasing faster than the spot price.

In [18]:

xQuarter = [1, 2]
yQuarter = [quarterTicker1.Buy, quarterTicker2.Sell]

xSpot = [1, 2]
ySpot = [spotTicker1.Sell, spotTicker2.Buy]
plt.plot(xQuarter, yQuarter, linewidth=5)
plt.plot(xSpot, ySpot, linewidth=5)
plt.show()

Out[18]:

Let's take a look at the change of the price difference. The price difference ranges from 284 at the time of hedging opening position (that is, futures go short, and spot go long) to 52 at the time of closing position (futures short position closed, spot long position closed). The price difference is from large to small.

In [19]:

xDiff = [1, 2]
yDiff = [quarterTicker1.Buy - spotTicker1.Sell, quarterTicker2.Sell - spotTicker2.Buy]
plt.plot(xDiff, yDiff, linewidth=5)
plt.show()

Out[19]:

For example, a1 is the futures price at time 1, and b1 is the spot price at time 1. A2 is the futures price at time 2, and b2 is the spot price at time 2.
As long as the futures spot price difference at time 1 (a1-b1) is greater than the futures spot price difference at time 2 (a2-b2), a1 - a2>b1 - b2 can be introduced.
There are three situations: (the quantity of futures and spot positions is the same)

  1. a1 - a2 is greater than 0, b1 - b2 is greater than 0
    a1 - a2 refers to the price difference of futures profits, and b1 - b2 refers to the price difference of spot losses (because the spot went long, the price of starting to buy is higher than the price of selling to close the position, so the money is lost), but the futures profits are greater than the spot losses. So it is profitable as a whole. This situation corresponds to the chart in step In [8].

  2. a1 - a2 is greater than 0, b1 - b2 is less than 0
    a1 - a2 is the price difference of futures profits, and b1 - b2 is the price difference of spot profits (b1 - b2 is less than 0, indicating that b2 is greater than b1, that is, the price of opening position and buying is low, and the price of selling and closing is high, so it is profitable).

  3. a1 - a2 less than 0, b1 - b2 less than 0
    a1 - a2 is the price difference of futures losses, and b1 - b2 is the price difference of spot profits. Since a1 - a2 > b1 - b2, the absolute value of a1 - a2 is less than the absolute value of b1 - b2, and the spot profits are greater than the futures losses. It is profitable as a whole.

There is no case where a1 - a2 is less than 0 and b1 - b2 is greater than 0, because a1 - a2 > b1 - b2 has been defined. Similarly, if a1 - a2 is equal to 0, since a1 - a2 > b1 - b2 is defined, b1 - b2 must be less than 0. Therefore, as long as the hedging method of short futures and long spot meets the conditions a1 - b1 > a2 - b2, the opening and closing position operations are profit hedging.

For example, the following model is one of the cases:

In [20]:

pythona1 = 10
b1 = 5
a2 = 11
b2 = 9

# a1 - b1 > a2 - b2 launches: a1 - a2 > b1 - b2

xA = [1, 2]
yA = [a1, a2]

xB = [1, 2]
yB = [b1, b2]
plt.plot(xA, yA, linewidth=5)
plt.plot(xB, yB, linewidth=5)
plt.show()

Out[20]:

Research environment JavaScript language file

The research environment supports not only Python, but also JavaScript
I also give an example of JavaScript research environment:

Analysis of the principle of futures-spot hedging (JavaScript).ipynb
In [1]:

// Import the required package, click "Save settings" on the FMZ's "Strategy editing page" to get the string configuration and convert it to an object.
var fmz = require("fmz")                           // Import the talib, TA, and plot libraries automatically after import
var task = fmz.VCtx({
start: '2019-09-19 00:00:00',
end: '2019-09-28 12:00:00',
period: '15m',
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD","stocks":1},{"eid":"OKX","currency":"BTC_USDT","balance":10000,"stocks":0}]
})

In [2]:

exchanges[0].SetContractType("quarter")  // The first exchange object OKX Futures (eid: Futures_OKCoin) calls the function to set the current contract as a quarterly contract.
var initQuarterAcc = exchanges[0].GetAccount()  // The initial account information of OKX Futures Exchange is recorded in the variable initQuarterAcc.
initQuarterAcc

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

In [3]:

var initSpotAcc = exchanges[1].GetAccount()     // The initial account information of the OKX Spot Exchange is recorded in the variable initSpotAcc.
initSpotAcc

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

In [4]:

var quarterTicker1 = exchanges[0].GetTicker()   // Get the futures exchange ticker, recorded in the variable quarterTicker1.
quarterTicker1

Out[4]:
{ Time: 1568851210000,
High: 10441.25002,
Low: 10441.25,
Sell: 10441.25002,
Buy: 10441.25,
Last: 10441.25001,
Volume: 1772,
OpenInterest: 0 }

In [5]:

var spotTicker1 = exchanges[1].GetTicker()     // Get the spot exchange ticker, recorded in the variable spotTicker1.
spotTicker1

Out[5]:
{ Time: 1568851210000,
High: 10156.60000002,
Low: 10156.6,
Sell: 10156.60000002,
Buy: 10156.6,
Last: 10156.60000001,
Volume: 7.4443,
OpenInterest: 0 }

In [6]:

quarterTicker1.Buy - spotTicker1.Sell         // The price difference between going short on futures and going long on spot.

Out[6]:
284.64999997999985
In [7]:

exchanges[0].SetDirection("sell")             // Set up a futures exchange and trade in the direction of going short
var quarterId1 = exchanges[0].Sell(quarterTicker1.Buy, 10)  // Go short futures to place orders. The order quantity is 10 contracts. The returned order ID is recorded in the variable quarterId1.
exchanges[0].GetOrder(quarterId1)                           // Check the details of the order with futures order ID quarterId1.

Out[7]:
{ Id: 1,
Price: 10441.25,
Amount: 10,
DealAmount: 10,
AvgPrice: 10441.25,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'quarter' }

In [8]:

var spotAmount = 10 * 100 / quarterTicker1.Buy                  // Calculate the currency equivalent of 10 contracts as the order quantity of the spot.
var spotId1 = exchanges[1].Buy(spotTicker1.Sell, spotAmount)    // Place orders on the spot exchange.
exchanges[1].GetOrder(spotId1)                                  // Check the order details of the spot order ID of spotId1.

Out[8]:
{ Id: 1,
Price: 10156.60000002,
Amount: 0.0957,
DealAmount: 0.0957,
AvgPrice: 10156.60000002,
Type: 0,
Offset: 0,
Status: 1,
ContractType: 'BTC_USDT_OKX' }

You can see that the orders quarterId1 and spotId1 are fully filled, i.e. the hedging of opening positions is completed.

In [9]:

Sleep(1000 * 60 * 60 * 24 * 7)            // Hold the position for a while and wait for the price difference to become smaller to close the position.

After the waiting time, prepare to close the position. Get the current ticker quarterTicker2, spotTicker2 and print them.
The transaction direction of the futures exchange object is set to close the short position: exchanges[0].SetDirection("closesell") place an order to close the position.
Print the details of the closing position order, showing that the closing order has been completed, the closing position finished.

In [10]:

var quarterTicker2 = exchanges[0].GetTicker()    // Get the current futures exchange ticker, recorded in the variable quarterTicker2.
quarterTicker2

Out[10]:
{ Time: 1569456010000,
High: 8497.20002,
Low: 8497.2,
Sell: 8497.20002,
Buy: 8497.2,
Last: 8497.20001,
Volume: 4311,
OpenInterest: 0 }

In [11]:

var spotTicker2 = exchanges[1].GetTicker()       // Get the current ticker of the spot exchange, recorded in the variable spotTicker2.
spotTicker2

Out[11]:
{ Time: 1569456114600,
High: 8444.70000001,
Low: 8444.69999999,
Sell: 8444.70000001,
Buy: 8444.69999999,
Last: 8444.7,
Volume: 78.6273,
OpenInterest: 0 }

In [12]:

quarterTicker2.Sell - spotTicker2.Buy            // The price difference between closing short position of futures and closing long position of spot.

Out[12]:
52.5000200100003

In [13]:

exchanges[0].SetDirection("closesell")           // Set the current trading direction of the futures exchange to close short positions.
var quarterId2 = exchanges[0].Buy(quarterTicker2.Sell, 10)       // The futures exchange places an order to close the position, and records the order ID to the variable quarterId2.
exchanges[0].GetOrder(quarterId2)                                // Check futures closing position order details.

Out[13]:
{ Id: 2,
Price: 8497.20002,
Amount: 10,
DealAmount: 10,
AvgPrice: 8493.95335,
Type: 0,
Offset: 1,
Status: 1,
ContractType: 'quarter' }

In [14]:

var spotId2 = exchanges[1].Sell(spotTicker2.Buy, spotAmount)     // The spot exchange places an order to close the position, and records the order ID to the variable spotId2.
exchanges[1].GetOrder(spotId2)                                   // Check spot closing position order details.

Out[14]:
{ Id: 2,
Price: 8444.69999999,
Amount: 0.0957,
DealAmount: 0.0957,
AvgPrice: 8444.69999999,
Type: 1,
Offset: 0,
Status: 1,
ContractType: 'BTC_USDT_OKX' }

In [15]:

var nowQuarterAcc = exchanges[0].GetAccount()     // Get the current futures exchange account information, recorded in the variable nowQuarterAcc.
nowQuarterAcc

Out[15]:
{ Balance: 0,
FrozenBalance: 0,
Stocks: 1.021786026184,
FrozenStocks: 0 }

In [16]:

var nowSpotAcc = exchanges[1].GetAccount()        // Get the current spot exchange account information, recorded in the variable nowSpotAcc.
nowSpotAcc

Out[16]:
{ Balance: 9834.74705446,
FrozenBalance: 0,
Stocks: 0,
FrozenStocks: 0 }
By comparing the initial account with the current account, the profit and loss of the hedging operation is calculated.

In [17]:

var diffStocks = Math.abs(nowQuarterAcc.Stocks - initQuarterAcc.Stocks)
var diffBalance = nowSpotAcc.Balance - initSpotAcc.Balance
if (nowQuarterAcc.Stocks - initQuarterAcc.Stocks > 0) {
    console.log("profits:", diffStocks * spotTicker2.Buy + diffBalance)
} else {
    console.log("profits:", diffBalance - diffStocks * spotTicker2.Buy)
}

Out[17]:
Profits: 18.72350977580652

Now let's see why the hedging is profitable. We can see the chart drawn. The futures price is a blue line and the spot price is an orange line. Both prices are decreasing. The futures price is decreasing faster than the spot price.

In [18]:

var objQuarter = {
    "index" : [1, 2],                                          // The index is 1, that is, the first time, the opening time, and 2 is the closing time.
    "arrPrice" : [quarterTicker1.Buy, quarterTicker2.Sell],
}
var objSpot = {
    "index" : [1, 2],
    "arrPrice" : [spotTicker1.Sell, spotTicker2.Buy],
}
plot([{name: 'quarter', x: objQuarter.index, y: objQuarter.arrPrice}, {name: 'spot', x: objSpot.index, y: objSpot.arrPrice}])

Out[18]:
Let's take a look at the change of the price difference. The price difference ranges from 284 at the time of hedging opening positions (that is, futures went short, and the spot went long) to 52 at the time of closing (close short position of futures, and close long position of spot). The price difference is from large to small.

In [19]:

var arrDiffPrice = [quarterTicker1.Buy - spotTicker1.Sell, quarterTicker2.Sell - spotTicker2.Buy]
plot(arrDiffPrice)

Out[19]:
For example, a1 is the futures price at time 1, and b1 is the spot price at time 1. A2 is the futures price at time 2, and b2 is the spot price at time 2.
As long as the futures spot price difference at time 1 (a1-b1) is greater than the futures spot price difference at time 2 (a2-b2), a1 - a2>b1 - b2 can be introduced.
There are three situations: (the quantity of futures and spot positions is the same)

  1. a1 - a2 is greater than 0, b1 - b2 is greater than 0
    a1 - a2 refers to the price difference of futures profits, and b1 - b2 refers to the price difference of spot losses (because the spot went long, the price of starting to buy is higher than the price of selling to close the position, so the money is lost), but the futures profits are greater than the spot losses. So it is profitable as a whole. This situation corresponds to the chart in step In [8].

  2. a1 - a2 is greater than 0, b1 - b2 is less than 0
    a1 - a2 is the price difference of futures profits, and b1 - b2 is the price difference of spot profits (b1 - b2 is less than 0, indicating that b2 is greater than b1, that is, the price of opening position and buying is low, and the price of selling and closing is high, so it is profitable).

  3. a1 - a2 less than 0, b1 - b2 less than 0
    a1 - a2 is the price difference of futures losses, and b1 - b2 is the price difference of spot profits. Since a1 - a2 > b1 - b2, the absolute value of a1 - a2 is less than the absolute value of b1 - b2, and the spot profits are greater than the futures losses. It is profitable as a whole.

There is no case where a1 - a2 is less than 0 and b1 - b2 is greater than 0, because a1 - a2 > b1 - b2 has been defined. Similarly, if a1 - a2 is equal to 0, since a1 - a2 > b1 - b2 is defined, b1 - b2 must be less than 0. Therefore, as long as the hedging method of short futures and long spot meets the conditions a1 - b1 > a2 - b2, the opening and closing position operations are profit hedging.

For example, the following model is one of the cases:

In [20]:

var a1 = 10
var b1 = 5
var a2 = 11
var b2 = 9

// a1 - b1 > a2 - b2 launches: a1 - a2 > b1 - b2

var objA = {
    "index" : [1, 2],
    "arrPrice" : [a1, a2],
}
var objB = {
    "index" : [1, 2],
    "arrPrice" : [b1, b2],
}
plot([{name : "a", x : objA.index, y : objA.arrPrice}, {name : "b", x : objB.index, y : objB.arrPrice}])

Out[20]:

Try it out, guys!

From: https://blog.mathquant.com/2022/12/27/better-tools-make-good-work-learn-to-use-the-research-environment-to-analyze-trading-principles.html

0
Subscribe to my newsletter

Read articles from FMZ Quant directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

FMZ Quant
FMZ Quant

Quantitative Trading For Everyone