
이전 기사에서 우리는 간단한 스팟 복사 트레이딩 로봇을 구현했습니다. 오늘은 간단한 복사 트레이딩 로봇의 계약 버전을 구현할 것입니다.
복사 트레이딩 로봇의 계약 버전과 현물 버전 사이에는 큰 차이가 있습니다. 현물 복사 트레이딩은 주로 계정 자산의 변화를 모니터링하여 달성할 수 있습니다. 선물 버전에서는 계좌 포지션의 변화를 모니터링해야 합니다. 따라서 선물 버전의 상황은 더 복잡합니다. 선물에는 롱 포지션, 숏 포지션, 그리고 다양한 계약이 있기 때문입니다. 이런 일련의 세부 사항을 처리해야 합니다. 핵심 아이디어는 입장의 변화를 모니터링하는 것입니다. 위치의 변화에 따라 복사 작업을 트리거합니다. 처음 설계되었을 당시에는 롱 포지션과 숏 포지션을 함께 처리할 계획이었지만, 이렇게 하면 매우 복잡해진다는 것을 알게 되었습니다. 문제를 분석한 후 롱 포지션과 숏 포지션을 별도로 처리하기로 결정했습니다.
전략 매개변수:

백테스팅이 지원되며, 기본 설정을 사용하여 관찰 결과를 백테스팅할 수 있습니다.
전략 소스 코드:
/*backtest
start: 2021-03-18 00:00:00
end: 2021-04-07 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"},{"eid":"Futures_OKCoin","currency":"BTC_USD"},{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
*/
function test() {
// 测试函数
var ts = new Date().getTime()
if (ts % (1000 * 60 * 60 * 6) > 1000 * 60 * 60 * 5.5) {
Sleep(1000 * 60 * 10)
var nowPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
var longPosAmount = nowPosAmount.long
var shortPosAmount = nowPosAmount.short
var x = Math.random()
if (x > 0.7) {
exchange.SetDirection("buy")
exchange.Buy(-1, _N(Math.max(1, x * 10), 0), "参考账户测试开单#FF0000")
} else if(x < 0.2) {
exchange.SetDirection("sell")
exchange.Sell(-1, _N(Math.max(1, x * 10), 0), "参考账户测试开单#FF0000")
} else if(x >= 0.2 && x <= 0.5 && longPosAmount > 4) {
exchange.SetDirection("closebuy")
exchange.Sell(-1, longPosAmount, "参考账户测试平仓#FF0000")
} else if(shortPosAmount > 4) {
exchange.SetDirection("closesell")
exchange.Buy(-1, _N(shortPosAmount / 2, 0), "参考账户测试平仓#FF0000")
}
}
}
function getPosAmount(pos, ct) {
var longPosAmount = 0
var shortPosAmount = 0
_.each(pos, function(ele) {
if (ele.ContractType == ct && ele.Type == PD_LONG) {
longPosAmount = ele.Amount
} else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
shortPosAmount = ele.Amount
}
})
return {long: longPosAmount, short: shortPosAmount}
}
function trade(e, ct, type, delta) {
var nowPosAmount = getPosAmount(_C(e.GetPosition), ct)
var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
if (delta > 0) {
// 开仓
var tradeFunc = type == PD_LONG ? e.Buy : e.Sell
e.SetDirection(type == PD_LONG ? "buy" : "sell")
tradeFunc(-1, delta)
} else if (delta < 0) {
// 平仓
var tradeFunc = type == PD_LONG ? e.Sell : e.Buy
e.SetDirection(type == PD_LONG ? "closebuy" : "closesell")
if (nowAmount <= 0) {
Log("未检测到持仓")
return
}
tradeFunc(-1, Math.min(nowAmount, Math.abs(delta)))
} else {
throw "错误"
}
}
function main() {
LogReset(1)
if (exchanges.length < 2) {
throw "没有跟单的交易所"
}
var exName = exchange.GetName()
// 检测参考交易所
if (!exName.includes("Futures_")) {
throw "仅支持期货跟单"
}
Log("开始监控", exName, "交易所", "#FF0000")
// 检测跟单交易所
for (var i = 1 ; i < exchanges.length ; i++) {
if (exchanges[i].GetName() != exName) {
throw "跟单的期货交易所和参考交易所不同!"
}
}
// 设置交易对、合约
_.each(exchanges, function(e) {
if (!IsVirtual()) {
e.SetCurrency(refCurrency)
if (isSimulate) {
if (e.GetName() == "Futures_OKCoin") {
e.IO("simulate", true)
}
}
}
e.SetContractType(refCt)
})
var initRefPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
while(true) {
if (IsVirtual()) { // 回测时才模拟
test() // 测试函数,模拟参考账户主动交易,触发跟单账户跟单
}
Sleep(5000)
var nowRefPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
var tbl = {
type : "table",
title : "持仓",
cols : ["名称", "标签", "多仓", "空仓", "账户资产(Stocks)", "账户资产(Balance)"],
rows : []
}
_.each(exchanges, function(e) {
var pos = getPosAmount(_C(e.GetPosition), refCt)
var acc = _C(e.GetAccount)
tbl.rows.push([e.GetName(), e.GetLabel(), pos.long, pos.short, acc.Stocks, acc.Balance])
})
LogStatus(_D(), "\n`" + JSON.stringify(tbl) + "`")
// 计算仓位变动量
var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short
// 检测变动
if (longPosDelta == 0 && shortPosDelta == 0) {
continue
} else {
// 检测到仓位变动
for (var i = 1 ; i < exchanges.length ; i++) {
// 执行多头动作
if (longPosDelta != 0) {
Log(exchanges[i].GetName(), exchanges[i].GetLabel(), "执行多头跟单,变动量:", longPosDelta)
trade(exchanges[i], refCt, PD_LONG, longPosDelta)
}
// 执行空头动作
if (shortPosDelta != 0) {
Log(exchanges[i].GetName(), exchanges[i].GetLabel(), "执行空头跟单,变动量:", shortPosDelta)
trade(exchanges[i], refCt, PD_SHORT, shortPosDelta)
}
}
}
// 执行跟单操作后,更新
initRefPosAmount = nowRefPosAmount
}
}
OKEX는 V5 인터페이스를 업데이트한 이후 OKEX 시뮬레이션 디스크를 사용할 수 있으므로 편리한 테스트를 위해 두 개의 OKEX 시뮬레이션 디스크 API 키를 사용했습니다.
첫 번째로 추가된 교환 객체는 참조 교환이고, 복사 교환은 이 교환 계정을 따라 작동합니다. OKEX 시뮬레이션 거래 페이지에서 거래소 계좌를 기준으로 3개의 ETH 분기별 코인 기반 계약을 수동으로 배치합니다.

실제 시장에서는 기준 거래소 계좌 포지션의 변화를 감지하고 그에 따른 작업이 이루어지는 것을 볼 수 있습니다.

방금 연 두 개의 계약 포지션을 닫아보도록 합시다. 닫은 후의 포지션은 그림과 같습니다.

실시간 작업을 따르고 2개의 계약을 성사시키세요.

이 전략은 최적화 없이 간단하고 이해하기 쉬운 방식으로 설계되었습니다. 개선은 여전히 주문을 따를 때 자산 탐지와 같은 세부 사항을 처리해야 합니다. 설계의 단순성을 위해 주문을 따를 때 시장 주문을 사용합니다. . 이 전략은 학습 아이디어만 제공하고, 실제 거래는 필요에 따라 최적화됩니다.
전략 주소: https://www.fmz.com/strategy/270012
메시지를 남겨주세요.