The resource loading... loading...

The strategy of cross-currency arbitrage based on the Brin Belt

Author: , Created: 2020-02-22 18:54:23, Updated: 2023-10-10 21:11:20

img

A summary

In his 1987 book, The Alchemist, Soros made an important proposition: I believe the market prices are always wrong in the sense that they present a biased view of the future. The market effectiveness hypothesis is only a theoretical hypothesis, in fact market participants are not always rational, and at each point in time, participants cannot fully access and objectively interpret all information, and even if it is the same information, everyone's feedback is not the same.

Second, the strategic principles

Based on the above principles, we also know that in an ineffective futures market, the market is not always influenced by the interest rate contracts between different periods, and their pricing is not always fully effective. So, based on the price of the interest rate contracts for different periods of the same trade mark, if two prices have a large spread, it is possible to buy and sell futures contracts for different periods at the same time, and to carry out cross-term quotes.

For example, suppose that the difference between the price of ETC during the week and the price of ETC during the quarter is around 5 for a long time. If the price difference reaches 7 on a given day, we expect the difference to return to 5 at some point in the future. Then we can sell ETC during the week and buy ETC during the quarter to make up the difference. And vice versa.

Third, strategic logic

This article will teach you how to use inventors' quantitative trading platforms and ETC futures contracts on the OkEX exchange in digital currency trading to demonstrate a simple strategy of leverage that can be used to capture momentary leverage opportunities, capture visible profits at every turn, and hedge potential risks.

Creating a digital currency cross-currency strategyDifficulty: Average

Strategic environment

  • This is the first time that the ETC has traded on ETH since its inception.
  • Price difference data: ETC during the week - ETC quarter (except for the co-integration check)
  • Trading cycle: 5 minutes
  • 头寸匹配:1:1
  • Type of transaction: cross-section of the same variety

Strategic logic

  • If the current account does not hold stock and the price is less than boll down, then the price is overbought.
  • If the current account does not hold stock and the price is higher than the boll, then the short position is open.
  • If the current account holds more ETC on a weekly basis and holds more ETC on a quarterly basis, and the price difference is greater than the boll, then the price difference is flat; i.e. sell ETC on a weekly basis and buy ETC on a quarterly basis.
  • If the current account holds ETC on a weekly basis and holds multiple ETC orders on a quarterly basis, and the price is less than the boll in the middle of the track, then the spot price is the difference; i.e. buy ETC on a weekly basis and sell ETC on a quarterly basis.

Four, write a strategic framework

The above is a simple description of the logic of the digital currency cross-term arbitrage strategy, so how do you implement your ideas in the program?

function Data() {}  // 基础数据函数
Data.prototype.mp = function () {}  // 持仓函数
Data.prototype.boll = function () {}  // 指标函数
Data.prototype.trade = function () {}  // 下单函数
Data.prototype.cancelOrders = function () {}  // 撤单函数
Data.prototype.isEven = function () {}  // 处理单只合约函数
Data.prototype.drawingChart = function () {}  // 画图函数

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB);  // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks;  // 账户余额
    var boll = data.boll(dataLength, timeCycle);  // 计算boll技术指标
    data.trade();  // 计算交易条件下单
    data.cancelOrders();  // 撤单
    data.drawingChart(boll);  // 画图
    data.isEven();  // 处理持有单个合约
}

//入口函数
function main() {
    while (true) {  // 进入轮询模式
        onTick();  // 执行onTick函数
        Sleep(500);  // 休眠0.5秒
    }
}

5 Write a strategy

It is easy to build a strategic framework by comparing strategic ideas and the trading process. The whole strategy can be simplified into three steps:

  • The transaction was pre-processed.
  • It's a great way to get and calculate data.
  • The order is then processed.

Next, we need to fill in the necessary detail code in the strategy framework based on the actual transaction process and transaction details.

Pre-processing of the dealStep 1: Declare the necessary global variables in the global environment.

//声明一个配置图表的 chart 对象
var chart = { }

//调用 Chart 函数,初始化图表
var ObjChart = Chart ( chart )

//声明一个空数组,用来存储价差序列
var bars = [ ]

//声明一个记录历史数据时间戳变量
var oldTime = 0

Step 2: External parameters of the policy configuration.

// 参数
var tradeTypeA = "this_week"; // 套利A合约
var tradeTypeB = "quarter"; // 套利B合约
var dataLength = 10; //指标周期长度
var timeCycle = 1; // K线周期
var name = "ETC"; // 币种
var unit = 1; // 下单量

Step 3: Define the data processing functionBasic data function: Data ()Create a constructor function Data and define its internal properties. These include: account data, holding data, K-line data time frames, bid/ask spread, positive/negative bid/ask spread.

// 基础数据
function Data(tradeTypeA, tradeTypeB) { // 传入套利A合约和套利B合约
    this.accountData = _C(exchange.GetAccount); // 获取账户信息
    this.positionData = _C(exchange.GetPosition); // 获取持仓信息
    var recordsData = _C(exchange.GetRecords); //获取K线数据
    exchange.SetContractType(tradeTypeA); // 订阅套利A合约
    var depthDataA = _C(exchange.GetDepth); // 套利A合约深度数据
    exchange.SetContractType(tradeTypeB); // 订阅套利B合约
    var depthDataB = _C(exchange.GetDepth); // 套利B合约深度数据
    this.time = recordsData[recordsData.length - 1].Time; // 获取最新数据时间
    this.askA = depthDataA.Asks[0].Price; // 套利A合约卖一价
    this.bidA = depthDataA.Bids[0].Price; // 套利A合约买一价
    this.askB = depthDataB.Asks[0].Price; // 套利B合约卖一价
    this.bidB = depthDataB.Bids[0].Price; // 套利B合约买一价
    // 正套价差(合约A卖一价 - 合约B买一价)
    this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price;
    // 反套价差(合约A买一价 - 合约B卖一价)
    this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price;
}

Obtain the holding function:mp ())Goes through the entire holding array, returns the number of holdings for the specified contract, the number of holdings for the specified direction, if none, returns false

// 获取持仓
Data.prototype.mp = function (tradeType, type) {
    var positionData = this.positionData; // 获取持仓信息
    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 ()New K-line sequences are synthesized based on positive/counter-setting price difference data. The uptrend, midtrend, and downtrend data calculated by the Boll indicator are returned.

// 合成新K线数据和boll指标数据
Data.prototype.boll = function (num, timeCycle) {
    var self = {}; // 临时对象
    // 正套价差和反套价差中间值
    self.Close = (this.basb + this.sabb) / 2;
    if (this.timeA == this.timeB) {
        self.Time = this.time;
    } // 对比两个深度数据时间戳
    if (this.time - oldTime > timeCycle * 60000) {
        bars.push(self);
        oldTime = this.time;
    } // 根据指定时间周期,在K线数组里面传入价差数据对象
    if (bars.length > num * 2) {
        bars.shift(); // 控制K线数组长度
    } else {
        return;
    }
    var boll = TA.BOLL(bars, num, 2); // 调用talib库中的boll指标
    return {
        up: boll[0][boll[0].length - 1], // boll指标上轨
        middle: boll[1][boll[1].length - 1], // boll指标中轨
        down: boll[2][boll[2].length - 1] // boll指标下轨
    } // 返回一个处理好的boll指标数据
}

The following function:trade ()Enter the name of the order and the type of the order, and then enter the order at the same price and return the result. Since it is necessary to order two different orders at the same time, the buy/sell price is converted according to the name of the order within the function.

// 下单
Data.prototype.trade = function (tradeType, type) {
    exchange.SetContractType(tradeType); // 下单前先重新订阅合约
    var askPrice, bidPrice;
    if (tradeType == tradeTypeA) { // 如果是A合约下单
        askPrice = this.askA; // 设置askPrice
        bidPrice = this.bidA; // 设置bidPrice
    } else if (tradeType == tradeTypeB) { // 如果是B合约下单
        askPrice = this.askB; // 设置askPrice
        bidPrice = this.bidB; // 设置bidPrice
    }
    switch (type) { // 匹配下单模式
        case "buy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        case "sell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closebuy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closesell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        default:
            return false;
    }
}

Cancel Orders () can be used to cancel orders.Retrieves all outstanding orders and cancels them one by one. Returns false if there are any outstanding orders, and true if there are no outstanding orders.

// 取消订单
Data.prototype.cancelOrders = function () {
    Sleep(500); // 撤单前先延时,因为有些交易所你懂的
    var orders = _C(exchange.GetOrders); // 获取未成交订单数组
    if (orders.length > 0) { // 如果有未成交的订单
        for (var i = 0; i < orders.length; i++) { //遍历未成交订单数组
            exchange.CancelOrder(orders[i].Id); //逐个取消未成交的订单
            Sleep(500); //延时0.5秒
        }
        return false; // 如果取消了未成交的单子就返回false
    }
    return true; //如果没有未成交的订单就返回true
}

Processing with single contracts: isEven ())One-legged trading occurs in the processing of leveraged trades, where all positions are directly dealt with by a simple flattering of the transaction. Of course, it can also be changed to a tracking method.

// 处理持有单个合约
Data.prototype.isEven = function () {
    var positionData = this.positionData; // 获取持仓信息
    var type = null; // 转换持仓方向
    // 如果持仓数组长度余2不等于0或者持仓数组长度不等于2
    if (positionData.length % 2 != 0 || positionData.length != 2) {
        for (var i = 0; i < positionData.length; i++) { // 遍历持仓数组
            if (positionData[i].Type == 0) { // 如果是多单
                type = 10; // 设置下单参数
            } else if (positionData[i].Type == 1) { // 如果是空单
                type = -10; // 设置下单参数
            }
            // 平掉所有仓位
            this.trade(positionData[i].ContractType, type, positionData[i].Amount);
        }
    }
}

DrawingChart ())Use the ObjChart.add () method to plot the necessary market data and indicator data in the chart: up, down, up, down, positive/negative spread.

// 画图
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: Execute transaction preprocessing code inside the main () input function, which runs only once after the program is started.

  • Not important information in the filter panel SetErrorFilter ()
  • Set up the digital currency to tradeexchange.IO ( )
  • Chart drawn before the program starts ObjChart.reset ()
  • Status bar information before the program is started LogProfitReset ()
//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
}

Once the transaction pre-processing is defined, the next step is to enter the routing mode, repeat the onTick () function, and set the sleep time during the routing, since some APIs for digital currency exchanges have built-in access limits for a certain amount of time.

//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
    while (true) { // 进入轮询模式
        onTick(); // 执行onTick函数
        Sleep(500); // 休眠0.5秒
    }
}

Access and calculate dataStep 1: Obtain basic data objects, account balances, and Boll indicator data for use in trading logic.

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
}

Order and follow-upStep 1: Execute the buy/sell operation according to the above strategic logic. First determine whether the price and indicator conditions are satisfied, then determine whether the holding conditions are satisfied, and finally execute the trade () suborder function.

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
}

Step 2: Once the order has been completed, it is necessary to deal with anomalies such as unfinished orders, holding a single contract and drawing a chart.

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
    data.cancelOrders(); // 撤单
    data.drawingChart(boll); // 画图
    data.isEven(); // 处理持有单个合约
}

Six, the full strategy

We have created a simple strategy for a digital currency's long-term leverage, complete with over 200 lines of code.

// 全局变量
// 声明一个配置图表的 chart 对象
var chart = {
    __isStock: true,
    tooltip: {
        xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
    },
    title: {
        text: '交易盈亏曲线图(详细)'
    },
    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: '价差'
        },
        opposite: false,
    },
    series: [{
        name: "上轨",
        id: "线1,up",
        data: []
    }, {
        name: "中轨",
        id: "线2,middle",
        data: []
    }, {
        name: "下轨",
        id: "线3,down",
        data: []
    }, {
        name: "basb",
        id: "线4,basb",
        data: []
    }, {
        name: "sabb",
        id: "线5,sabb",
        data: []
    }]
};
var ObjChart = Chart(chart); // 画图对象
var bars = []; // 存储价差序列
var oldTime = 0; // 记录历史数据时间戳

// 参数
var tradeTypeA = "this_week"; // 套利A合约
var tradeTypeB = "quarter"; // 套利B合约
var dataLength = 10; //指标周期长度
var timeCycle = 1; // K线周期
var name = "ETC"; // 币种
var unit = 1; // 下单量

// 基础数据
function Data(tradeTypeA, tradeTypeB) { // 传入套利A合约和套利B合约
    this.accountData = _C(exchange.GetAccount); // 获取账户信息
    this.positionData = _C(exchange.GetPosition); // 获取持仓信息
    var recordsData = _C(exchange.GetRecords); //获取K线数据
    exchange.SetContractType(tradeTypeA); // 订阅套利A合约
    var depthDataA = _C(exchange.GetDepth); // 套利A合约深度数据
    exchange.SetContractType(tradeTypeB); // 订阅套利B合约
    var depthDataB = _C(exchange.GetDepth); // 套利B合约深度数据
    this.time = recordsData[recordsData.length - 1].Time; // 获取最新数据时间
    this.askA = depthDataA.Asks[0].Price; // 套利A合约卖一价
    this.bidA = depthDataA.Bids[0].Price; // 套利A合约买一价
    this.askB = depthDataB.Asks[0].Price; // 套利B合约卖一价
    this.bidB = depthDataB.Bids[0].Price; // 套利B合约买一价
    // 正套价差(合约A卖一价 - 合约B买一价)
    this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price;
    // 反套价差(合约A买一价 - 合约B卖一价)
    this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price;
}

// 获取持仓
Data.prototype.mp = function (tradeType, type) {
    var positionData = this.positionData; // 获取持仓信息
    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线数据和boll指标数据
Data.prototype.boll = function (num, timeCycle) {
    var self = {}; // 临时对象
    // 正套价差和反套价差中间值
    self.Close = (this.basb + this.sabb) / 2;
    if (this.timeA == this.timeB) {
        self.Time = this.time;
    } // 对比两个深度数据时间戳
    if (this.time - oldTime > timeCycle * 60000) {
        bars.push(self);
        oldTime = this.time;
    } // 根据指定时间周期,在K线数组里面传入价差数据对象
    if (bars.length > num * 2) {
        bars.shift(); // 控制K线数组长度
    } else {
        return;
    }
    var boll = TA.BOLL(bars, num, 2); // 调用talib库中的boll指标
    return {
        up: boll[0][boll[0].length - 1], // boll指标上轨
        middle: boll[1][boll[1].length - 1], // boll指标中轨
        down: boll[2][boll[2].length - 1] // boll指标下轨
    } // 返回一个处理好的boll指标数据
}

// 下单
Data.prototype.trade = function (tradeType, type) {
    exchange.SetContractType(tradeType); // 下单前先重新订阅合约
    var askPrice, bidPrice;
    if (tradeType == tradeTypeA) { // 如果是A合约下单
        askPrice = this.askA; // 设置askPrice
        bidPrice = this.bidA; // 设置bidPrice
    } else if (tradeType == tradeTypeB) { // 如果是B合约下单
        askPrice = this.askB; // 设置askPrice
        bidPrice = this.bidB; // 设置bidPrice
    }
    switch (type) { // 匹配下单模式
        case "buy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        case "sell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closebuy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closesell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        default:
            return false;
    }
}

// 取消订单
Data.prototype.cancelOrders = function () {
    Sleep(500); // 撤单前先延时,因为有些交易所你懂的
    var orders = _C(exchange.GetOrders); // 获取未成交订单数组
    if (orders.length > 0) { // 如果有未成交的订单
        for (var i = 0; i < orders.length; i++) { //遍历未成交订单数组
            exchange.CancelOrder(orders[i].Id); //逐个取消未成交的订单
            Sleep(500); //延时0.5秒
        }
        return false; // 如果取消了未成交的单子就返回false
    }
    return true; //如果没有未成交的订单就返回true
}

// 处理持有单个合约
Data.prototype.isEven = function () {
    var positionData = this.positionData; // 获取持仓信息
    var type = null; // 转换持仓方向
    // 如果持仓数组长度余2不等于0或者持仓数组长度不等于2
    if (positionData.length % 2 != 0 || positionData.length != 2) {
        for (var i = 0; i < positionData.length; i++) { // 遍历持仓数组
            if (positionData[i].Type == 0) { // 如果是多单
                type = 10; // 设置下单参数
            } else if (positionData[i].Type == 1) { // 如果是空单
                type = -10; // 设置下单参数
            }
            // 平掉所有仓位
            this.trade(positionData[i].ContractType, type, positionData[i].Amount);
        }
    }
}

// 画图
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);
}

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
    data.cancelOrders(); // 撤单
    data.drawingChart(boll); // 画图
    data.isEven(); // 处理持有单个合约
}

//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
    while (true) { // 进入轮询模式
        onTick(); // 执行onTick函数
        Sleep(500); // 休眠0.5秒
    }
}

The policy address:https://www.fmz.com/strategy/104964

7 and Summary

This strategy is just a throwing dice, the real thing is not so simple, but you can play with your imagination by following the example. I need to remind you that in my limited experience, the current state of the digital currency market, pure futures arbitrage strategies are basically all not worth running, whether it is a risk-free triangle arbitrage or cross-market arbitrage.

The reason is that the futures market of any digital currency exchange is not backed by fiat currency. Today, almost all digital currencies have fallen by about 70% since the beginning of the year. That is, the strategy is always in the coin, but the price of the coin is falling.


Related

More