Some people may be unfamiliar with the word “arbitrage”, but “arbitrage” is very common in real life. For example, the owner of a convenience store buys a bottle of mineral water from the wholesale market for 0.5 yuan, then sells it in the store for 1 yuan, and finally earns a difference of 0.5 yuan. This process is actually similar to arbitrage. Arbitrage in financial markets is similar to this principle, except that there are many forms of arbitrage.
In the commodity futures market, in theory, the price of the Apple contract delivered in May minus the price of the Apple contract delivered in October, the result should be close to 0 or stable within a certain price range. But in fact, due to weather, market supply and demand and other factors, the price of short-term and long-term contracts will be affected to varying degrees over a period of time, and the price difference will also fluctuate significantly.
But in any case, the price difference will eventually return to a certain price range, then if the price difference is greater than this range, sell short the May contract, and buy long the October contract at the same time, short the difference to make a profit; if the price difference is less than this range, buy long May contract, at the same time sell short October contract, make a profit of buying long the spread. This is the intertemporal arbitrage through buying and selling the same variety but different delivery months.
In addition to intertemporal arbitrage, there are cross-market arbitrage such as buying soybeans from exporting countries while selling soybeans from importing countries, or selling soybeans from exporting countries and importing soybeans from importing countries; buying upstream raw materials, iron ore, and selling downstream finished thread Steel, or sell the upstream raw material iron ore while buying downstream finished rebar arbitrage, etc.
Although the above arbitrage methods are literally “arbitrage”, they are not purely arbitrage. they are essentially risky speculation. This way of speculation is to make profit by buying long or selling short the price spreads. Although the spread has stabilized for most of the time, there may be a market situation that the price spread does not return for a long time.
The core principle of “futures and spots” arbitrage is that the same commodity can only have one price at the same time point. Futures will become a spot when the delivery time is reached, so a price return will be forced when the contract delivery time is near. This is completely different from intertemporal arbitrage. The intertemporal arbitrage is a contract with two different delivery months. When it expires, it becomes the spot of two different months. Or it can be two prices.
The biggest feature of “futures and spots” arbitrage is that there is no risk in theory, mainly based on the spread of the state to calculate the profit range. If the spread is too large, you can long the spot and short the futures at the same time, wait for the spread to return to zero, you can close the position on both sides of the futures and spot, and earn a profit from the spread. There are two main methods: “double close position” arbitrage and “contract delivery” arbitrage.
To put it simply, the most complicated link is spot trading of commodities, which involves a series of issues such as warehouse receipts, taxation and so on. First of all, a company related to the investment scope is needed. If it is a contract delivery arbitrage futures account, it must be a corporate legal person. If double close position arbitrage is needed, a reliable sales channel is needed. There are many online spot trading websites.
It should be noted that spot transactions usually have a value-added tax of 17% to 20%, so if it is a double close position arbitrage, you need to short futures 1.2 to 1.25 times after buying spot. In the case of contract delivery arbitrage, you need to short the same proportion of futures after buying the spot, and you also need to consider the costs of transaction fees, transportation, and warehouses. Of course, the premise of all this is that the current price spread is large enough and there are enough boundaries.
In addition, due to the existence of gold (T+D) on the Shanghai Gold Exchange, the current arbitrage in the gold period can not only be positive arbitrage, but also reverse arbitrage operations without gold leasing. The deferred trading of spot gold (T+D) on the Shanghai Gold Exchange is not only convenient to trade, but also has a large volume of transactions and positions, and liquidity is very suitable for “futures and spots” arbitrage.
There are many types of spot and spread data online, most of which are presented in the form of tables, which is obviously not suitable for analyzing and judging the market. FMZ Quant trading platform (FMZ.COM) has built-in commodity futures fundamental data, including spot data and spread data. Only need to call a function to get the spot and spread price of each variety, and support historical data from 2016 to the present.
# Backtest configuration
'''backtest
start: 2020-06-01 00:00:00
end: 2020-06-02 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
# Strategy entry
def main():
while True:
ret = exchange.GetData("GDP") # Calling GDP data
Log(ret) # Print data
Sleep(1000 * 60 * 60 * 24 * 30)
Return result
{
"Quarterly": "Q1 2006",
"GDP": {
"Absolute Value (100 million yuan)": 47078.9,
"YoY Growth": 0.125
},
"primary industry": {
"Absolute Value (100 million yuan)": 3012.7,
"YoY Growth": 0.044
},
"Tertiary Industry": {
"Absolute Value (100 million yuan)": 22647.4,
"YoY Growth": 0.131
},
"Secondary industry": {
"Absolute Value (100 million yuan)": 21418.7,
"YoY Growth": 0.131
}
}
Let us use FMZ platform to quantify and realize spot prices and spread prices in the form of charts. First, register and log in to the FMZ website (FMZ.COM), click “Dashboard” , and click Strategy Library + New Strategy. Select Python in the drop-down menu in the upper left corner and fill in the name of the strategy.
# Strategy main function
def onTick():
pass
# Strategy entrance
def main():
while True: # Enter loop mode
onTick() # execution strategy main function
Sleep(1000 * 60 * 60 * 24) # Strategy sleep for one day
The strategy framework are two functions, the main
function is the entrance of the strategy, the main
function is the pre-processing before the trading, the program will start from the main
function, and then enter the infinite loop mode, repeatedly execute the onTick
function, the onTick
function is the main function of the strategy , Mainly execute the core code.
# Global variables
# Futures and Spots chart
cfgA = {
"extension": {
"layout":'single',
"col": 6,
"height": "500px",
},
"title": {
"text": "futures and spots chart"
},
"xAxis": {
"type": "datetime"
},
"series": [{
"name": "Futures Price",
"data": [],
}, {
"name": "Spot Price",
"data": [],
}
]
}
# Spread chart
cfgB = {
"extension": {
"layout":'single',
"col": 6,
"height": "500px",
},
"title": {
"text": "Spread chart"
},
"xAxis": {
"type": "datetime"
},
"series": [{
"name": "Spread Price",
"data": [],
}]
}
chart = Chart([cfgA, cfgB]) # Create a chart object
# Strategy main function
def onTick():
chart.add(0, []) # draw chart
chart.add(1, []) # draw chart
chart.add(2, []) # draw chart
chart.update([cfgA, cfgB]) # update chart
# Strategy entrance
def main():
LogReset() # Clear the previous log information before running
chart.reset() # Clear the previous chart information before running
while True: # Enter loop mode
onTick() # execution strategy main function
Sleep(1000 * 60 * 60 * 24) # Strategy sleep for one day
In this strategy, a total of 2 charts have been created and are arranged side by side. Among them, cfgA
on the left is a current chart, including futures prices and spot prices, and cfgB
on the right is a spread chart. Then call the FMZ platform built-in Python line drawing library to create a chart object. Finally, the data in the chart is updated in real time in the onTick
function.
last_spot_price = 0 # Save the last valid spot price
last_spread_price = 0 # Save the last valid spread price
def onTick():
global last_spread_price, last_spot_price # import global variables
exchange.SetContractType("i888") # Subscribe to futures varieties
futures = _C(exchange.GetRecords)[-1] # Get the latest K line data
futures_ts = futures.Time # Get the latest K-line futures timestamp
futures_price = futures.Close # Get the latest K-line closing price
spot = exchange.GetData("SPOTPRICE") # Get spot data
spot_ts = spot.Time # Get spot timestamp
if 'iron ore' in spot.Data:
spot_price = spot.Data['iron ore']
last_spot_price = spot_price
else:
spot_price = last_spot_price
spread = exchange.GetData("spread") # Get spread data
spread_ts = spread.Time # Get spread timestamp
if 'iron ore' in spread.Data:
spread_price = spread.Data['iron ore']
last_spread_price = spread_price
else:
spread_price = last_spread_price
In total, we need to obtain three kinds of data: futures price, spot price, and spread price. Obtaining the futures price is simple. Use the SetContractType
function to directly subscribe to the futures symbol, and then use the GetRecords
function to obtain the closing price of the K line. For the prices of spot and spread, you can use the method introduced earlier, use the GetData
function to call the fundamental data code, and return the dictionary data that contains the timestamp.
# fmz@b72930603791887d7452f25f23a13bde
'''backtest
start: 2017-01-01 00:00:00
end: 2020-06-01 00:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
# Global variables
# Futures and Spots chart
cfgA = {
"extension": {
"layout":'single',
"col": 6,
"height": "500px",
},
"title": {
"text": "futures and spots chart"
},
"xAxis": {
"type": "datetime"
},
"series": [{
"name": "Futures Price",
"data": [],
}, {
"name": "Spot Price",
"data": [],
}
]
}
# spread chart
cfgB = {
"extension": {
"layout":'single',
"col": 6,
"height": "500px",
},
"title": {
"text": "spread chart"
},
"xAxis": {
"type": "datetime"
},
"series": [{
"name": "spread Price",
"data": [],
}]
}
last_spot_price = 0 # Save the last valid spot price
last_spread_price = 0 # Save the last valid spread price
chart = Chart([cfgA, cfgB]) # Create a chart object
def onTick():
global last_spread_price, last_spot_price # import global variables
exchange.SetContractType("i888") # Subscribe to futures varieties
futures = _C(exchange.GetRecords)[-1] # Get the latest candlestick data
futures_ts = futures.Time # Get the latest K-line futures timestamp
futures_price = futures.Close # Get the latest K-line closing price
Log('Future price:', futures_ts, futures_price)
spot = exchange.GetData("SPOTPRICE") # Get spot data
spot_ts = spot.Time # Get spot timestamp
if 'iron ore' in spot.Data:
spot_price = spot.Data['iron ore']
last_spot_price = spot_price
else:
spot_price = last_spot_price
Log('Spot price:', spot_ts, spot_price)
spread = exchange.GetData("spread") # Get spread data
spread_ts = spread.Time # Get spread timestamp
if 'iron ore' in spread.Data:
spread_price = spread.Data['iron ore']
last_spread_price = spread_price
else:
spread_price = last_spread_price
Log('spread price:', spread_ts, spread_price)
chart.add(0, [futures_ts, futures_price]) # draw chart
chart.add(1, [spot_ts, spot_price]) # draw chart
chart.add(2, [spread_ts, spread_price]) # draw chart
chart.update([cfgA, cfgB]) # update chart
Log('---------')
# Strategy entrance
def main():
LogReset() # Clear the previous log information before running
chart.reset() # Clear the previous chart information before running
while True: # Enter loop mode
onTick() # execution strategy main function
Sleep(1000 * 60 * 60 * 24) # Strategy sleep for one day
The complete strategy has been posted on the FMZ platform (FMZ.COM) strategy square, it can be used directly by clicking the link below.
https://www.fmz.com/strategy/211941
Arbitrage is not as complicated as imagined. It does not require too much knowledge of financial theory, nor does it require too complicated mathematical or statistical models. Arbitrage is essentially to make a profit from an unreasonable price to a reasonable return. Market conditions change every year. For traders, it is best not to copy historical data to the present, but to combine the current data to study whether the price spread is reasonable.