리소스 로딩... 로딩...

FMZ 업그레이드 후 어떻게 더 빠르게 통용형 다화 거래 전략을 구축할 수 있습니까?

저자:초목, 창작: 2024-09-30 15:22:55, 업데이트: 2024-10-10 17:18:56

img

전문

발명자는 양자 거래 플랫폼을 설립한 시기는 너무 이른 시기였으며, 당시 거래소와 통화도 매우 제한되어 있었고 거래 방식도 많지 않았기 때문에 초기 API 디자인은 매우 단순하고 단일 통화 거래 전략에 초점을 맞췄다. 몇 년의 반복, 특히 최근의 버전이 비교적 완성되었으며, 일반적인 거래소 API는 모두 포장 된 기능으로 완료되었습니다. 특히 다중 통화 전략, 거래, 계좌 및 거래는 이전보다 훨씬 단순화되었으며, 거래소에 액세스하는 API 인터페이스에는 IO 기능이 더 필요하지 않았습니다. 리테이크는 또한 업그레이드되었으며, 실제 호환도 있습니다. 요컨대, 귀하의 전략이 원래 많은 특화된 방법이 있었고, 여러 거래소와 리테이크를 동시에 사용할 수 없다면 업그레이드 할 수 있습니다. 다음 문서는 전략을 통합하고 현재 전략 구조를 소개합니다.

호스트는 완전한 지원을 위해 3.7으로 업그레이드해야하며, API 인터페이스 추가 기능과 같은 정보는 발명자의 양적 거래 플랫폼의 API 문서에 동기 업데이트되었습니다:

문법 설명서:https://www.fmz.com/syntax-guide사용자 설명서:https://www.fmz.com/user-guide

정확성을 얻습니다.

현재 API에는 통일된 취득 정확도 함수가 있으며, 여기서 영구 계약의 예를 소개합니다.

//全局的变量,储存数据,SYMBOLS代表要交易的币种,格式如"BTC,ETH,LTC", QUOTO为基础货币,永续合约常见的有USDT,USDC,INTERVAL代表循环的间隔。
var Info = { trade_symbols: SYMBOLS.split(","), base_coin: QUOTO, ticker: {}, order: {}, account: {}, precision: {}, 
            position: {}, time:{}, count:{}, interval:INTERVAL}
function InitInfo() {
    //初始化策略
    if (!IsVirtual() && Version() < 3.7){
        throw "[trans]FMZ平台升级API,需要下载最新托管者|Update to neweset docekr[/trans]";
    }
    Info.account = {init_balance:0};
    Info.time = {
        update_ticker_time: 0,
        update_pos_time: 0,
        update_profit_time: 0,
        update_account_time: 0,
        update_status_time: 0,
        last_loop_time:0,
        loop_delay:0,
    };
    for (let i = 0; i < Info.trade_symbols.length; i++) {
        let symbol = Info.trade_symbols[i];
        Info.ticker[symbol] = { last: 0, ask: 0, bid: 0 };
        Info.order[symbol] = { buy: { id: 0, price: 0, amount: 0 }, sell: { id: 0, price: 0, amount: 0 } };
        Info.position[symbol] = { amount: 0, hold_price: 0, unrealised_profit: 0, open_time: 0, value: 0 };
        Info.precision[symbol] =  {};
    }
}
//获取精度
function GetPrecision() {
    let exchange_info = exchange.GetMarkets();
    for (let pair in exchange_info) {
        let symbol = pair.split('_')[0]; //永续合约交易对的格式为 BTC_USDT.swap
        if (Info.trade_symbols.indexOf(symbol) > -1 && pair.split('.')[0].endsWith(Info.base_coin) && pair.endsWith("swap")) {
            Info.precision[symbol].tick_size = exchange_info[pair].TickSize;
            Info.precision[symbol].amount_size = exchange_info[pair].AmountSize;
            Info.precision[symbol].price_precision = exchange_info[pair].PricePrecision
            Info.precision[symbol].amount_precision = exchange_info[pair].AmountPrecision
            Info.precision[symbol].min_qty = exchange_info[pair].MinQty
            Info.precision[symbol].max_qty = exchange_info[pair].MaxQty
            Info.precision[symbol].min_notional = exchange_info[pair].MinNotional
            Info.precision[symbol].ctVal = exchange_info[pair].CtVal; //合约价值,如1张代表0.01个币
            if (exchange_info[pair].CtValCcy != symbol){ //价值的计价货币,这里不处理币本位的情况,如1张价值100美元
                throw "[trans]不支持币本位|Don't support coin margin type[/trans]"
            }
        }
    }
}

시장에 접근하는 방법

다종양 전략을 설계하기 위해서는 전체 시장을 확보해야 한다. 이러한 종합시장 인터페이스는 필수적이며, GetTickers 기능은 대부분의 주류 거래소를 지원한다.

function UpdateTicker() {
    //更新价格
    let ticker = exchange.GetTickers();
    if (!ticker) {
        Log("[trans]获取行情失败|Fail to get market[/trans]", GetLastError());
        return;
    }
    Info.time.update_ticker_time = Date.now();
    for (let i = 0; i < ticker.length; i++) {
        let symbol = ticker[i].Symbol.split('_')[0];
        if (!ticker[i].Symbol.split('.')[0].endsWith(Info.base_coin) || Info.trade_symbols.indexOf(symbol) < 0 || !ticker[i].Symbol.endsWith('swap')) {
            continue;
        }
        Info.ticker[symbol].ask = parseFloat(ticker[i].Sell);
        Info.ticker[symbol].bid = parseFloat(ticker[i].Buy);
        Info.ticker[symbol].last = parseFloat(ticker[i].Last);
    }
}

계정 위치 정보를 얻으십시오

선물 계정 정보는 추가 처리로 인한 호환불합격성을 방지하기 위해 엑위티 총권익 필드와 UPnL 필드를 새로 추가했다. GetPositions 함수는 전체 포지션을 획득하는 것을 지원한다.

function UpdateAccount() {
    //更新账户
    if (Date.now() - Info.time.update_account_time < 60 * 1000) {
        return;
    }
    Info.time.update_account_time = Date.now();
    let account = exchange.GetAccount();
    if (account === null) {
        Log("[trans]更新账户失败|Fail to get account[/trans]");
        return;
    }
    Info.account.margin_used = _N(account.Equity - account.Balance, 2);
    Info.account.margin_balance = _N(account.Equity, 2); //当前余额
    Info.account.margin_free = _N(account.Balance, 2);
    Info.account.wallet_balance = _N(account.Equity - account.UPnL, 2);
    Info.account.unrealised_profit = _N(account.UPnL, 2);
    if (!Info.account.init_balance) {
        if (_G("init_balance") && _G("init_balance") > 0) {
            Info.account.init_balance = _N(_G("init_balance"), 2);
        } else {
            Info.account.init_balance = Info.account.margin_balance;
            _G("init_balance", Info.account.init_balance);
        }
    }
    Info.account.profit = _N(Info.account.margin_balance - Info.account.init_balance, 2);
    Info.account.profit_rate = _N((100 * Info.account.profit) / init_balance, 2);
}

function UpdatePosition() {
    let pos = exchange.GetPositions(Info.base_coin + ".swap");
    if (!pos) {
        Log("[trans]更新仓位超时|Fail to get position[/trans]");
        return;
    }
    Info.time.update_pos_time = Date.now();
    let position_info = {};
    for (let symbol of Info.trade_symbols) {
        position_info[symbol] = {
            amount: 0,
            hold_price: 0,
            unrealised_profit: 0
        }; //有的交易所没有仓位返回空
    }
    for (let k = 0; k < pos.length; k++) {
        let symbol = pos[k].Symbol.split("_")[0];
        if (!pos[k].Symbol.split(".")[0].endsWith(Info.base_coin) || Info.trade_symbols.indexOf(symbol) < 0) {
            continue;
        }
        if (position_info[symbol].amount != 0){
            throw "[trans]需要单向持仓|Position need net Mode:[/trans]";
        }
        position_info[symbol] = {
            amount: pos[k].Type == 0 ? pos[k].Amount * Info.precision[symbol].ctVal : -pos[k].Amount * Info.precision[symbol].ctVal,
            hold_price: pos[k].Price,
            unrealised_profit: pos[k].Profit
        };
    }
    Info.count = { long: 0, short: 0, total: 0, leverage: 0 };
    for (let symbol in position_info) {
        let deal_volume = Math.abs(position_info[symbol].amount - Info.position[symbol].amount);
        let direction = position_info[symbol].amount - Info.position[symbol].amount > 0 ? 1 : -1;
        if (deal_volume) {
            let deal_price = direction == 1 ? Info.order[symbol].buy.price : Info.order[symbol].sell.price;
            Log(
                symbol,
                "[trans]仓位更新:|Position update:[/trans]",
                _N(Info.position[symbol].value, 1),
                " -> ",
                _N(position_info[symbol].amount * Info.ticker[symbol].last, 1),
                direction == 1 ? "[trans], 买|. Buy[/trans]" : "[trans], 卖|, Sell[/trans]",
                "[trans], 成交价:| Deal price: [/trans]",
                deal_price,
                "[trans], 成本价:| Hold price: [/trans]",
                _N(Info.position[symbol].hold_price, Info.precision[symbol].price_precision),
            );
        }
        Info.position[symbol].amount = position_info[symbol].amount;
        Info.position[symbol].hold_price = position_info[symbol].hold_price;
        Info.position[symbol].value = _N(Info.position[symbol].amount * Info.ticker[symbol].last, 2);
        Info.position[symbol].unrealised_profit = position_info[symbol].unrealised_profit;
        Info.count.long += Info.position[symbol].amount > 0 ? Math.abs(Info.position[symbol].value) : 0;
        Info.count.short += Info.position[symbol].amount < 0 ? Math.abs(Info.position[symbol].value) : 0;
    }
    Info.count.total = _N(Info.count.long + Info.count.short, 2);
    Info.count.leverage = _N(Info.count.total / Info.account.margin_balance, 2);
}

거래

트레이딩은 또한 방정식의 문제를 처리합니다. 최신 CreateOrder 기능을 사용하여 주문을 처리하는 것이 매우 편리합니다.

function Order(symbol, direction, price, amount, msg) {
    let ret = null;
    let pair = symbol + "_" + Info.base_coin + ".swap"
    ret = exchange.CreateOrder(pair, direction, price,  amount, msg)
    if (ret) {
        Info.order[symbol][direction].id = ret;
        Info.order[symbol][direction].price = price;
    }else {
        Log(symbol, direction, price, amount, "[trans]下单异常|Error on order[/trans]");
    }
}

function Trade(symbol, direction, price, amount, msg) {
    price = _N(price - (price % Info.precision[symbol].tick_size), Info.precision[symbol].price_precision);
    amount = amount / Info.precision[symbol].ctVal;
    amount = _N(amount - (amount % Info.precision[symbol].amount_size), Info.precision[symbol].amount_precision);
    amount = Info.precision[symbol].max_qty > 0 ? Math.min(amount, Info.precision[symbol].max_qty) : amount;
    let new_order = false;
    if (price > 0 && Math.abs(price - Info.order[symbol][direction].price) / price > 0.0001) { //两次订单有差价了才撤单
        new_order = true;
    }
    if (amount <= 0 || Info.order[symbol][direction].id == 0) { //传入amount为0 撤单
        new_order = true;
    }
    if (new_order) {
        if (Info.order[symbol][direction].id) { //原有订单撤销
            CancelOrder(symbol, direction, Info.order[symbol][direction].id);
            Info.order[symbol][direction].id = 0;
        }
        if ( //延时过高不下单
            Date.now() - Info.time.update_pos_time > 2 * Info.interval * 1000 ||
            Date.now() - Info.time.update_ticker_time > 2 * Info.interval * 1000 ||
        ) {
            return;
        }
        if (price * amount <= Info.precision[symbol].min_notional || amount < Info.precision[symbol].min_qty) {
            Log(symbol, "[trans]下单量太低|amount is too small[/trans]", price * amount);
            return;
        }
        Order(symbol, direction, price, amount, msg);
    }
}

상태 표시

일반적으로 두 개의 표를 표시합니다.

unction UpdateStatus() {
    if (Date.now() - Info.time.update_status_time < 4000) {
        return;
    }
    Info.time.update_status_time = Date.now();
    let table1 = {
        type: "table",
        title: "[trans]账户信息|Account info[/trans]",
        cols: [
            "[trans]初始余额|Initial Balance[/trans]",
            "[trans]钱包余额|Wallet balance[/trans]",
            "[trans]保证金余额|Margin balance[/trans]",
            "[trans]已用保证金|Used margin[/trans]",
            "[trans]可用保证金|Avaiable margin[/trans]",
            "[trans]总收益|Profit[/trans]",
            "[trans]收益率|Profit rate[/trans]",
            "[trans]未实现收益|Unrealised profit[/trans]",
            "[trans]总持仓|Total value[/trans]",
            "[trans]已用杠杆|Leverage-used[/trans]",
            "[trans]循环延时|Delay[/trans]",
        ],
        rows: [
            [
                Info.account.init_balance,
                Info.account.wallet_balance,
                Info.account.margin_balance,
                Info.account.margin_used,
                Info.account.margin_free,
                Info.account.profit,
                Info.account.profit_rate + "%",
                _N(Info.account.unrealised_profit, 2),
                _N(Info.count.total, 2),
                Info.count.leverage,
                Info.time.loop_delay + "ms",
            ],
        ],
    };
    let table2 = {
        type: "table",
        title: "[trans]交易对信息|Symbol info[/trans]",
        cols: [
            "[trans]币种|Symbol[/trans]",
            "[trans]方向|Direction[/trans]",
            "[trans]数量|Amount[/trans]",
            "[trans]持仓价格|Hold price[/trans]",
            "[trans]持仓价值|Value[/trans]",
            "[trans]现价|Price[/trans]",
            "[trans]挂单买价|Buy price[/trans]",
            "[trans]挂单卖价|Sell price[/trans]",
            "[trans]未实现盈亏|Unrealised profit[/trans]",
        ],
        rows: [],
    };
    for (let i in Info.trade_symbols) {
        let symbol = Info.trade_symbols[i];
        table2.rows.push([
            symbol,
            Info.position[symbol].amount > 0 ? "LONG" : "SHORT",
            _N(Info.position[symbol].amount, Info.precision[symbol].amount_precision+2),
            _N(Info.position[symbol].hold_price, Info.precision[symbol].price_precision),
            _N(Info.position[symbol].value, 2),
            _N(Info.ticker[symbol].last, Info.precision[symbol].price_precision),
            Info.order[symbol].buy.price,
            Info.order[symbol].sell.price,
            _N(Info.position[symbol].unrealised_profit, 2),
        ]);
    }

    LogStatus(
        "[trans]初始化时间: | Initial date: [/trans]" + _D(new Date(Info.time.start_time)) + "\n",
        "`" + JSON.stringify(table1) + "`" + "\n" + "`" + JSON.stringify(table2) + "`\n",
        "[trans]最后执行时间: |Last run date: [/trans]" + _D() + "\n",
    );
    if (Date.now() - Info.time.update_profit_time > 5 * 60 * 1000) {
        UpdateAccount();
        LogProfit(_N(Info.account.profit, 3));
        Info.time.update_profit_time = Date.now();
    }
}

거래 논리

팔찌가 맞물려지면 가장 핵심적인 거래 논리 코드가 간단해집니다. 여기 가장 간단한 얼음 덩어리 아래의 전략이 있습니다.

function MakeOrder() {
    for (let i in Info.trade_symbols) {
        let symbol = Info.trade_symbols[i];
        let buy_price = Info.ticker[symbol].bid;
        let buy_amount = 50 / buy_price;
        if (Info.position[symbol].value < 2000){
            Trade(symbol, "buy", buy_price, buy_amount, symbol);
        }
    }
}

주 순환

function OnTick() {
    try {
        UpdateTicker();
        UpdatePosition();
        MakeOrder();
        UpdateStatus();
    } catch (error) {
        Log("[trans]循环出错: | Loop error: [/trans]" + error);
    }
}

function main() {
    InitInfo();
    while (true) {
        let loop_start_time = Date.now();
        if (Date.now() - Info.time.last_loop_time > Info.interval * 1000) {
            OnTick();
            Info.time.last_loop_time = Date.now();
            Info.time.loop_delay = Date.now() - loop_start_time;
        }
        Sleep(5);
    }
}

요약

이 문서에서는 최신 API를 활용한 간단한 지속 가능한 다화폐 계약 거래 프레임 워크를 제공하여 보다 편리하고 빠르게 호환성 전략을 구축할 수 있으며 시도할 가치가 있습니다.


더 많은

이안젠123이 문이 얼마나 깊은지 알게 된 것에 감사드립니다.