NO.1

索罗斯在1987年撰写的《金融炼金术》 一书中,曾经提出过一个重要的命题:I believe the market prices are always wrong in the sense that they present a biased view of the future.

市场有效假说只是理论上的假设,实际上市场参与者并不总是理性的,并且在每一个时间点上,参与者不可能完全获取和客观解读所有的信息,再者就算是同样的信息,每个人的反馈都不尽相同。

也就是说,价格本身就已经包含了市场参与者的错误预期,所以本质上市场价格总错误的。这或许是套利者的利润来源。

NO.2

根据上述原理,我们也就知道,在一个非有效的期货市场中,不同时期交割合约之间受到市场影响也并不总是同步,其定价也并非完全有效的原因。

那么,根据同一种交易标的的不同时期交割合约价格为基础,如果两个价格出现了较大的价差幅度,就可以同时买卖不同时期的期货合约,进行跨期套利。

与商品期货一样,数字货币也有与之相关的跨期套利合约组合。如在 OkEX 交易所中就有:ETC 当周、ETC 次周、ETC 季度。

举个例子,假设 ETC 当周和 ETC 季度的价差长期维持在 5 左右。如果某一天价差达到 7,我们预计价差会在未来某段时间回归到 5。那么就可以卖出 ETC 当周,同时买入 ETC 季度,来做空这个价差。反之亦然。

NO.3

尽管这种价差是存在的,但是人工操作耗时、准确性差以及价格变化的影响,人工套利往往存在诸多不确定性。

通过量化模型捕捉套利机会并制定套利交易策略,以及程序化算法自动向交易所下达交易订单,快速准确捕捉机会,高效稳定赚取收益,这就是量化套利的魅力所在。

本篇将会教大家如何在数字货币交易中,利用发明者量化交易平台和 OkEX 交易所中 ETC 期货合约,以一个简单的套利策略,来演示如果捕捉瞬时的套利机会,把握住每一次可以看得到的利润,同时对冲有可能遇到的风险。

NO.4

创建一个数字货币跨期套利策略

难易度:普通级

策略环境

  • 交易标的:以太经典(ETC)
  • 价差数据:ETC 当周 - ETC 季度(省略协整性检验)
  • 交易周期:5 分钟
  • 头寸匹配:1:1
  • 交易类型:同品种跨期

策略逻辑

  • 做多价差开仓条件:如果当前账户没有持仓,并且价差小于 boll 下轨,就做多价差。即:买开 ETC 当周,卖开 ETC 季度。
  • 做空价差开仓条件:如果当前账户没有持仓,并且价差大于 boll 上轨,就做空价差。即:卖开 ETC 当周,买开 ETC 季度。
  • 做多价差平仓条件:如果当前账户持有 ETC 当周多单,并且持有 ETC 季度空单,并且价差大于 boll 中轨,就平多价差。即:卖平 ETC 当周,买平 ETC 季度。
  • 做空价差平仓条件:如果当前账户持有 ETC 当周空单,并且持有 ETC 季度多单,并且价差小于 boll 中轨,就平空价差。即:买平 ETC 当周,卖平 ETC 季度。

NO.5

上面是一个简单的数字货币跨期套利策略逻辑描述,那么如何在程序中实现自己的想法呢?我们试着在发明者量化交易平台先把框架搭建起来。

策略框架:

/*
全局变量
*/// 基础数据
function Data(tradeTypeA, tradeTypeB) {}// 获取持仓
Data.prototype.mp = function (tradeType, type) {}// 合成新K线数据和boll指标数据
Data.prototype.boll = function (num, timeCycle) {}// 下单
Data.prototype.trade = function (tradeType, type) {}// 取消订单
Data.prototype.cancelOrders = function () {}// 处理持有单个合约
Data.prototype.isEven = function () {}// 画图
Data.prototype.drawingChart = function (boll) {}// 交易条件
function onTick() {var data; // 获取基础数据对象data.accountData.Stocks; // 获取账户余额data.boll; // 获取boll指标数据data.mp; // 获取持仓状态data.cancelOrders(); // 撤单data.drawingChart(boll); // 画图data.isEven(); // 处理持有单个合约
}//入口函数
function main() {/* 交易策略预处理*/while (true) { // 进入轮询模式onTick(); // 执行onTick函数Sleep(500); // 休眠0.5秒}
}

对照着策略思路以及交易流程,可以很轻松把策略框架搭建起来。整个策略可以简化为三个步骤:

  1. 交易前预处理。
  2. 获取并计算数据。获取并计算数据。
  3. 下单并对后续处理。下单并对后续处理。

NO.6

接下来,我们就需要根据实际交易流程和交易细节,在策略框架里面填充必要的细节代码。

一、交易前预处理

第1步:在全局环境中,声明必要的全局变量。

  • 声明一个配置图表的 chart 对象
    var chart = { }
  • 调用 Chart 函数,初始化图表
    var ObjChart = Chart ( chart )
  • 声明一个空数组,用来存储价差序列
    var bars = [ ]
  • 声明一个记录历史数据时间戳变量
    var oldTime = 0

    第2步:配置策略的外部参数。

第3步:定义数据处理函数

基础数据函数:Data ( )

创建一个构造函数 Data,并定义它的内部属性。包括:账户数据、持仓数据、K线数据时间戳、套利A/B合约的买/卖一价、正/反套价差。

// 基础数据
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;
}

获取持仓函数:mp ( )

遍历整个持仓数组,返回指定合约、指定方向的持仓数量,如果没有就返回 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线和指标函数:boll ( )

根据正/反套价差数据,合成新的K线序列。并返回由boll指标计算的上轨、中轨、下轨数据。

// 合成新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指标数据
}

下单函数:trade ( )

传入下单合约名称和下单类型,然后以对价下单,并返回下单后的结果。由于需要同时下两个不同方向的单子,所以在函数内部根据下单合约名称对买/卖一价做了转换。

// 下单
Data.prototype.trade = function (tradeType, type) {exchange.SetContractType(tradeType); // 下单前先重新订阅合约var askPrice, bidPrice;if (tradeType == tradeTypeA) { // 如果是A合约下单askPrice = this.askA; // 设置askPricebidPrice = this.bidA; // 设置bidPrice} else if (tradeType == tradeTypeB) { // 如果是B合约下单askPrice = this.askB; // 设置askPricebidPrice = 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;}
}

取消订单函数:cancelOrders ( )

获取所有未成交订单数组,并逐个取消。并且如果有未成交的订单就返回false,如果没有未成交的订单就返回true。

// 取消订单
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
}

处理持有单个合约:isEven ( )

在处理套利交易中出现单腿情况,这里直接用简单的平掉所有仓位处理。当然,也可以改为追单方式。

// 处理持有单个合约
Data.prototype.isEven = function () {var positionData = this.positionData; // 获取持仓信息var type = null; // 转换持仓方向// 如果持仓数组长度余2不等于0或者持仓数组长度不等于2if (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 ( )

调用 ObjChart.add ( ) 方法,在图表中画出必要的行情数据和指标数据:上轨、中轨、下轨、正/反套价差。

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

第4步:在入口函数 main ( ) 里面,执行交易前预处理代码,这些代码在程序启动后,只运行一次。包括:

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

NO.7

定义完上述的交易前预处理,紧接着就要进入下一个步骤,进入轮询模式,重复执行 onTick ( ) 函数。

并设置 Sleep ( ) 轮询时的休眠时间,因为部分数字货币交易所的 API 对一定时间内内置了访问次数限制。

//入口函数
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秒}
}

二、获取并计算数据

第1步:获取基础数据对象、账户余额、boll 指标数据,以供交易逻辑使用。

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

三、下单并对后续处理

第1步:根据上述的策略逻辑,执行买卖操作。首先会判断价格和指标条件是否成立,然后再判断持仓条件是否成立,最后执行 trade ( ) 下单函数

// 交易条件
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开多}}}
}

第2步:下单完成后,需要对未成交的订单、持有单个合约等非正常情况做处理。以及绘制图表。

// 交易条件
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(); // 处理持有单个合约
}

NO.8

以上,我们通过 200 多行,就把一个简单的数字货币跨期套利策略完完整整的创建出来。完整的代码如下:

// 全局变量
// 声明一个配置图表的 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; // 设置askPricebidPrice = this.bidA; // 设置bidPrice} else if (tradeType == tradeTypeB) { // 如果是B合约下单askPrice = this.askB; // 设置askPricebidPrice = 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或者持仓数组长度不等于2if (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秒}
}

NO.9

本篇策略只是一个抛砖引玉,真实的实盘可不是这么简单,不过你可以照着例子发挥自己天马行空的想象。

需要提醒大家的是,以我有限的经验来看,目前的数字货币市场状况,纯粹的期期套利策略基本上全部不值得跑,不论是无风险的三角套利还是跨市场套利。

原因就在于,无论哪个数字货币交易所的期货市场,其保证金不是法币。现如今几乎所有的数字货币从今年初至今已经下跌了70%左右。也就是说策略始终都是在赚币,但是币价是下跌的。

放眼望去,数字货币市场俨然已经脱离了区块链,就像当年的郁金香一样,价格始终来自于人们的预期和信心,信心又来源于价格…

原文链接:https://quant.la/Article/View/816

区块链量化投资系列课程(3)相关推荐

  1. 区块链量化投资系列课程(4) - 动态平衡策略

    阅读原文:区块链量化投资系列课程(4) - 动态平衡策略 前言 沃伦 · 巴菲特的导师本杰明 · 格雷厄姆曾经在<聪明的投资者>一书中,曾经提到过一种股票债券动态平衡的交易模式. 这种交易 ...

  2. 从零构建区块链量化交易平台课程总结-思维模型和方法论提炼

    一.前言 零零散散大概花了半年时间终于把自己这三年的区块链量化交易实践总结成了一门课程,总共十一章.61节课,包含了Java后端.Web前端.APP移动端的开发过程和整套项目源码.从一开始的软件安装. ...

  3. 腾讯区块链发行多少腾讯区块链如何投资?

    腾讯区块链发行多少腾讯区块链如何投资? 虽然比特币一直都是毁誉参半,但支撑其发展的底层技术区块链却日益得到关注.2018开年,自从2017年底就有不少企业推出了自己的区块链产品,到目前为止,市场上已经 ...

  4. 李嘉诚的区块链应用投资哲学

    李嘉诚终于退了. 昨天中午12点,这位纵横商海的超人最后一次主持股东会.从12岁开始工作,到现在90岁,他已经整整工作78年. 尽管已经到了鲐背之年,但李嘉诚对于新事物的接受度超过许多同龄人.比如,巴 ...

  5. BTA分论坛现场直击|区块链与投资,不是“钱”那么简单!

    [CSDN现场报道]2018 年 3 月 30 - 31 日,由 CSDN.火星财经主办,中关村区块链产业联盟.柏链道捷.区块链大本营.TokenSky 联合主办的 2018 区块链技术及应用峰会(B ...

  6. WiBS区块链女性领导力系列研讨会分论坛圆满成功,打破性别偏见,重塑女性价值观

    活动时间:2020年8月16日 2020年8月16日,WiBS(Women in Blockchain-BSV)区块链女性领导力系列研讨会分论坛于北京正式举办.本次分论坛主题为<女性在职场与创新 ...

  7. 区块链DAPP开发入门课程(PHP版)

    1.前言及环境部署 1.1 课程说明 为什么有这个课程? 入门区块链开发已有些时日,走过的坑实在是太多了,对于像我这种从事互联网软件开发多事的老兵来说,上手都有些吃力,可想而知对于新手来说就更难了,尤 ...

  8. 浙大下半年将首开“区块链与数字货币”课程

    点击上方蓝色字体,关注我们 这段时间,百万年薪招区块链人才的新闻屡见不鲜,不仅国内奇缺,美国.日本等发达国家也同样紧缺. 现在,终于有国内院校出手了.浙江大学计算机学院和软件学院都将于今年下半年新开区 ...

  9. Comunion 区块链深度学习系列|区块链的发展历程

    打开一切科学的钥匙都毫无异议地是问号,我们大部分的伟大发现都应当归功于如何?而生活的智慧大概就在于逢事都问个为什么? -- 巴尔扎克 之前写过一篇区块链新手学习要有系统性的文章,也有读者获取到了学习视 ...

最新文章

  1. 分布式消息队列 — Overview
  2. 对称矩阵、Hermite矩阵、正交矩阵、酉矩阵、奇异矩阵、正规矩阵、幂等矩阵
  3. 内核线程、轻量级进程、用户线程
  4. linux如何找到桌面,我怎样才能找到我正在使用的桌面环境?
  5. GDCM:gdcm::XMLDictReader的测试程序
  6. FATE HDU - 2159(二维完全背包)
  7. 漫步凸分析六——凸集的相对内点
  8. 深度学习-通用模型调试技巧
  9. android write file,Android Study Day 3 --Android File Read And Write
  10. Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=
  11. 心语收集12:我以为要是唱的用心良苦,你就会对我多点在乎
  12. autobench 快速入门
  13. 分治法(divide conquer)与动态规划(dynamic programming)应用举例
  14. 搞深度学习如何找到需要的代码
  15. 集线器、交换机、路由器
  16. android 七彩进度条背景图片,Android编程实现自定义ProgressBar样式示例(背景色及一级、二级进度条颜色)...
  17. 华为EC1261使用小记
  18. 降噪效果好的蓝牙耳机该怎么选?盘点四款高品质降噪蓝牙耳机
  19. 计算机对无人机的影响,无人机中的人工智能和自主性你了解多少
  20. 功能性和非功能性需求 UP中FURPS+模型需求分类方式

热门文章

  1. 3dmax2010软件下载3dmax2010安装教程
  2. python 散点图点击链接图片_Python散点图。 标记的大小和样式
  3. vi编辑器如何设置行号
  4. 2023辽宁大学新闻与传播硕士(MJC)专业考研成功经验分享
  5. 项目管理之 项目阶段划分
  6. 【莫烦】python基础教程
  7. 对一个8位(一字节)数的倒序处理
  8. python的IDLE打不开如何解决
  9. amazon mechanical turk介绍
  10. 【c++并发编程】pthread_create的使用