
この戦略のインスピレーションは、Zhihu の著者「Dream Dealer」による機会投稿「TRUMP と MELANIA の低リスク相関アービトラージ モデル」から得られました。この記事では、BN で開始された 2 つの契約 (TRUMP と MELANIA) 間の価格相関を調査し、2 つの契約間の微妙な時間遅延を利用して短期的な市場変動を捉え、低リスクの裁定取引を実現しようとします。次に、この戦略の原則、コード実装ロジックを説明し、可能な最適化の方向性を探ります。
事前に必要知らせ問題は、この戦略が手動の取引ジョブと同等であることです。 2 つの適切な取引ペアを見つけた後にのみ、一定の利益機会があり、取引ペアの利益寿命は短い可能性があります。 利益機会がないことが判明した場合、利益の減少や損失を防ぐために、戦略を適時に停止する必要があります。
TRUMP 契約と MELANIA 契約は両方とも同じ発行チームによって発行され、同じ管理ファンドを持っているため、ほとんどの場合、価格動向は高度に同期しています。ただし、契約設計や市場執行などの要因により、MELANIA の価格は TRUMP の価格より 1 ~ 2 秒遅れる傾向があります。このわずかな遅延により、裁定取引業者は価格差を捉え、高頻度のコピー取引を行う機会を得ることができます。簡単に言えば、トランプ氏が急激に変動すると、メラニア氏もすぐにそれに追随する傾向がある。この遅れをうまく利用することで、より低いリスクで取引を完了できる。


同様の相関現象は暗号通貨市場では珍しいことではありません。
この相関関係により、高頻度取引業者や裁定取引業者は安定した取引シグナルと低リスクの運用機会を得ることができますが、微妙な市場の変化に非常に敏感で、リアルタイムで対応できる取引戦略も必要になります。
コードは主に複数の部分で構成されており、各モジュールはアービトラージ戦略の主要なステップに対応しています。
function GetPosition(pair){
let pos = exchange.GetPosition(pair)
if(pos.length == 0){
return {amount:0, price:0, profit:0}
}else if(pos.length > 1){
throw '不支持双向持仓'
}else if(pos.length == 1){
return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
}else{
Log('未获取仓位数据')
return null
}
}
function InitAccount(){
let account = _C(exchange.GetAccount)
let total_eq = account.Equity
let init_eq = 0
if(!_G('init_eq')){
init_eq = total_eq
_G('init_eq', total_eq)
}else{
init_eq = _G('init_eq')
}
return init_eq
}
function CancelPendingOrders() {
orders = exchange.GetOrders(); // 获取订单
for (let order of orders) {
if (order.Status == ORDER_STATE_PENDING) { // 只取消未完成的订单
exchange.CancelOrder(order.Id); // 取消挂单
}
}
}
メイン関数は無限ループを使用して、以下のステップを継続的に実行します。
データ収集と市場計算
各サイクルはexchange.GetRecords Pair_A と Pair_B の市場データをそれぞれ取得します。
開業条件を決定し、注文する
現在のポジションがない場合 (position_B.amount == 0) かつ取引が許可されている場合 (afterTrade==1):
ストッププロフィットとストップロスのロジック
ポジションが確立されると、戦略はポジションの方向に応じて対応する利益確定注文と損切り注文を設定します。
ポジションをクローズした後の利益統計とログ記録
各ポジションがクローズされると、システムは口座残高の変化を取得し、利益数、損失数、累積損益額をカウントします。
同時に、表やグラフを使用して、現在の位置情報、トランザクション統計、サイクル遅延をリアルタイムで表示し、その後の戦略効果分析に便利です。
この戦略は、相関性の高い 2 つの契約間の微妙な遅延を利用しますが、改善できる領域はまだ数多くあります。
この記事では、短期遅れ契約相関アービトラージ戦略の基本原理と実装コードを詳しく紹介します。価格の上昇と下落の差を利用してエントリーの機会を捉えることから、ポジション管理のためのストッププロフィットとストップロスの設定まで、この戦略は暗号市場における資産間の高い相関関係を最大限に活用します。同時に、リアルタイム アプリケーションにおける戦略の安定性と収益性をさらに向上させるために、動的パラメータ調整、信号フィルタリング、システムの堅牢性、コードの最適化など、いくつかの最適化提案も行いました。
この戦略は独創的で実装も簡単ですが、高頻度かつ変動の激しい暗号通貨市場では、裁定取引は慎重に行う必要があります。この記事が、定量取引や裁定取引戦略に熱心な友人にとって貴重な参考資料やインスピレーションとなることを願っています。
注: 戦略テスト環境はOKXシミュレーション取引であり、具体的な詳細は取引所ごとに変更可能です。
function GetPosition(pair){
let pos = exchange.GetPosition(pair)
if(pos.length == 0){
return {amount:0, price:0, profit:0}
}else if(pos.length > 1){
throw '不支持双向持仓'
}else if(pos.length == 1){
return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
}else{
Log('未获取仓位数据')
return null
}
}
function InitAccount(){
let account = _C(exchange.GetAccount)
let total_eq = account.Equity
let init_eq = 0
if(!_G('init_eq')){
init_eq = total_eq
_G('init_eq', total_eq)
}else{
init_eq = _G('init_eq')
}
return init_eq
}
function CancelPendingOrders() {
orders = exchange.GetOrders(); // 获取订单
for (let order of orders) {
if (order.Status == ORDER_STATE_PENDING) { // 只取消未完成的订单
exchange.CancelOrder(order.Id); // 取消挂单
}
}
}
var pair_a = Pair_A + "_USDT.swap";
var pair_b = Pair_B + "_USDT.swap";
function main() {
exchange.IO('simulate', true);
LogReset(0);
Log('策略开始运行')
var precision = exchange.GetMarkets();
var ratio = 0
var takeProfitOrderId = null;
var stopLossOrderId = null;
var successCount = 0;
var lossCount = 0;
var winMoney = 0;
var failMoney = 0;
var afterTrade = 1;
var initEq = InitAccount();
var curEq = initEq
var pricePrecision = precision[pair_b].PricePrecision;
while (true) {
try{
let startLoopTime = Date.now();
let position_B = GetPosition(pair_b);
let new_r_pairB = exchange.GetRecords(pair_b, 1).slice(-1)[0];
if (!new_r_pairB || !position_B) {
Log('跳过当前循环');
continue;
}
// 合并交易条件:检查是否可以开仓并进行交易
if (afterTrade == 1 && position_B.amount == 0) {
let new_r_pairA = exchange.GetRecords(pair_a, 1).slice(-1)[0];
if (!new_r_pairA ) {
Log('跳过当前循环');
continue;
}
ratio = (new_r_pairA.Close - new_r_pairA.Open) / new_r_pairA.Open - (new_r_pairB.Close - new_r_pairB.Open) / new_r_pairB.Open;
if (ratio > diffLevel) {
CancelPendingOrders();
Log('实时ratio:', ratio, '买入:', pair_b, position_B.amount);
exchange.CreateOrder(pair_b, "buy", -1, Trade_Number);
afterTrade = 0;
} else if (ratio < -diffLevel) {
CancelPendingOrders();
Log('实时ratio:', ratio, '卖出:', pair_b, position_B.amount);
exchange.CreateOrder(pair_b, "sell", -1, Trade_Number);
afterTrade = 0;
}
}
// 判断止盈止损
if (position_B.amount > 0 && takeProfitOrderId == null && stopLossOrderId == null && afterTrade == 0) {
Log('多仓持仓价格:', position_B.price, '止盈价格:', position_B.price * (1 + stopProfitLevel), '止损价格:', position_B.price * (1 - stopLossLevel));
takeProfitOrderId = exchange.CreateOrder(pair_b, "closebuy", position_B.price * (1 + stopProfitLevel), position_B.amount);
Log('止盈订单:', takeProfitOrderId);
}
if (position_B.amount > 0 && takeProfitOrderId != null && stopLossOrderId == null && new_r_pairB.Close < position_B.price * (1 - stopLossLevel) && afterTrade == 0) {
CancelPendingOrders();
takeProfitOrderId = null
Log('多仓止损');
stopLossOrderId = exchange.CreateOrder(pair_b, "closebuy", -1, position_B.amount);
Log('多仓止损订单:', stopLossOrderId);
}
if (position_B.amount < 0 && takeProfitOrderId == null && stopLossOrderId == null && afterTrade == 0) {
Log('空仓持仓价格:', position_B.price, '止盈价格:', position_B.price * (1 - stopProfitLevel), '止损价格:', position_B.price * (1 + stopLossLevel));
takeProfitOrderId = exchange.CreateOrder(pair_b, "closesell", position_B.price * (1 - stopProfitLevel), -position_B.amount);
Log('止盈订单:', takeProfitOrderId, '当前价格:', new_r_pairB.Close );
}
if (position_B.amount < 0 && takeProfitOrderId != null && stopLossOrderId == null && new_r_pairB.Close > position_B.price * (1 + stopLossLevel) && afterTrade == 0) {
CancelPendingOrders();
takeProfitOrderId = null
Log('空仓止损');
stopLossOrderId = exchange.CreateOrder(pair_b, "closesell", -1, -position_B.amount);
Log('空仓止损订单:', stopLossOrderId);
}
// 平市价单未完成
if (takeProfitOrderId == null && stopLossOrderId != null && afterTrade == 0) {
let stoplosspos = GetPosition(pair_b)
if(stoplosspos.amount > 0){
Log('平多仓市价单未完成')
exchange.CreateOrder(pair_b, 'closebuy', -1, stoplosspos.amount)
}
if(stoplosspos.amount < 0){
Log('平空仓市价单未完成')
exchange.CreateOrder(pair_b, 'closesell', -1, -stoplosspos.amount)
}
}
// 未平仓完毕
if (Math.abs(position_B.amount) < Trade_Number && Math.abs(position_B.amount) > 0 && afterTrade == 0){
Log('未平仓完毕')
if(position_B.amount > 0){
exchange.CreateOrder(pair_b, 'closebuy', -1, position_B.amount)
}else{
exchange.CreateOrder(pair_b, 'closesell', -1, -position_B.amount)
}
}
// 计算盈亏
if (position_B.amount == 0 && afterTrade == 0) {
if (stopLossOrderId != null || takeProfitOrderId != null) {
stopLossOrderId = null;
takeProfitOrderId = null;
let afterEquity = exchange.GetAccount().Equity;
let curAmount = afterEquity - curEq;
curEq = afterEquity
if (curAmount > 0) {
successCount += 1;
winMoney += curAmount;
Log('盈利金额:', curAmount);
} else {
lossCount += 1;
failMoney += curAmount;
Log('亏损金额:', curAmount);
}
afterTrade = 1;
}
}
if (startLoopTime % 10 == 0) { // 每 10 次循环记录一次
let curEquity = exchange.GetAccount().Equity
// 输出交易信息表
let table = {
type: "table",
title: "交易信息",
cols: [
"初始权益", "当前权益", Pair_B + "仓位", Pair_B + "持仓价", Pair_B + "收益", Pair_B + "价格",
"盈利次数", "盈利金额", "亏损次数", "亏损金额", "胜率", "盈亏比"
],
rows: [
[
_N(_G('init_eq'), 2), // 初始权益
_N(curEquity, 2), // 当前权益
_N(position_B.amount, 1), // Pair B 仓位
_N(position_B.price, pricePrecision), // Pair B 持仓价
_N(position_B.profit, 1), // Pair B 收益
_N(new_r_pairB.Close, pricePrecision), // Pair B 价格
_N(successCount, 0), // 盈利次数
_N(winMoney, 2), // 盈利金额
_N(lossCount, 0), // 亏损次数
_N(failMoney, 2), // 亏损金额
_N(successCount + lossCount === 0 ? 0 : successCount / (successCount + lossCount), 2), // 胜率
_N(failMoney === 0 ? 0 : winMoney / failMoney * -1, 2) // 盈亏比
]
]
};
$.PlotMultLine("ratio plot", "幅度变化差值", ratio, startLoopTime);
$.PlotMultHLine("ratio plot", diffLevel, "差价上限", "red", "ShortDot");
$.PlotMultHLine("ratio plot", -diffLevel, "差价下限", "blue", "ShortDot");
LogStatus("`" + JSON.stringify(table) + "`");
LogProfit(curEquity - initEq, '&')
}
}catch(e){
Log('策略出现错误:', e)
}
Sleep(200);
}
}