avatar of 发明者量化-小小梦 发明者量化-小小梦
집중하다 사신
4
집중하다
1271
수행원

볼린저 밴드를 기반으로 한 암호화폐 기간 간 차익거래 전략

만든 날짜: 2020-02-22 18:54:23, 업데이트 날짜: 2023-10-10 21:11:20
comments   0
hits   4202

볼린저 밴드를 기반으로 한 암호화폐 기간 간 차익거래 전략

I. 요약

소로스는 1987년에 쓴 그의 책 The Alchemy of Finance에서 중요한 명제를 내놓았습니다. 저는 시장 가격이 미래에 대한 편향된 관점을 제시한다는 의미에서 항상 틀렸다고 믿습니다. 효율적 시장 가설은 단지 이론일 뿐입니다. 사실, 시장 참여자는 항상 합리적이지는 않으며, 모든 시점에서 참여자가 모든 정보를 완전히 얻고 객관적으로 해석하는 것은 불가능합니다. 게다가 정보가 같더라도 모든 사람의 피드백은 다릅니다. 다양합니다. 즉, 가격 자체에 이미 시장 참여자들의 잘못된 기대가 포함되어 있으므로, 시장 가격은 본질적으로 항상 잘못된 것입니다. 이는 중재자에게 이익의 원천이 될 수 있습니다.

2. 전략 원칙

위의 원리에 근거하여, 우리는 비효율적인 선물 시장에서는 서로 다른 기간의 인도 계약에 대한 시장 영향이 항상 동기화되지 않으며, 가격 책정이 완전히 효과적이지 않다는 것을 알 수 있습니다. 이후, 서로 다른 기간의 동일 거래 대상의 인도계약 가격을 기준으로, 두 가격 사이에 큰 가격 차이가 있는 경우, 서로 다른 기간의 선물계약을 동시에 매수/매도하여 기간 간 차익거래를 진행할 수 있습니다. 상품 선물과 마찬가지로 디지털 통화에도 여러 기간의 차익거래 계약이 결합되어 있습니다. 예를 들어, OkEX 거래소에는 ETC Weekly, ETC Biweekly, ETC Quarterly가 있습니다.

예를 들어, ETC 주간과 ETC 분기별 가격 차이가 장기간 5 정도로 유지된다고 가정해 보겠습니다. 만약 어느 날 스프레드가 7에 도달한다면, 미래의 어느 시점에서 스프레드가 5로 돌아올 것으로 예상됩니다. 그러면 매주 ETC를 매도하고 분기별로 ETC를 매수해 스프레드를 단축할 수 있습니다. 그 반대도 마찬가지다. 이러한 가격 차이는 존재하지만, 수동 차익거래는 시간이 많이 걸리는 수동 작업, 정확도 저하, 가격 변화의 영향 등으로 인해 많은 불확실성을 수반하는 경우가 많습니다. 정량적 차익거래의 매력은 정량적 모델을 통해 차익거래 기회를 포착하고 차익거래 거래 전략을 수립하는 것과 더불어, 프로그램된 알고리즘을 통해 거래소에 자동으로 거래 주문을 내어, 신속하고 정확하게 기회를 포착하고 효율적이고 안정적으로 수익을 창출하는 데 있습니다.

3. 전략 논리

이 기사에서는 간단한 중재 전략을 사용하여 디지털 통화 거래에서 Inventor Quantitative Trading Platform과 OkEX 거래소의 ETC 선물 계약을 사용하는 방법을 알려드립니다. 이를 통해 즉각적인 중재 기회를 포착하고 헤지하는 동안 수익을 얻을 수 있는 모든 기회를 잡는 방법을 보여줍니다. 발생 가능한 위험.

암호화폐 교차기간 중재 전략 만들기 난이도: 보통

전략적 환경

  • 거래주체 : 이더리움 클래식(ETC)
  • 가격 차이 데이터: ETC 주간 - ETC 분기별(공적분 테스트 생략)
  • 거래주기: 5분
  • 포지션 매칭 : 1:1
  • 거래 유형: 동일 제품 교차 기간

전략 논리

  • 롱 스프레드 개설 조건: 현재 계좌에 포지션이 없고 스프레드가 하단 볼 트랙보다 낮을 경우, 스프레드에 대해 롱 포지션을 취하세요. 즉, 일주일 동안 ETC를 매수하고 분기 동안 ETC를 매도하는 것입니다.
  • 숏 스프레드 포지션을 개설하기 위한 조건: 현재 계좌에 포지션이 없고 스프레드가 볼 상단 트랙보다 큰 경우, 스프레드를 숏하세요. 즉, 일주일 동안 ETC를 매도하고 분기 동안 ETC를 매수하는 것입니다.
  • 롱 스프레드를 마감하기 위한 조건: 현재 계좌가 ETC에 대한 주간 롱 포지션과 분기별 숏 포지션을 보유하고 있으며, 스프레드가 볼 중간 추적보다 큰 경우, 롱 스프레드는 마감됩니다. 즉, 일주일 동안 ETC를 매도하고 분기 동안 ETC를 매수하는 것입니다.
  • 숏 스프레드를 마감하기 위한 조건: 현재 계좌가 이번 주에 ETC에 대한 숏 포지션을 보유하고 있고, 이번 분기에 ETC에 대한 롱 포지션을 보유하고 있으며, 스프레드가 볼 중간 트랙보다 작을 경우, 숏 스프레드는 마감됩니다. 즉, 일주일 동안 ETC를 매수하고 분기 동안 ETC를 매도하는 것입니다.

4. 정책 프레임워크 작성

위의 내용은 디지털 통화 교차 기간 차익 거래 전략의 논리에 대한 간단한 설명입니다. 그러면 프로그램에서 아이디어를 어떻게 구현합니까? 우리는 먼저 Inventor Quantitative Trading Platform에 프레임워크를 구축하려고 했습니다.

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. 전략을 작성하세요

전략적 아이디어와 거래 과정을 비교함으로써 쉽게 전략 프레임워크를 구축할 수 있습니다. 전체 전략은 세 단계로 단순화될 수 있습니다.

  • 거래 전 처리.
  • 데이터를 얻고 계산합니다.
  • 주문을 하고 후속 조치를 처리합니다.

다음으로, 실제 거래 프로세스와 거래 세부 정보를 기반으로 전략 프레임워크에 필요한 세부 코드를 입력해야 합니다.

거래 전 처리 1단계: 글로벌 환경에서 필요한 글로벌 변수를 선언합니다.

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

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

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

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

2단계: 전략의 외부 매개변수를 구성합니다.

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

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-라인 시퀀스를 합성합니다. 그리고 볼 지표에 의해 계산된 상단 레일, 중간 레일, 하단 레일 데이터를 반환합니다.

// 合成新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; // 设置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;
    }
}

주문 취소 기능 : 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或者持仓数组长度不等于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() 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(); //程序启动前清空之前的状态栏信息
}

위에서 설명한 거래 전처리 과정을 정의한 후 다음 단계로 넘어가서 폴링 모드로 들어가서 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단계: 거래 로직에 사용할 기본 데이터 객체, 계좌 잔액, 볼 지표 데이터를 가져옵니다.

// 交易条件
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단계: 위의 전략 논리에 따라 매수 및 매도 작업을 실행합니다. 먼저 가격 및 지표 조건이 만족되는지 판단하고, 그 다음 포지션 조건이 만족되는지 판단하여 마지막으로 거래( ) 주문 함수를 실행합니다.

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

6. 완전한 전략

위에서 우리는 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; // 设置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秒
    }
}

정책 주소: https://www.fmz.com/strategy/104964

VII. 결론

이 전략은 단지 시작점일 뿐입니다. 실제 거래는 그렇게 간단하지 않지만, 예시를 사용하여 상상력을 최대한 발휘할 수 있습니다. 제한된 경험에 근거해 말씀드리자면, 현재 암호화폐 시장 상황을 고려할 때, 위험 없는 삼각 차익거래든 시장 간 차익거래든 순수한 기간별 차익거래 전략은 기본적으로 실행할 가치가 없습니다.

그 이유는 어떤 디지털 화폐 거래소의 선물 시장에서든 증거금은 합법적인 통화가 아니기 때문입니다. 요즘에는 거의 모든 디지털 통화가 올해 초 이래로 약 70% 하락했습니다. 즉, 이 전략은 항상 돈을 버는 것이지만, 화폐의 가격은 떨어지고 있습니다. 주변을 둘러보면 디지털 화폐 시장은 블록체인에서 분리된 듯합니다. 그 당시 튤립처럼 가격은 항상 사람들의 기대와 자신감에서 나오고, 자신감은 가격에서 나옵니다…