0bps

Strategy vault / Grid

Bybit Inverse Contract Dynamic Grid Special Grid

JavaScript strategy by @cqz.

JavaScriptGrid
Author
@cqz
Complexity
Advanced · 615 lines
Parameters
9

The original spec has no written description. Read the source below.

Parameters

NameDefaultWhat it controls
GridPercentage0.05GridPercentage
StartPositionRate0.3StartPositionRate
EndPositionRate0.7EndPositionRate
StartPrice24000StartPrice
EndPrice60000EndPrice
MinOrderCoinSize0.001MinOrderCoinSize
CoinSymbolBTCCoinSymbol
MinOrderAmount5MinOrderAmount
ResetfalseReset

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([{ // this chart At/InJS In the language, it is an object, in useChart Before the function, we need to declare an object variable to configure the chartchart.
    __isStock: true, // Mark whether it is a general chart. If interested, you can change it to false and run it.
    tooltip: {
        xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
    }, // Zoom tool
    title: {
        text: 'Profit analysis chart'
    }, // Title
    rangeSelector: { // Selection range
        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'
    }, // Horizontal axis of the coordinate system, i.e., x-axis. The currently set type is: Time
    yAxis: { // coordinate axis vertical axis i.e., y-axis, default value adjusts with data size.
        title: {
            text: 'Revenue'
        }, // Title
        opposite: false, // Whether to enable the right vertical axis
    },
    series: [ // Data series, this attribute saves each data series (line, candlestick chart, label, etc...)
        {
            name: "Zero",
            id: "Zero",
            dashStyle: 'shortdash',
            data: []
        }, {
            name: "Strategy income",
            id: "Strategy income,strategyProfit",
            data: []
        }, {
            name: "Spot income",
            id: "Spot income,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 ? "Purchase successful:" : "Sold successfully:", "Price" + orders[i].AvgPrice + ", Transaction quantity", 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("Failed to obtain account information");
            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;
    }
    //Cancel unknown order
    let cancelUnknownOrder = false;
    for (let i = 0; i < orders.length; i++) {
        if (!PendingOrders.hasOwnProperty(orders[i])) {
            exchange.CancelOrder(orders[i], "Unknown order:", 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], "Unfilled orders:", 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: 'Grid Statistics',
        cols: ["Buy price", "Buy quantity", "Sell price", "Sell quantity", "Grid profit and loss (U)", "Order fee (U)", "Yield", "Annualized."", ""],
        rows: [[]]
    }, {
        type: 'table',
        title: 'Asset Summary',
        cols: [CoinSymbol + "|Value(U)", "Legal currency(U)", "Proportion", "Total Assets(U)", "Initial Assets(U)", "Position Profit(U)", "Grid Profit(U)", "Total income(U)", "Yield", "Annualized"],
        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": "Reset", "cmd": "resetTransaction", "description": "Reset transaction information"};

    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) + '`'
        // + "\nCurrent Time: " + _D(now.getTime())
        + "\nRunning Duration: " + _N((now.getTime() - transactionalData.initTime) / 24 / 3600 / 1000, 1) + "Sky/Heaven"
        // + "\nLast Reset: " + _N((now.getTime() - transactionalData.resetTime) / 24 / 3600 / 1000, 1) + "Sky/Heaven"
    );
    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("Cancel All Orders...");
    cancelAllOrders();
}

function onerror() {
    Log("Cancel All Orders...");
    cancelAllOrders();
}

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

Published by @cqz on the FMZ strategy square · last modified 2022-05-24. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a grid bot with inventory caps and order deduplication from strategies/Bybit-Inverse-Contract-Dynamic-Grid-Special-Grid.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open