Skip to content

Latest commit

 

History

History
655 lines (600 loc) · 23.9 KB

bybit反向合约动态网格特异网格.md

File metadata and controls

655 lines (600 loc) · 23.9 KB

Name

bybit反向合约动态网格特异网格

Author

@cqz

Strategy Arguments

Argument Default Description
GridPercentage 0.05 GridPercentage
StartPositionRate 0.3 StartPositionRate
EndPositionRate 0.7 EndPositionRate
StartPrice 24000 StartPrice
EndPrice 60000 EndPrice
MinOrderCoinSize 0.001 MinOrderCoinSize
CoinSymbol BTC CoinSymbol
MinOrderAmount 5 MinOrderAmount
Reset false Reset

Source (javascript)

//Arguments:
//1. StartPrice
//2. EndPrice
//3. MinOrderCoinSize

// noinspection SillyAssignmentJS
CoinSymbol = CoinSymbol;
// noinspection SillyAssignmentJS
StartPrice = StartPrice;
// noinspection SillyAssignmentJS
EndPrice = EndPrice;
// noinspection SillyAssignmentJS
StartPositionRate = StartPositionRate;//0.3
// noinspection SillyAssignmentJS
EndPositionRate = EndPositionRate;//0.7
// noinspection SillyAssignmentJS
GridPercentage = GridPercentage;//0.05
// noinspection SillyAssignmentJS
MinOrderAmount = MinOrderAmount;
// noinspection SillyAssignmentJS
Reset = Reset;

let varA = (EndPositionRate - StartPositionRate) / (StartPrice - EndPrice);
let varB = EndPositionRate - StartPrice * varA;
let buyVarA = varA / (1 - GridPercentage / 100);
let sellVarA = varA / (1 + GridPercentage / 100);


function getPositionRate(varA, price) {
    if (price < StartPrice) {
        price = StartPrice;
    }
    if (price > EndPrice) {
        price = EndPrice;
    }
    return varA * price + varB;
}

function getAmounts(coinSize, currency, currentPrice, minOrderCoinSize) {
    let buyPrice = currentPrice - 5;
    let buyPositionRate = getPositionRate(buyVarA, buyPrice);
    let buyCoin = buyPositionRate * (coinSize * buyPrice + currency) / buyPrice - coinSize;
    buyCoin = buyCoin > minOrderCoinSize ? buyCoin : minOrderCoinSize;
    let sellPrice = currentPrice + 5;
    let sellPositionRate = getPositionRate(sellVarA, sellPrice);
    let sellCoin = sellPositionRate * (coinSize * sellPrice + currency) / sellPrice - coinSize;
    sellCoin = sellCoin < -minOrderCoinSize ? sellCoin : -minOrderCoinSize;
    return [buyCoin, sellCoin];
}

function getPrice(coinSize, currency, amount) {
    let varA = amount < 0 ? sellVarA : buyVarA;
    // Log("(" + (coinSize + amount) + " * y) / (" + coinSize + " * y + " + currency + ") = " + varA + " * y + " + varB);
    // ((coinSize + amount) * y) / (coinSize * y + currency) = varA * y + varB;
    // Log("(" + (coinSize + amount) + " * y)  = " + (varA * coinSize) + " * y^ + " + ((varA * currency) + (varB * coinSize)) + " * y + " + (varB * currency));
    //((coinSize + amount) * y)  = varA * coinSize * y^ + ((varA * currency) + (varB * coinSize)) * y + (varB * currency)
    // Log("y^2 + " + (((varA * currency) + (varB * coinSize) - (coinSize + amount)) / (varA * coinSize)) + " * y  =  " + (-(varB * currency) / (varA * coinSize)));
    // y^ + (((varA * currency) + (varB * coinSize) - (coinSize + amount))/(varA * coinSize)) * y  =  -(varB * currency)/(varA * coinSize)
    let k = (varA * currency + varB * coinSize - coinSize - amount) / (2 * varA * coinSize);
    let l = Math.sqrt(-((varB * currency) / (varA * coinSize)) + Math.pow(k, 2));
    let p1 = -l - k;
    let p2 = l - k;
    let price;
    if (p1 > 0 && p2 > 0) {
        price = amount < 0 ? Math.max(p1, p2) : Math.min(p1, p2);
    } else {
        price = Math.max(p1, p2);
    }

    let z = (coinSize + amount) * price / (coinSize * price + currency);
    let z1 = varA * StartPrice + varB;
    let z2 = varA * EndPrice + varB;
    if (z > z1) {
        price = z1 * currency / (coinSize + amount - z1 * coinSize)
    } else if (z < z2) {
        price = z2 * currency / (coinSize + amount - z2 * coinSize)
    }
    return parseInt(price);
}


let pricesChart = Chart([{ // 这个 chart 在JS 语言中 是对象, 在使用Chart 函数之前我们需要声明一个配置图表的对象变量chart。
    __isStock: true, // 标记是否为一般图表,有兴趣的可以改成 false 运行看看。
    tooltip: {
        xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
    }, // 缩放工具
    title: {
        text: '收益分析图'
    }, // 标题
    rangeSelector: { // 选择范围
        buttons: [{
            type: 'hour',
            count: 1,
            text: '1h'
        }, {
            type: 'hour',
            count: 3,
            text: '3h'
        }, {
            type: 'hour',
            count: 8,
            text: '8h'
        }, {
            type: 'all',
            text: 'All'
        }],
        selected: 0,
        inputEnabled: false
    },
    xAxis: {
        type: 'datetime'
    }, // 坐标轴横轴 即:x轴, 当前设置的类型是 :时间
    yAxis: { // 坐标轴纵轴 即:y轴, 默认数值随数据大小调整。
        title: {
            text: '收益'
        }, // 标题
        opposite: false, // 是否启用右边纵轴
    },
    series: [ // 数据系列,该属性保存的是 各个 数据系列(线, K线图, 标签等..)
        {
            name: "零",
            id: "Zero",
            dashStyle: 'shortdash',
            data: []
        }, {
            name: "策略收益",
            id: "策略收益,strategyProfit",
            data: []
        }, {
            name: "现货收益",
            id: "现货收益,spotProfit",
            data: []
        }
    ]
}]);


function GetOrders() {
    let ordersResp = exchange.IO("api", "GET", "/v2/private/order", "symbol=" + CoinSymbol + "USD");
    if (!ordersResp || ordersResp.ret_code !== 0) {
        return false;
    }
    let orderIds = [];
    for (let i = 0; i < ordersResp.result.length; i++) {
        orderIds.push(ordersResp.result[i].order_id);
    }
    return orderIds;
}

function GetFilledOrder(orderId) {
    let orderResp = exchange.IO("api", "GET", "/v2/private/order", "symbol=" + CoinSymbol + "USD&order_id=" + orderId);
    if (!orderResp || orderResp.ret_code !== 0 || orderResp.result.order_status !== "Filled") {
        return false;
    }
    let amount = orderResp.result.qty;
    amount = orderResp.result.side === "Sell" ? -amount : amount;
    return [orderResp.result.price, amount];

}


function sell(price, amount) {
    let createOrderResp = exchange.IO("api", "POST", "/v2/private/order/create", "symbol=" + CoinSymbol + "USD&side=Sell&order_type=Limit&qty=" + -amount + "&price=" + price + "&time_in_force=PostOnly");
    if (!createOrderResp || createOrderResp.ret_code !== 0) {
        return false;
    }
    Log("[Sell] Price:" + price + ", Amount:" + amount + ", orderId:" + createOrderResp.result.order_id);
    return createOrderResp.result.order_id;
}

function buy(price, amount) {
    let createOrderResp = exchange.IO("api", "POST", "/v2/private/order/create", "symbol=" + CoinSymbol + "USD&side=Buy&order_type=Limit&qty=" + amount + "&price=" + price + "&time_in_force=PostOnly");
    if (!createOrderResp || createOrderResp.ret_code !== 0) {
        return false;
    }
    Log("[Buy] Price:" + price + ", Amount:" + amount + ", orderId:" + createOrderResp.result.order_id);
    return createOrderResp.result.order_id;
}

function GetInversePosition() {
    let positionResp = exchange.IO("api", "GET", "/v2/private/position/list", "symbol=" + CoinSymbol + "USD");
    if (!positionResp || positionResp.ret_code !== 0) {
        return false;
    }
    let amount = positionResp.result.size;
    amount = positionResp.result.side === "Sell" ? -amount : amount;
    return [positionResp.result.entry_price, amount];
}

function GetInverseBalance() {
    let balanceResp = exchange.IO("api", "GET", "/v2/private/wallet/balance", "coin=" + CoinSymbol);
    if (!balanceResp || balanceResp.ret_code !== 0) {
        return false;
    }
    return balanceResp.result[CoinSymbol].wallet_balance;
}

function GetInverseAccount() {
    let position = GetInversePosition();
    if (!position) {
        return false;
    }
    let balance = GetInverseBalance();
    if (!balance) {
        return false;
    }
    let entry_price = position[0];
    let currency = -position[1];
    let coin = balance;
    coin = coin - (currency / entry_price);
    return [coin, currency];
}

function GetInverseTickerPrice() {
    let tickersResp = exchange.IO("api", "GET", "/v2/public/tickers", "symbol=" + CoinSymbol + "USD");
    if (!tickersResp || tickersResp.ret_code !== 0) {
        return false;
    }
    return (parseInt(tickersResp.result[0].bid_price) + parseInt(tickersResp.result[0].ask_price)) / 2;
}

function GetUSDTPosition() {
    let positionResp = null;
    let retryCount = 0;
    while (!positionResp || positionResp.ret_code !== 0) {
        if (retryCount++ > 0) {
            Sleep(2000);
        }
        positionResp = exchange.IO("api", "GET", "/private/linear/position/list", "symbol=" + CoinSymbol + "USDT");
    }
    // noinspection JSConstructorReturnsPrimitive
    return positionResp;
}

// function loop1(ctx) {
//     if (PendingOrders.length === 0) {
//         let account = _C(exchange.GetAccount);
//         let position = _C(exchange.GetPosition);
//         let ticker = _C(exchange.GetTicker);
//         let askPrice = ticker.Sell;
//         let bidPrice = ticker.Buy;
//         let balance = position.length === 0 ? 0 : (position[0].Amount + position[0].FrozenAmount);
//         let stocks = account.Stocks + account.FrozenStocks;
//         let dynamicPrice = balance / stocks;
//         let nextBuyPrice = _N(dynamicPrice * (1 - trimSpreadRatio / 100), pricePrecision);
//         let nextBuyAmount = _N((balance - stocks * nextBuyPrice) / 2, amountPrecision);
//         let nextSellPrice = _N(dynamicPrice * (1 + trimSpreadRatio / 100), pricePrecision);
//         let nextSellAmount = _N((stocks * nextSellPrice - balance) / 2, amountPrecision);
//         // Log("balance:" + balance + ", stocks:" + stocks + ", dynamicPrice:" + dynamicPrice + ", buyPrice:" + nextBuyPrice + ", sellPrice:" + nextSellPrice);
//         let sellOrder = sell(askPrice > nextSellPrice ? askPrice : nextSellPrice, nextSellAmount);
//         if (sellOrder) {
//             PendingOrders.push(sellOrder.orderID);
//         }
//         let buyOrder = buy(bidPrice < nextBuyPrice ? bidPrice : nextBuyPrice, nextBuyAmount);
//         if (buyOrder) {
//             PendingOrders.push(buyOrder.orderID);
//         }
//     } else {
//         let orders = [];
//         for (let i = 0; i < PendingOrders.length; i++) {
//             while (true) {
//                 orders[i] = _C(exchange.GetOrder, PendingOrders[i]);
//                 if (orders[i].Status !== ORDER_STATE_UNKNOWN) {
//                     break
//                 } else {
//                     Sleep(1000)
//                 }
//             }
//         }
//         let allOrderFinish = true;
//         let hasFinishOrder = false;
//         for (let i = 0; i < orders.length; i++) {
//             if (orders[i].Status === ORDER_STATE_PENDING) {
//                 allOrderFinish = false;
//             } else {
//                 hasFinishOrder = true;
//             }
//         }
//         if (hasFinishOrder) {
//             for (let i = 0; i < orders.length; i++) {
//                 if (orders[i].Status === ORDER_STATE_PENDING) {
//                     exchange.CancelOrder(PendingOrders[i], orders[i]);
//                 }
//             }
//         }
//         if (allOrderFinish) {
//             let avgPrice = 0;
//             for (let i = 0; i < orders.length; i++) {
//                 if (orders[i].DealAmount > 0) {
//                     avgPrice = orders[i].AvgPrice;
//                     Log(orders[i].Type === ORDER_TYPE_BUY ? "买入成功:" : "卖出成功:", "价格" + orders[i].AvgPrice + ", 成交数量", orders[i].DealAmount);
//                 }
//             }
//             if (avgPrice > 0) {
//                 ctx.positionChange = true;
//                 ctx.avgPrice = avgPrice;
//             }
//             PendingOrders = [];
//         } else {
//             if (PendingOrders.length === 1 && new Date().getTime() - new Date(orders[0].transactTime).getTime() > 5 * 1000) {
//                 exchange.CancelOrder(PendingOrders[0], orders[0]);
//             }
//         }
//     }
// }

// function checkArgs() {
//     exchange.SetContractType("XBTU20");
//     exchange.SetMarginLevel(1);
//     let RunningInfo = _G("runningInfo");
//     if (RunningInfo == null) {
//         RunningInfo = {};
//         let account = _C(exchange.GetAccount);
//         let position = _C(exchange.GetPosition);
//         OriginStocks = account.Stocks + account.FrozenStocks + (position.length === 0 ? 0 : (position[0].Amount + position[0].FrozenAmount) / position[0].Price);
//         RunningInfo.OriginStocks = OriginStocks;
//         _G("runningInfo", RunningInfo);
//     } else {
//         OriginStocks = RunningInfo.OriginStocks;
//     }
//     Log(OriginStocks);
// }

let PendingOrders = {};
let transactionalData = {
    "initTime": 0,
    "initAssets": 0,
    "initPrice": 1,
    "buyAmount": 0,
    "buyPrice": 1,
    "sellAmount": 0,
    "sellPrice": 1,
    "transactions": [],
};

function Init() {
    _CDelay(2000);
    let _transactionalData = _G("transactionalData");
    if (_transactionalData !== null) {
        transactionalData = _transactionalData;
    }
    if (!transactionalData.initTime || transactionalData.initTime <= 0) {
        resetTransaction();
    }
    Log(transactionalData);
    cancelAllOrders();
}

function resetTransaction() {
    transactionalData.initTime = 0;
    cancelAllOrders();
    while (!transactionalData.initTime || transactionalData.initTime <= 0) {
        let position = GetInversePosition();
        let balance = GetInverseBalance();
        let currentPrice = GetInverseTickerPrice();
        if (!position || !balance || !currentPrice) {
            Log("获取账户信息失败");
            Sleep(1000);
            continue
        }
        let entry_price = parseFloat(position[0]);
        let currency = -position[1];
        transactionalData.initTime = new Date().getTime();
        transactionalData.initPrice = currentPrice;
        transactionalData.initAssets = currency + (balance - (currency / entry_price)) * currentPrice;
        transactionalData.buyPrice = 1;
        transactionalData.buyAmount = 0;
        transactionalData.sellPrice = 1;
        transactionalData.sellAmount = 0;
        transactionalData.transactions = [];
        transactionalData.transactions.push([-currency, currentPrice]);
        LogReset(2);
        LogProfitReset();
        pricesChart.reset();
        Log("reset transactionalData:", transactionalData);
        _G("transactionalData", transactionalData);
        LogStatusInfo({
            "currentPrice": currentPrice,
            "coinSize": balance - (currency / entry_price),
            "currency": currency
        });
    }
}

function mergeTransactions() {
    let checkAmountSameSign = function (a1, a2) {
        return a1 === 0 || a2 === 0 || (a1 > 0 && a2 > 0) || (a1 < 0 && a2 < 0);
    };
    let mergeTransaction = function (amount, price) {
        if (amount > 0) {
            let buyCoin = amount / price + transactionalData.buyAmount / transactionalData.buyPrice;
            transactionalData.buyAmount = transactionalData.buyAmount + amount;
            transactionalData.buyPrice = transactionalData.buyAmount / buyCoin;
        } else if (amount < 0) {
            let sellCoin = -amount / price + transactionalData.sellAmount / transactionalData.sellPrice;
            transactionalData.sellAmount = transactionalData.sellAmount - amount;
            transactionalData.sellPrice = transactionalData.sellAmount / sellCoin;
        }
    };
    let length = transactionalData.transactions.length;
    if (length <= 2) {
        return;
    }
    let transaction1 = transactionalData.transactions[length - 2];
    let transaction2 = transactionalData.transactions[length - 1];
    if (checkAmountSameSign(transaction1[0], transaction2[0])) {
        return
    }
    let transactions = [];
    for (let i = 0; i < length - 2; i++) {
        transactions.push(transactionalData.transactions[i])
    }
    let mergeAmount = transaction1[0] + transaction2[0];
    if (mergeAmount === 0) {
        mergeTransaction(transaction1[0], transaction1[1]);
        mergeTransaction(transaction2[0], transaction2[1]);
    } else if (checkAmountSameSign(mergeAmount, transaction1[0])) {
        mergeTransaction(-transaction2[0], transaction1[1]);
        mergeTransaction(transaction2[0], transaction2[1]);
        transactions.push([mergeAmount, transaction1[1]])
    } else {
        mergeTransaction(transaction1[0], transaction1[1]);
        mergeTransaction(-transaction1[0], transaction2[1]);
        transactions.push([mergeAmount, transaction2[1]])
    }
    transactionalData.transactions = transactions;
    mergeTransactions()
}

function loop() {
    let orders = GetOrders();
    if (!orders) {
        return false;
    }
    //取消未知订单
    let cancelUnknownOrder = false;
    for (let i = 0; i < orders.length; i++) {
        if (!PendingOrders.hasOwnProperty(orders[i])) {
            exchange.CancelOrder(orders[i], "未知订单:", orders[i]);
            cancelUnknownOrder = true;
        }
    }
    if (cancelUnknownOrder) {
        return false;
    }

    let pendingOrderIds = Object.getOwnPropertyNames(PendingOrders);
    if (pendingOrderIds.length !== 0) {
        if (orders.length === pendingOrderIds.length) {
            return false;
        }
        for (let i = 0; i < orders.length; i++) {
            PendingOrders[orders[i]] = false;
            exchange.CancelOrder(orders[i], "未成交订单:", orders[i]);
        }
        for (let pendingOrderId of pendingOrderIds) {
            if (PendingOrders[pendingOrderId] === true) {
                let order = GetFilledOrder(pendingOrderId);
                if (!order) {
                    continue
                }
                let price = order[0];
                let amount = order[1];
                transactionalData.transactions.push([amount, price]);
                mergeTransactions();
                _G("transactionalData", transactionalData);
            }
        }
        PendingOrders = {};
        return false;
    }

    let inverseAccount = GetInverseAccount();
    if (!inverseAccount) {
        return false;
    }
    let currentPrice = GetInverseTickerPrice();
    if (!currentPrice) {
        return false;
    }
    let minOrderCoinSize = MinOrderAmount / currentPrice;
    // Log("account:", inverseAccount, "currentPrice:", currentPrice);
    let amounts = getAmounts(inverseAccount[0], inverseAccount[1], currentPrice, minOrderCoinSize);
    let buyPrice = getPrice(inverseAccount[0], inverseAccount[1], amounts[0]);
    let buyAmount = Math.round(amounts[0] * buyPrice);
    let sellPrice = getPrice(inverseAccount[0], inverseAccount[1], amounts[1]);
    let sellAmount = Math.round(amounts[1] * sellPrice);
    // Log("buy:", buyAmount, buyPrice);
    // Log("sell:", sellAmount, sellPrice);
    if ((currentPrice - buyPrice) / currentPrice < 0.1) {
        let buyOrderId = buy(buyPrice, buyAmount);
        if (buyOrderId) {
            PendingOrders[buyOrderId] = true
        }
    }
    if ((sellPrice - currentPrice) / currentPrice < 0.1) {
        let sellOrderId = sell(sellPrice, sellAmount);
        if (sellOrderId) {
            PendingOrders[sellOrderId] = true;
        }
    }
    return {"currentPrice": currentPrice, "coinSize": inverseAccount[0], "currency": inverseAccount[1]}
}

function LogStatusInfo(ctx) {
    let now = new Date();
    let statusTable = [{
        type: 'table',
        title: '网格统计',
        cols: ["买入价格", "买入数量", "卖出价格", "卖出数量", "网格盈亏(U)", "挂单手续费(U)", "收益率", "年化", ""],
        rows: [[]]
    }, {
        type: 'table',
        title: '资产汇总',
        cols: [CoinSymbol + "|价值(U)", "法币(U)", "比例", "汇总资产(U)", "初始资产(U)", "持仓收益(U)", "网格收益(U)", "总收益(U)", "收益率", "年化"],
        rows: [[]]
    }];
    statusTable[0].rows[0][0] = _N(transactionalData.buyPrice, 1);
    statusTable[0].rows[0][1] = transactionalData.buyAmount;
    statusTable[0].rows[0][2] = _N(transactionalData.sellPrice, 1);
    statusTable[0].rows[0][3] = transactionalData.sellAmount;
    let transactionPrice = (transactionalData.buyPrice + transactionalData.sellPrice) / 2;
    let transactionProfit = (transactionalData.buyAmount / transactionalData.buyPrice * transactionPrice - transactionalData.buyAmount) + (transactionalData.sellAmount - transactionalData.sellAmount / transactionalData.sellPrice * transactionPrice);
    statusTable[0].rows[0][4] = _N(transactionProfit, 2);
    let feeProfit = (transactionalData.buyAmount + transactionalData.sellAmount) * 0.025 / 100;
    statusTable[0].rows[0][5] = _N(feeProfit, 2);
    let profitRate = (feeProfit + transactionProfit) / transactionalData.initAssets * 100;
    statusTable[0].rows[0][6] = _N(profitRate, 2) + "%";
    statusTable[0].rows[0][7] = _N(profitRate / ((now.getTime() - transactionalData.initTime) / 24 / 3600 / 1000) * 365, 2) + "%";
    statusTable[0].rows[0][8] = {"type": "button", "name": "重置", "cmd": "resetTransaction", "description": "重置交易信息"};

    statusTable[1].rows[0][0] = _N(ctx.coinSize, 5) + "|" + _N(ctx.coinSize * ctx.currentPrice, 2);
    statusTable[1].rows[0][1] = _N(ctx.currency, 2);
    let currentAssets = ctx.coinSize * ctx.currentPrice + ctx.currency;
    statusTable[1].rows[0][2] = _N(ctx.coinSize * ctx.currentPrice / currentAssets, 3) + "%";
    statusTable[1].rows[0][3] = _N(currentAssets, 2);
    statusTable[1].rows[0][4] = _N(transactionalData.initAssets, 2);
    statusTable[1].rows[0][5] = _N(currentAssets - transactionProfit - feeProfit - transactionalData.initAssets, 2);
    statusTable[1].rows[0][6] = _N(transactionProfit + feeProfit, 2);
    statusTable[1].rows[0][7] = _N(currentAssets - transactionalData.initAssets, 2);
    let assetsProfitRate = (currentAssets - transactionalData.initAssets) / transactionalData.initAssets * 100;
    statusTable[1].rows[0][8] = _N(assetsProfitRate, 2) + "%";
    statusTable[1].rows[0][9] = _N(assetsProfitRate / ((now.getTime() - transactionalData.initTime) / 24 / 3600 / 1000) * 365, 2) + "%";
    LogStatus('`' + JSON.stringify(statusTable) + '`'
        // + "\n当前时间: " + _D(now.getTime())
        + "\n运行时长: " + _N((now.getTime() - transactionalData.initTime) / 24 / 3600 / 1000, 1) + "天"
        // + "\n上次重置: " + _N((now.getTime() - transactionalData.resetTime) / 24 / 3600 / 1000, 1) + "天"
    );
    LogProfit(transactionProfit + feeProfit, '&');
    pricesChart.add([0, [now.getTime(), 0]]);
    pricesChart.add([1, [now.getTime(), currentAssets - transactionalData.initAssets]]);
    pricesChart.add([2, [now.getTime(), (transactionalData.initAssets / transactionalData.initPrice) * ctx.currentPrice - transactionalData.initAssets]]);
}

function main() {
    if (Reset) {
        resetTransaction();
    }
    LogReset(1000);
    LogVacuum();
    Init();
    while (true) {
        let cmd = GetCommand();
        if (cmd) {
            Log(cmd);
            if (cmd === "resetTransaction") {
                resetTransaction();
            }
        }
        Sleep(3000);
        let ctx = loop();
        if (!ctx) {
            continue
        }
        LogStatusInfo(ctx);
    }

    // Log(GetUSDTPosition());
    // if (restoreData) {
    //     LogProfitReset();
    //     LogReset(1);
    //     LogVacuum();
    //     pricesChart.reset();
    //     _G(null);
    // }
    // checkArgs();
    // _CDelay(2000);
    // // noinspection InfiniteLoopJS
    // while (true) {
    //     let ctx = {};
    //     loop(ctx);
    //     Sleep(2000);
    //     if (ctx.positionChange) {
    //         updateChart(ctx.avgPrice);
    //     }
    // }
}

function onexit() {
    Log("取消所有订单...");
    cancelAllOrders();
}

function onerror() {
    Log("取消所有订单...");
    cancelAllOrders();
}

function cancelAllOrders() {
    let orders = _C(exchange.GetOrders);
    for (let i = 0; i < orders.length; i++) {
        exchange.CancelOrder(orders[i].Id, orders[i]);
    }
}

Detail

https://www.fmz.com/strategy/365406

Last Modified

2022-05-24 17:38:11