I. Summary
George Soros put forward an important proposition in "The Alchemy of Finance" written in 1987: I believe the market prices are always wrong in the sense that they present a Biased view of the future. The market efficiency hypothesis is only a theoretical hypothesis. In fact, market participants are not always rational, and at each time point, participants cannot completely obtain and objectively interpret all information. Moreover, even if it is the same information, everyone's feedback is different. That is to say, the price itself already contains the wrong expectations of market participants, so in essence, the market price is always wrong. This may be the profit source of arbitrageurs.
II. Strategic principles
According to the above principles, we can know that in an ineffective futures market, the reason why the market impact on delivery contracts in different periods is not always synchronous, and the pricing is not completely effective. Then, based on the delivery contract price of the same transaction object in different periods, if there is a large price difference between the two prices, we can buy and sell futures contracts in different periods at the same time for cross-period arbitrage. Like commodity futures, digital currency also has a cross-period arbitrage contract portfolio. For example, in the OKEX exchange, there are: ETC current week, ETC next week, ETC quarter.
For example, suppose that the price difference between the current week of ETC and the quarter of ETC remains around 5 for a long time. If the price difference reaches 7 one day, we expect that the price difference will return to 5 in the future. Then we can sell ETC that week and buy ETC quarter at the same time to go short the price difference, vice versa. Although this price difference exists, there are many uncertainties in manual arbitrage due to time-consuming manual operations, poor accuracy and the impact of price changes. The charm of quantitative arbitrage lies in capturing arbitrage opportunities through quantitative models and formulating arbitrage trading strategies, as well as placing trading orders automatically to exchanges through programmed algorithms, so as to capture opportunities quickly and accurately and make profits efficiently and stably.
III. Strategy logic
This article will teach you how to use the FMZ Quant Trading Platform and the ETC futures contract in the OKEX exchange to demonstrate how to capture the instantaneous arbitrage opportunities, seize the profits that can be seen every time, and hedge the risks that may be encountered in the digital currency trading with a simple arbitrage strategy.
Create a cross-period arbitrage strategy for digital currency
Difficulty: Normal
Strategy environment
- Transaction object: Ether Classic (ETC)
- Spread data: ETC current week - ETC quarter (omit cointegration test)
- Transaction period: 5 minutes
- Position matching: 1:1
- Transaction type: cross period of the same type
Strategy logic
- Conditions for opening positions with going long the price difference: if the current account has no positions and the price difference is less than the lower bound of the boll, then go long the price difference. That is, buy opening positions ETC for the week, sell opening positions ETC for the quarter.
- Conditions for opening positions with going short the price difference: if there is no position in the current account, and the price difference is greater than the upper bound of the boll, then go short the price difference. That is, sell opening positions ETC for the week, buy opening positions ETC for the quarter.
- Conditions for closing positions with going long the price difference: if the current account holds going long orders of ETC in the current week and holds going short orders of ETC quarter, and the price difference is greater than the middle bound of the boll, then close long the price difference. That is, sell closing positions ETC for the week, buy closing positions ETC for the quarter.
- Conditions for closing positions with going short the price difference: if the current account holds going short orders of ETC in the current week, and holds going long orders of ETC quarter, and the price difference is less than the middle bound of the boll, then close short the price difference. That is, buy closing positions ETC for the week, sell closing positions ETC for the quarter.
IV. Write a strategy framework
The above is a simple logic description of the cross-period arbitrage strategy of digital currency. So how to implement our ideas in the program? We try to build the framework on the FMZ Quant Trading Platform.
function Data() {} // Basic data function Data.prototype.mp = function () {} // Position function Data.prototype.boll = function () {} // Indicator function Data.prototype.trade = function () {} // Order placement function Data.prototype.cancelOrders = function () {} // Order withdrawal function Data.prototype.isEven = function () {} // Processing single contract function Data.prototype.drawingChart = function () {} // Drawing function // Trading conditions function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object var accountStocks = data.accountData.Stocks; // Account balance var boll = data.boll(dataLength, timeCycle); // Calculate the technical indicators of boll data.trade(); // Calculate trading conditions to place an order data.cancelOrders(); // Cancel orders data.drawingChart(boll); // drawing data.isEven(); // Processing of holding individual contract } //Entry function function main() { while (true) { // Enter the polling mode onTick(); // Execute onTick function Sleep(500); // Sleep for 0.5 seconds } }
V. Writing strategy
The strategy framework can be easily set up according to the strategy idea and transaction process. The whole strategy can be simplified into three steps:
Pre-processing before transaction.
Get and calculate data.
Place an order and deal with it later.
Next, we need to fill in the necessary detail code in the strategy framework according to the actual transaction process and transaction details.
Pre-processing before transaction
Step 1: Declare the necessary global variables in the global scope.
//Declare a chart object for the configuration chart var chart = { } //Call Chart function and initialize the chart var ObjChart = Chart ( chart ) //Declare an empty array to store price difference series var bars = [ ] //Declare a record history data timestamp variable var oldTime = 0
Step 2: Configure the external parameters of the strategy.
// parameters var tradeTypeA = "this_week"; // Arbitrage A Contract var tradeTypeB = "quarter"; // Arbitrage B Contract var dataLength = 10; //Indicator period length var timeCycle = 1; // K-line period var name = "ETC"; // Currencies var unit = 1; // Order quantity
Step 3: Define the data processing function
Basic data function: Data()
Create a constructor, Data, and define its internal properties. Including: account data, position data, K-line data timestamp, buy/sell price of arbitrage A/B contract, and positive/negative arbitrage price difference.
// Basic data function Data(tradeTypeA, tradeTypeB) { // Pass in arbitrage A contract and arbitrage B contract this.accountData = _C(exchange.GetAccount); // Get account information this.positionData = _C(exchange.GetPosition); // Get position information var recordsData = _C(exchange.GetRecords); // Get K-line data exchange.SetContractType(tradeTypeA); // Subscription arbitrage A contract var depthDataA = _C(exchange.GetDepth); // Depth data of arbitrage A contract exchange.SetContractType(tradeTypeB); // Subscription arbitrage B contract var depthDataB = _C(exchange.GetDepth); // Depth data of arbitrage B contract this.time = recordsData[recordsData.length - 1].Time; // Time of obtaining the latest data this.askA = depthDataA.Asks[0].Price; // Sell one price of Arbitrage A contract this.bidA = depthDataA.Bids[0].Price; // Buy one price of Arbitrage A contract this.askB = depthDataB.Asks[0].Price; // Sell one price of Arbitrage B contract this.bidB = depthDataB.Bids[0].Price; // Buy one price of Arbitrage B contract // Positive arbitrage price differences (Sell one price of contract A - Buy one price of contract B) this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price; // Negative arbitrage price differences (Buy one price of contract A - Sell one price of contract B) this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price; }
Get the position function: mp ( )
Traverse the entire position array and return the position quantity of the specified contract and direction. If not, return false
// Get positions Data.prototype.mp = function (tradeType, type) { var positionData = this.positionData; // Get position information for (var i = 0; i < positionData.length; i++) { if (positionData[i].ContractType == tradeType) { if (positionData[i].Type == type) { if (positionData[i].Amount > 0) { return positionData[i].Amount; } } } } return false; }
K-line and indicator function: boll()
A new K-line sequence is synthesized according to the positive arbitrage/negative arbitrage price difference data. The data of upper track, middle track and lower track calculated by the boll indicator are returned.
// Synthesis of new K-line data and boll indicator data Data.prototype.boll = function (num, timeCycle) { var self = {}; // Temporary objects // Median value of positive arbitrage price difference and negative arbitrage price difference self.Close = (this.basb + this.sabb) / 2; if (this.timeA == this.timeB) { self.Time = this.time; } // Compare two depth data timestamps if (this.time - oldTime > timeCycle * 60000) { bars.push(self); oldTime = this.time; } // Pass in the price difference data object into the K-line array according to the specified time period if (bars.length > num * 2) { bars.shift(); // Control the length of the K-line array } else { return; } var boll = TA.BOLL(bars, num, 2); // Call the boll indicator in the talib library return { up: boll[0][boll[0].length - 1], // boll indicator upper track middle: boll[1][boll[1].length - 1], // boll indicator middle track down: boll[2][boll[2].length - 1] // boll indicator down track } // Return a processed boll indicator data }
Order function: trade()
Pass in the order contract name and order type, then place the order with consideration, and return the result after placing the order. Since it is necessary to place two orders in different directions at the same time, the buy/sell one price is converted within the function according to the contract name of the order.
// place the order Data.prototype.trade = function (tradeType, type) { exchange.SetContractType(tradeType); // Resubscribe to a contract before placing an order var askPrice, bidPrice; if (tradeType == tradeTypeA) { // If the order is placed in contract A askPrice = this.askA; // set askPrice bidPrice = this.bidA; // set bidPrice } else if (tradeType == tradeTypeB) { // If the order is placed in contract B askPrice = this.askB; // set askPrice bidPrice = this.bidB; // set bidPrice } switch (type) { // Match order placement mode case "buy": exchange.SetDirection(type); // Set order placement mode return exchange.Buy(askPrice, unit); case "sell": exchange.SetDirection(type); // Set order placement mode return exchange.Sell(bidPrice, unit); case "closebuy": exchange.SetDirection(type); // Set order placement mode return exchange.Sell(bidPrice, unit); case "closesell": exchange.SetDirection(type); // Set order placement mode return exchange.Buy(askPrice, unit); default: return false; } }
Cancel Order Function: cancelOrders()
Get an array of all outstanding orders and cancel them one by one. In addition, false is returned if there is an unfilled order, and true is returned if there is no unfilled order.
// Cancel order Data.prototype.cancelOrders = function () { Sleep(500); // Delay before cancellation, because some exchanges, you know what I mean var orders = _C(exchange.GetOrders); // Get an array of unfilled orders if (orders.length > 0) { // If there are unfilled orders for (var i = 0; i < orders.length; i++) { //Iterate through the array of unfilled orders exchange.CancelOrder(orders[i].Id); //Cancel unfilled orders one by one Sleep(500); //Delay 0.5 seconds } return false; // Return false if an unfilled order is cancelled } return true; // Return true if there are no unfilled orders }
Handle holding a single contract: isEven()
In the case of a single leg in the arbitrage transaction, we will simply close all positions. Of course, it can also be changed to the tracking method.
// Handle holding a single contract Data.prototype.isEven = function () { var positionData = this.positionData; // Get position information var type = null; // Switch position direction // If the remaining 2 of the position array length is not equal to 0 or the position array length is not equal to 2 if (positionData.length % 2 != 0 || positionData.length != 2) { for (var i = 0; i < positionData.length; i++) { // Iterate through the position array if (positionData[i].Type == 0) { // If it is a long order type = 10; // Set order parameters } else if (positionData[i].Type == 1) { // If it is a short order type = -10; // Set order parameters } // Close all positions this.trade(positionData[i].ContractType, type, positionData[i].Amount); } } }
Drawing function: drawingChart ( )
Call ObjChart Add () method, draw the necessary market data and indicator data in the chart: upper track, middle track, lower track, positive/negative arbitrage price difference.
// Drawing Data.prototype.drawingChart = function (boll) { var nowTime = new Date().getTime(); ObjChart.add([0, [nowTime, boll.up]]); ObjChart.add([1, [nowTime, boll.middle]]); ObjChart.add([2, [nowTime, boll.down]]); ObjChart.add([3, [nowTime, this.basb]]); ObjChart.add([4, [nowTime, this.sabb]]); ObjChart.update(chart); }
Step 4: In the entry function main(), execute the pre-transaction pre-processing code, which will only run once after the program is started, including:
- SetErrorFilter( ) to filter the unimportant information in the console
- exchange.IO ( https://www.squadhelp.com/name/exchange.io?lp=d ) ( ) to set the digital currency to be traded
- ObjChart reset ( ) to clear the previous chart drawn before starting the program
- LogProfitReset( ) to clear the status bar information before starting the program
//entry function function main() { // Filter the unimportant information in the console SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP"); exchange.IO("currency", name + '_USDT'); //Set the digital currency to be traded ObjChart.reset(); // Clear the previous chart drawn before starting the program LogProfitReset(); // Clear the status bar information before starting the program }
After the above pre-transaction pre-processing is defined, the next step is to enter the polling mode and execute the onTick() function repeatedly. It also sets the sleep time for Sleep () polling, because the API of some digital currency exchanges has built-in access limit for a certain period of time.
//entry function function main() { // Filter the unimportant information in the console SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP"); exchange.IO("currency", name + '_USDT'); //Set the digital currency to be traded ObjChart.reset(); //Clear the previous chart drawn before starting the program LogProfitReset(); //Clear the status bar information before starting the program while (true) { // Enter the polling mode onTick(); // Execute onTick function Sleep(500); // Sleep for 0.5 seconds } }
Get and calculate data
Step 1: Obtain basic data object, account balance, and boll indicator data for use in the trading logic.
// Trading conditions function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object var accountStocks = data.accountData.Stocks; // Account balance var boll = data.boll(dataLength, timeCycle); // Get boll indicator data if (!boll) return; // Return if there is no boll data }
Place an order and handle the follow-up
Step 1: Execute the buying and selling operation according to the above strategic logic. First, judge whether the price and indicator conditions are valid, then judge whether the position conditions are valid, and finally execute the trade () order function
// Trading conditions function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object var accountStocks = data.accountData.Stocks; // Account balance var boll = data.boll(dataLength, timeCycle); // Get boll indicator data if (!boll) return; // Return if there is no boll data // Explanation of the price difference // basb = (Sell one price of contract A - Buy one price of contract B) // sabb = (Buy one price of contract A - Sell one price of contract B) if (data.sabb > boll.middle && data.sabb < boll.up) { // If sabb is higher than the middle track if (data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "closebuy"); // Contract A closes long position } if (data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "closesell"); // Contract B closes short position } } else if (data.basb < boll.middle && data.basb > boll.down) { // If basb is lower than the middle track if (data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "closesell"); // Contract A closes short position } if (data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "closebuy"); // Contract B closes long position } } if (accountStocks * Math.max(data.askA, data.askB) > 1) { // If there is balance in the account if (data.basb < boll.down) { // If basb price difference is lower than the down track if (!data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "buy"); // Contract A opens long position } if (!data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "sell"); // Contract B opens short position } } else if (data.sabb > boll.up) { // If sabb price difference is higher than the upper track if (!data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "sell"); // Contract A opens short position } if (!data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "buy"); // Contract B opens long position } } } }
Step 2: After the order is placed, it is necessary to deal with the abnormal situations such as the unsettled order and the holding of a single contract. And draw the chart.
// Trading conditions function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object var accountStocks = data.accountData.Stocks; // Account balance var boll = data.boll(dataLength, timeCycle); // Get boll indicator data if (!boll) return; // Return if there is no boll data // Explanation of the price difference //basb = (Sell one price of contract A - Buy one price of contract B) // sabb = (Buy one price of contract A - Sell one price of contract B) if (data.sabb > boll.middle && data.sabb < boll.up) { // If sabb is higher than the middle track if (data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "closebuy"); // Contract A closes long position } if (data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "closesell"); // Contract B closes short position } } else if (data.basb < boll.middle && data.basb > boll.down) { // If basb is lower than the middle track if (data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "closesell"); // Contract A closes short position } if (data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "closebuy"); // Contract B closes long position } } if (accountStocks * Math.max(data.askA, data.askB) > 1) { // If there is balance in the account if (data.basb < boll.down) { // If basb price difference is lower than the down track if (!data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "buy"); // Contract A opens long position } if (!data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "sell"); // Contract B opens short position } } else if (data.sabb > boll.up) { // If sabb price difference is higher than the upper track if (!data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "sell"); // Contract A opens short position } if (!data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "buy"); // Contract B opens long position } } } data.cancelOrders(); // cancel orders data.drawingChart(boll); // drawing data.isEven(); // Handle holding individual contracts }
VI. Complete strategy
As above, we have created a simple cross-period arbitrage strategy of digital currency completely through more than 200 lines code. The complete code is as follows:
// Global variable // Declare a chart object for the configuration chart var chart = { __isStock: true, tooltip: { xDateFormat: '%Y-%m-%d %H:%M:%S, %A' }, title: { text: 'transaction profit and loss curve (detailed)' }, rangeSelector: { buttons: [{ type: 'hour', count: 1, text: '1h' }, { type: 'hour', count: 2, text: '3h' }, { type: 'hour', count: 8, text: '8h' }, { type: 'all', text: 'All' }], selected: 0, inputEnabled: false }, xAxis: { type: 'datetime' }, yAxis: { title: { text: 'price difference' }, opposite: false, }, series: [{ name: "upper track", id: "line1,up", data: [] }, { name: "middle track", id: "line2,middle", data: [] }, { name: "down track", id: "line3,down", data: [] }, { name: "basb", id: "line4,basb", data: [] }, { name: "sabb", id: "line5,sabb", data: [] }] }; var ObjChart = Chart(chart); // Drawing object var bars = []; // Storage price difference series var oldTime = 0; // Record historical data timestamp // parameters var tradeTypeA = "this_week"; // Arbitrage A contract var tradeTypeB = "quarter"; // Arbitrage B contract var dataLength = 10; //Indicator period length var timeCycle = 1; // K-line period var name = "ETC"; // Currencies var unit = 1; // Order quantity // basic data function Data(tradeTypeA, tradeTypeB) { // Pass in arbitrage A contract and arbitrage B contract this.accountData = _C(exchange.GetAccount); // Get account information this.positionData = _C(exchange.GetPosition); // Get position information var recordsData = _C(exchange.GetRecords); //Get K-line data exchange.SetContractType(tradeTypeA); // Subscribe to arbitrage A contract var depthDataA = _C(exchange.GetDepth); // Arbitrage A contract depth data exchange.SetContractType(tradeTypeB); // Subscribe to arbitrage B contract var depthDataB = _C(exchange.GetDepth); // Arbitrage B contract depth data this.time = recordsData[recordsData.length - 1].Time; // Time to get the latest data this.askA = depthDataA.Asks[0].Price; // Sell one price of arbitrage A contract this.bidA = depthDataA.Bids[0].Price; // Buy one price of arbitrage A contract this.askB = depthDataB.Asks[0].Price; // Sell one price of arbitrage B contract this.bidB = depthDataB.Bids[0].Price; // Buy one price of arbitrage B contract // Positive arbitrage price difference (Sell one price of contract A - Buy one price of contract B) this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price; // Negative arbitrage price difference (Buy one price of contract A - Sell one price of contract B) this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price; } // Get position Data.prototype.mp = function (tradeType, type) { var positionData = this.positionData; // Get position information for (var i = 0; i < positionData.length; i++) { if (positionData[i].ContractType == tradeType) { if (positionData[i].Type == type) { if (positionData[i].Amount > 0) { return positionData[i].Amount; } } } } return false; } // Synthesis of new K-line data and boll indicator data Data.prototype.boll = function (num, timeCycle) { var self = {}; // Temporary objects // Median value of between positive arbitrage price difference and negative arbitrage price difference self.Close = (this.basb + this.sabb) / 2; if (this.timeA == this.timeB) { self.Time = this.time; } // Compare two depth data timestamps if (this.time - oldTime > timeCycle * 60000) { bars.push(self); oldTime = this.time; } // Pass in the price difference data object into the K-line array according to the specified time period if (bars.length > num * 2) { bars.shift(); // Control the length of the K-line array } else { return; } var boll = TA.BOLL(bars, num, 2); // Call the boll indicator in the talib library return { up: boll[0][boll[0].length - 1], // boll indicator upper track middle: boll[1][boll[1].length - 1], // boll indicator middle track down: boll[2][boll[2].length - 1] // boll indicator down track } // Return a processed boll indicator data } // Place an order Data.prototype.trade = function (tradeType, type) { exchange.SetContractType(tradeType); // Resubscribe to a contract before placing an order var askPrice, bidPrice; if (tradeType == tradeTypeA) { // If the order is placed in contract A askPrice = this.askA; // Set askPrice bidPrice = this.bidA; // Set bidPrice } else if (tradeType == tradeTypeB) { // If the order is placed in contract B askPrice = this.askB; // Set askPrice bidPrice = this.bidB; // Set bidPrice } switch (type) { // Match order placement mode case "buy": exchange.SetDirection(type); // Set order placement mode return exchange.Buy(askPrice, unit); case "sell": exchange.SetDirection(type); // Set order placement mode return exchange.Sell(bidPrice, unit); case "closebuy": exchange.SetDirection(type); // Set order placement mode return exchange.Sell(bidPrice, unit); case "closesell": exchange.SetDirection(type); // Set order placement mode return exchange.Buy(askPrice, unit); default: return false; } } // Cancel orders Data.prototype.cancelOrders = function () { Sleep(500); // Delay before cancellation, because some exchanges, you know what I mean var orders = _C(exchange.GetOrders); //Get an array of unfilled orders if (orders.length > 0) { // If there are unfilled orders for (var i = 0; i < orders.length; i++) { //Iterate through the array of unfilled orders exchange.CancelOrder(orders[i].Id); //Cancel unfilled orders one by one Sleep(500); //Sleep for 0.5 seconds } return false; // Return false if an unfilled order is cancelled } return true; //Return true if there are no unfilled orders } // Handle holding individual contracts Data.prototype.isEven = function () { var positionData = this.positionData; // Get position information var type = null; // Switch position direction // If the remaining 2 of the position array length is not equal to 0 or the position array length is not equal to 2 if (positionData.length % 2 != 0 || positionData.length != 2) { for (var i = 0; i < positionData.length; i++) { // Iterate through the position array if (positionData[i].Type == 0) { // If it is a long order type = 10; // Set order parameters } else if (positionData[i].Type == 1) { // If it is a short order type = -10; // Set order parameters } // Close all positions this.trade(positionData[i].ContractType, type, positionData[i].Amount); } } } // Drawing Data.prototype.drawingChart = function (boll) { var nowTime = new Date().getTime(); ObjChart.add([0, [nowTime, boll.up]]); ObjChart.add([1, [nowTime, boll.middle]]); ObjChart.add([2, [nowTime, boll.down]]); ObjChart.add([3, [nowTime, this.basb]]); ObjChart.add([4, [nowTime, this.sabb]]); ObjChart.update(chart); } // Trading conditions function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object var accountStocks = data.accountData.Stocks; // Account balance var boll = data.boll(dataLength, timeCycle); // Get boll indicator data if (!boll) return; // Return if there is no boll data // Explanation of price difference // basb = (Sell one price of contract A - Buy one price of contract B) // sabb = (Buy one price of contract A - Sell one price of contract B) if (data.sabb > boll.middle && data.sabb < boll.up) { // If sabb is higher than the middle track if (data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "closebuy"); // Contract A closes long position } if (data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "closesell"); // Contract B closes short position } } else if (data.basb < boll.middle && data.basb > boll.down) { // If basb is lower than the middle track if (data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "closesell"); // Contract A closes short position } if (data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "closebuy"); // Contract B closes long position } } if (accountStocks * Math.max(data.askA, data.askB) > 1) { // If there is a balance in the account if (data.basb < boll.down) { // If basb price difference is lower than the down track if (!data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order data.trade(tradeTypeA, "buy"); // Contract A opens long position } if (!data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order data.trade(tradeTypeB, "sell"); // Contract B opens short position } } else if (data.sabb > boll.up) { // If sabb price difference is higher than the upper track if (!data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order data.trade(tradeTypeA, "sell"); // Contract A opens short position } if (!data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order data.trade(tradeTypeB, "buy"); // Contract B opens long position } } } data.cancelOrders(); // Cancel orders data.drawingChart(boll); // Drawing data.isEven(); // Handle holding individual contracts } //Entry function function main() { // Filter unimportant information in the console SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP"); exchange.IO("currency", name + '_USDT'); //Set the digital currency to be traded ObjChart.reset(); //Clear the previous chart drawn before starting the program LogProfitReset(); //Clear the status bar information before starting the program while (true) { // Enter polling mode onTick(); // Execute the onTick function Sleep(500); // Sleep for 0.5 seconds } }
Strategy address:
https://www.fmz.com/strategy/104964
VII. Summary
The strategy in this article is just an example. The real bot is not simple, but you can follow the example and use your own wild imagination It should be reminded that, based on my limited experience, in the current digital currency market, almost all pure futures-futures arbitrage strategies are not worth running, whether it is risk-free triangular arbitrage or cross-market arbitrage.
The reason is that no matter which digital currency exchange futures market, its margin is not legal currency. At pressent, almost all digital currencies have dropped by about 70% since the beginning of the year. That is to say, the strategy is always to earn currencies, but the currency price is decreasing. On the whole, the digital currency market seems to have detached from the blockchain. Like the tulips back then, the price always comes from people's expectations and confidence, and confidence comes from the price…