Loading ...

跟单机器人【币安U本位合约多账号全币种】V1.0

Author: 夏天不打你, Date: 2021-09-24 07:45:49
Tags:

功能说明: 1、支持币安U本位合约全部币种 2、支持多个跟单账号同时跟单 3、支持的跟单类型包括:市价单、限价单、止盈止损、仓位止盈止损 4、支持跟单账号数据本地保存和恢复 5、支持U本位合约资产自动划转到现货账户 6、支持自定义不同跟单账户设置不同的跟单数量 7、支持手动平仓和撤单

参数说明: 程序运行周期:单位毫秒,根据跟单账号数量自行调整,只要IP访问次数不受限制即可 杠杆倍数:默认的杠杆倍数,只有在杠杆跟随没勾的时候生效 杠杆跟随:将会跟随主账号的杠杆倍数进行下单 下单数量(默认):默认的下单数量,多个跟单账号用英文逗号分开,仅在下单张数跟随没有勾选的时候生效 下单张数百分比%:跟单账号实际的下单数量是主账号下单数量的百分之多少。多个跟单账号用英文逗号分割。 最大跟单时间间隔(秒):但主账号的持仓或者挂单更新时间超过这个时间,将不会跟单。 使用自动划转:勾上之后,将会开启U本位合约自动划转到现货账户的功能。 当资产大于* U时自动移出100U:当U本位合约的资产大于该设定值时候,会自动划转100U到现货账号。



/*
跟单机器人【币安U本位合约多账号全币种】V1.0
Version: 1.0
Author: summer
Date: 2021.9.24

注意事项:
1、仅支持币安U本位合约
2、添加的交易所标签不要重复,避免本地数据读写出错
3、主账号只能放在第一个交易所,跟单账号从第二个交易所开始
4、止盈止损限价单默认转化为市价单
*/


// 策略参数变量
var _Interval = Interval;                                                   // 程序运行周期
var _MarginLevel = MarginLevel;                                             // 默认杠杆倍数
var _FollowMarginLevel = FollowMarginLevel;                                 // 杠杆跟随
var _OrderSize = [];                                                        // 默认下单张数
var _FollowOrderSize = FollowOrderSize;                                     // 下单张数跟随
var _FollowOrderSizePercent = [];                                           // 下单张数百分比
var _MaxFollowInterval = MaxFollowInterval;                                 // 最大跟单时间间隔(秒)
var _UseAutoTransfer = UseAutoTransfer;                                     // 使用自动划转
var _AccountMaxBalance = [];                                                // 当前资产大于*U时自动移出100U

// 相对固定变量
var _MinimOrderSize = 0.0001;                                               // 最小下单数量
var _MinimOrderCash = 5;                                                    // 最小下单金额 5USDT

// 统计变量
var _UserStartTime = [];
var _InitAsset = [];
var _ProfitLocal = [];
var _TakeProfitCount = [];
var _TradeCount = [];
var _TransferAmount = [];
var StrategyRunTimeStampString = "strategy_run_time";
var _StrategyDatas = { start_run_timestamp: 0, others: "" };
var _UserDatas = [];

// 策略变量
var _Positions = [];
var _PendingOrders = [];
var _CurrentAssets = [];
var _Accounts = [];
var _BinanceExchangeInfo = null;
var _EnableLogPrint = false;


// 保存程序起始运行时间 秒级时间戳
function saveStrategyRunTime() {
    var local_data_strategy_run_time = _G(StrategyRunTimeStampString);

    if (local_data_strategy_run_time == null) {
        _StrategyDatas.start_run_timestamp = Unix();
        _G(StrategyRunTimeStampString, _StrategyDatas.start_run_timestamp);
    }
    else {
        _StrategyDatas.start_run_timestamp = local_data_strategy_run_time;
    }
}

// 计算两个时间戳之间的天数,参数是秒级时间戳
function getDaysFromTimeStamp(start_time, end_time) {
    if (end_time < start_time)
        return 0;

    return Math.trunc((end_time - start_time) / (60 * 60 * 24));
}

// 保存所有账号的数据到本地
function saveUserDatasLocal() {
    // 保存数据之前,先把UserData数据清零
    _UserDatas = [];
    for (var i = 0; i < exchanges.length - 1; i++) {
        _UserDatas.push({
            start_time: _UserStartTime[i],
            init_assets: _InitAsset[i],
            profit_local: _ProfitLocal[i],
            take_profit_count: _TakeProfitCount[i],
            trade_count: _TradeCount[i],
            transfer_amount: _TransferAmount[i]
        });
        // 存储到本地
        _G(exchanges[i + 1].GetLabel(), _UserDatas[i]);
    }
    Log("已把所有数据保存到本地.");
}

// 读取用户本地数据,程序启动时候运行一次
function readUserDataLocal() {
    for (var i = 1; i < exchanges.length; i++) {
        var user_data = _G(exchanges[i].GetLabel());
        if (user_data == null) {
            var total_equity = null;
            var account = null;
            [total_equity, account] = getTotalEquityBinance(i);
            if (total_equity == null) {
                throw "账户权益获取失败,请重新尝试!";
            }
            _UserDatas[i - 1] = {
                start_time: Unix(),
                init_assets: total_equity,
                profit_local: 0,
                take_profit_count: 0,
                trade_count: 0,
                transfer_amount: 0
            };
        } else {
            _UserDatas[i - 1] = user_data;
        }
    }
}

// 清除用户本地数据,交互按钮点击运行
function clearUserDataLocal(num) {
    if (num == -1) {
        for (var i = 1; i < exchanges.length; i++) {
            _G(exchanges[i].GetLabel(), null);
        }
        Log("已清除所有账号的本地数据.");
    } else {
        _G(exchanges[num].GetLabel(), null);
    }
    Log(exchanges[num].GetLabel(), ":已清除本地数据.");
}

// 获取当前时间,单位:毫秒
function getCurrentTime() {
    return UnixNano() / 1000000;
}

// 获取账户总权益
function getTotalEquityBinance(num) {
    var total_equity = null;
    var ret = _C(exchanges[num].GetAccount);
    if (ret) {
        try {
            total_equity = parseFloat(ret.Info.totalWalletBalance);
        } catch (e) {
            Log(exchanges[num].GetLabel(), ": 获取账户总权益失败!");
            return null;
        }
    }
    // Log("总资产:", total_equity);
    return [total_equity, ret];
}

// 收益统计
function calculateProfit(num) {
    // 重新获取一下账户资产
    [_CurrentAssets[num], _Accounts[num]] = getTotalEquityBinance(num);
    // 当前总收益 - 上一次总收益 = 本次的收益
    var current_profit = (_CurrentAssets[num] + _TransferAmount[num - 1] - _InitAsset[num - 1]) - _ProfitLocal[num - 1];
    _ProfitLocal[num - 1] += current_profit;
    // 统计胜率
    if (current_profit > 0)
        _TakeProfitCount[num - 1]++;
    _TradeCount[num - 1]++;
    // 打印收益和保存数据
    Log(exchanges[num].GetLabel(), ":本次收益:", _N(current_profit, 2), ", 总收益:", _N(_ProfitLocal[num - 1], 2), ", 总交易次数:", _TradeCount[num - 1],
        ", 盈利次数:", _TakeProfitCount[num - 1], ", 胜率:", _N(_TakeProfitCount[num - 1] / _TradeCount[num - 1] * 100, 2), "%");
    if (num == 1) {     // 只绘制第一个跟单账号的收益曲线
        LogProfit(_ProfitLocal[num - 1], "&");
    }
    if (num == (exchanges.length - 1)) {    // 只有当最后一个账号平仓之后,才保存所有数据到本地
        saveUserDatasLocal();
    }
}

// 自动移出
function autoTransfer(num, current_asset, amount) {
    if (num <= 0) {
        return;
    }
    if (_UseAutoTransfer) {
        if (current_asset > _AccountMaxBalance) {
            if (transferToMain(num, amount)) {
                _TransferAmount[num - 1] += amount;
            }
        }
    }
}

// 从U本位合约钱包向现货钱包划转指定数量的USDT
function transferToMain(num, amount){
    var time = UnixNano() / 1000000;
    var param = "type=UMFUTURE_MAIN" + "&asset=USDT" + "&amount=" + amount.toString() + "&timestamp=" + time.toString();
    exchanges[num].SetBase('https://api.binance.com');
    var ret = exchanges[num].IO("api", "POST", "/sapi/v1/asset/transfer", param);
    exchanges[num].SetBase('https://fapi.binance.com');
    if (ret) {
        Log(exchanges[num].GetLabel(), ": 已从U本位钱包向现货钱包划转: ", amount, " USDT");
        return true;
    } else {
        Log(exchanges[num].GetLabel(), ": 资金划转失败");
        return false;
    }
}

// 交易函数
function directTrade(num, distance, price, amount) {
    var tradeFunc = null;

    if (distance == "buy") {
        tradeFunc = exchanges[num].Buy;
    } else if (distance == "sell") {
        tradeFunc = exchanges[num].Sell;
    } else if (distance == "closebuy") {
        tradeFunc = exchanges[num].Sell;
    } else {
        tradeFunc = exchanges[num].Buy;
    }

    exchanges[num].SetDirection(distance);
    return tradeFunc(price, amount);
}

function openLong(num, price, amount) {
    return directTrade(num, "buy", price, amount);
}

function openShort(num, price, amount) {
    return directTrade(num, "sell", price, amount);
}

function coverLong(num, price, amount) {
    directTrade(num, "closebuy", price, amount)
    return true;
}

function coverShort(num, price, amount) {
    directTrade(num, "closesell", price, amount);
    return true;
}

// 挂多单止损
function longStopLoss(num, symbol, price, size) {
    var time = UnixNano() / 1000000;
    var param = null;
    // var param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=SELL" + "&type=STOP_MARKET" + "&timestamp=" + time.toString();                           // 单向持仓
    if (size != 0) {
        param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=SELL" + "&type=STOP_MARKET" + "&positionSide=LONG" + "&timestamp=" + time.toString();       // 双向持仓
    } else {
        param = "symbol=" + symbol + "&closePosition=true" + "&stopPrice=" + price.toString() + "&side=SELL" + "&type=STOP_MARKET" + "&positionSide=LONG" + "&timestamp=" + time.toString();                // 仓位止盈止损
    }
    var ret = exchanges[num].IO("api", "POST", "/fapi/v1/order", param);
    Log(exchanges[num].GetLabel(), ": 挂多单止损:", ret);
    return true;
}

// 挂多单止盈
function longTakeProfit(num, symbol, price, size) {
    var time = UnixNano() / 1000000;
    var param = null;
    if (size != 0) {
        param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=SELL" + "&type=TAKE_PROFIT_MARKET" + "&positionSide=LONG" + "&timestamp=" + time.toString();       // 双向持仓
    } else {
        param = "symbol=" + symbol + "&closePosition=true" + "&stopPrice=" + price.toString() + "&side=SELL" + "&type=TAKE_PROFIT_MARKET" + "&positionSide=LONG" + "&timestamp=" + time.toString();                // 仓位止盈止损
    }
    var ret = exchanges[num].IO("api", "POST", "/fapi/v1/order", param);
    Log(exchanges[num].GetLabel(), ": 挂多单止盈:", ret);
    return true;
}

// 挂空单止损
function shortStopLoss(num, symbol, price, size) {
    var time = UnixNano() / 1000000;
    var param = null;
    // var param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=BUY" + "&type=STOP_MARKET" + "&timestamp=" + time.toString();                          // 单向持仓   
    if (size != 0) {     
        param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=BUY" + "&type=STOP_MARKET" + "&positionSide=SHORT" + "&timestamp=" + time.toString();     // 双向持仓
    } else {
        param = "symbol=" + symbol + "&closePosition=true" + "&stopPrice=" + price.toString() + "&side=BUY" + "&type=STOP_MARKET" + "&positionSide=SHORT" + "&timestamp=" + time.toString();              // 仓位止盈止损
    }
    var ret = exchanges[num].IO("api", "POST", "/fapi/v1/order", param);
    Log(exchanges[num].GetLabel(), ": 挂空单止损:", ret);
    return true;
}

// 挂空单止盈
function shortTakeProfit(num, symbol, price, size) {
    var time = UnixNano() / 1000000;
    var param = null;
    if (size != 0) {     
        param = "symbol=" + symbol + "&quantity=" + size.toString() + "&stopPrice=" + price.toString() + "&side=BUY" + "&type=TAKE_PROFIT_MARKET" + "&positionSide=SHORT" + "&timestamp=" + time.toString();     // 双向持仓
    } else {
        param = "symbol=" + symbol + "&closePosition=true" + "&stopPrice=" + price.toString() + "&side=BUY" + "&type=TAKE_PROFIT_MARKET" + "&positionSide=SHORT" + "&timestamp=" + time.toString();              // 仓位止盈止损
    }
    var ret = exchanges[num].IO("api", "POST", "/fapi/v1/order", param);
    Log(exchanges[num].GetLabel(), ": 挂空单止盈:", ret);
    return true;
}

// 是否有多单
function isHadLongPosition(position) {
    var position_size = 0;

    if (position) {
        if (position.length >= 1 && position[0].Type == PD_LONG && position[0].Amount > 0) {
            position_size = position[0].Amount;
        } else if (position.length == 2 && position[1].Type == PD_LONG && position[1].Amount > 0) {
            position_size = position[1].Amount;
        }
    }

    return position_size;
}

// 是否有空单
function isHadShortPosition(position) {
    var position_size = 0;

    if (position) {
        if (position.length >= 1 && position[0].Type == PD_SHORT && position[0].Amount > 0) {
            position_size = position[0].Amount;
        } else if (position.length == 2 && position[1].Type == PD_SHORT && position[1].Amount > 0) {
            position_size = position[1].Amount;
        }
    }

    return position_size;
}

// 从持仓列表中获取特定币种的持仓
function getPositionBySymbol(positions, symbol) {
    var index = -1;

    if (positions && positions.length > 0) {
        for (var i = 0; i < positions.length; i++) {
            if (positions[i][0].Symbol == symbol) {
                index = i;
                break;
            }
        }
    }

    return index == -1 ? null : positions[index];
}

// 获取所有持仓列表
function getAllPositionFromBinance(num) {
    var ret = exchanges[num].IO("api", "GET", "/fapi/v2/account");
    var positions = [];
    var i = 0;

    if (!ret || !ret.positions) {
        return null;
    }
    // 获取所有持仓
    for (i = 0; i < ret.positions.length; i++) {
        if (ret.positions[i].positionAmt != 0 && ret.positions[i].symbol.endsWith("USDT")) {
            positions.push([{
                Symbol: ret.positions[i].symbol.substring(0, ret.positions[i].symbol.lastIndexOf("USDT")) + "_USDT",
                Amount: Number(Math.abs(ret.positions[i].positionAmt)),
                FrozenAmount: 0,
                Price: Number(ret.positions[i].entryPrice),
                Profit: Number(ret.positions[i].unrealizedProfit),
                Type: ret.positions[i].positionAmt < 0 ? PD_SHORT : PD_LONG,
                ContractType: "swap",
                Margin: Number(ret.positions[i].positionInitialMargin),
                LevelRate: Number(ret.positions[i].leverage),
                UpdateTime: ret.positions[i].updateTime
            }]);
        }
    }
    // 合并相同币种的持仓(同一币种,多空双向持仓)
    if (positions.length >= 2) {
        for (i = 0; i < positions.length; i++) {
            for (var j = i + 1; j < positions.length; j++) {
                if (positions[i][0].Symbol == positions[j][0].Symbol) {
                    positions[i].push(JSON.parse(JSON.stringify(positions[j][0])));
                    positions.splice(j, 1);                     // 删除相同币种
                    break;
                }
            }
        }
    }

    return positions;
}

// 从跟单账号中寻找相同的挂单
function getTheSamePendigOrdersFromFollowOrders(symbol, type, price, stop_price, amount, follow_pending_orders) {
    var pending_order = [];

    if (follow_pending_orders && follow_pending_orders.length > 0) {
        for (var i = 0; i < follow_pending_orders.length; i++) {
            if (follow_pending_orders[i].Symbol == symbol && follow_pending_orders[i].Type == type
                && follow_pending_orders[i].Price == price && follow_pending_orders[i].StopPrice == stop_price && follow_pending_orders[i].Amount == amount) {
                pending_order.push(follow_pending_orders[i]);
            }
        }
    }

    return pending_order;
}

// 从主账号中寻找相同的挂单
function getTheSamePendigOrdersFromMainOrders(num, symbol, type, price, stop_price, amount, main_pending_orders) {
    var pending_order = [];
    var main_type = 0;
    var main_price = 0;
    var main_stop_price = 0;
    var main_amount = 0;

    if (main_pending_orders && main_pending_orders.length > 0) {
        for (var i = 0; i < main_pending_orders.length; i++) {
            [main_type, main_price, main_stop_price, main_amount] = getMainPendingOrdersInfo(num, main_pending_orders[i]);
            if (main_type == 0) {
                continue;       // 挂单类型没有正确识别,跳过
            }
            if (main_pending_orders[i].Symbol == symbol && main_pending_orders[i].Type == type
                && main_price == price && main_stop_price == stop_price && main_amount == amount) {
                pending_order.push(main_pending_orders[i]);
            }
        }
    }

    return pending_order;
}

// 获取所有挂单
function getAllPendingOrders(num) {
    var ret = exchanges[num].IO("api", "GET", "/fapi/v1/openOrders");
    var pending_orders = [];

    if (!ret || ret.length <= 0) {
        return null;
    }

    for (var i = 0; i < ret.length; i++) {
        var type = "";
        if (ret[i].stopPrice == "0") {
            if (ret[i].positionSide == "LONG") {
                type = ret[i].side == "BUY" ? "限价开多" : "限价平多";
            } else if (ret[i].positionSide == "SHORT") {
                type = ret[i].side == "SELL" ? "限价开空" : "限价平空";
            } else {
                type = "挂单类型错误";
            }
        } else {
            if (ret[i].origType == "TAKE_PROFIT_MARKET" || ret[i].origType == "TAKE_PROFIT") {
                if (ret[i].closePosition) {
                    type = ret[i].positionSide == "LONG" ? "多单仓位止盈" : "空单仓位止盈";
                } else {
                    type = ret[i].positionSide == "LONG" ? "多单止盈" : "空单止盈";
                }
            } else if (ret[i].origType == "STOP_MARKET" || ret[i].origType == "STOP") {
                if (ret[i].closePosition) {
                    type = ret[i].positionSide == "LONG" ? "多单仓位止损" : "空单仓位止损";
                } else {
                    type = ret[i].positionSide == "LONG" ? "多单止损" : "空单止损";
                }
            } else {
                type = "挂单类型错误";
            }
        }
        pending_orders.push({
            OrderId: ret[i].orderId,
            Symbol: ret[i].symbol.substring(0, ret[i].symbol.lastIndexOf("USDT")) + "_USDT",
            Price: Number(ret[i].price),
            Amount: Number(ret[i].origQty),
            DealAmount: Number(ret[i].executedQty),
            Type: type,
            StopPrice: Number(ret[i].stopPrice),
            Time: ret[i].time,
        });
    }

    return pending_orders;
}

// 根据主账号挂单,重新计算下单类型、价格、止损价格和数量
function getMainPendingOrdersInfo(num, main_pending_order) {
    var order_type = 0;
    var order_size = main_pending_order.Amount * _FollowOrderSizePercent[num - 1];
    
    if (main_pending_order.Type == "限价开多") {
        order_type = 1;
    } else if (main_pending_order.Type == "限价开空") {
        order_type = 2;
    } else if (main_pending_order.Type == "限价平多") {
        order_type = 3;
    } else if (main_pending_order.Type == "限价平空") {
        order_type = 4;
    } else if (main_pending_order.Type == "多单止损" || main_pending_order.Type == "多单仓位止损") {
        order_type = 5;
        order_size = main_pending_order.Type == "多单仓位止损" ? 0 : order_size;
    } else if (main_pending_order.Type == "空单止损" || main_pending_order.Type == "空单仓位止损") {
        order_type = 6;
        order_size = main_pending_order.Type == "空单仓位止损" ? 0 : order_size;
    } else if (main_pending_order.Type == "多单止盈" || main_pending_order.Type == "多单仓位止盈") {
        order_type = 7;
        order_size = main_pending_order.Type == "多单仓位止盈" ? 0 : order_size;
    } else if (main_pending_order.Type == "空单止盈" || main_pending_order.Type == "空单仓位止盈") {
        order_type = 8;
        order_size = main_pending_order.Type == "空单仓位止盈" ? 0 : order_size;
    } else {
        Log("挂单类型识别有误.");
    }

    var price_precision = 2;
    var amount_precision = 2;
    [price_precision, amount_precision] = getOrderPrecisionFromBinance(main_pending_order.Symbol);

    return [order_type, _N(main_pending_order.Price, price_precision), _N(main_pending_order.StopPrice, price_precision), _N(order_size, amount_precision)];
}

// 手动平仓
function closePositionByIndex(i, j, k) {
    if (i <= 0) {
        return;     // 避免出错对主账号进行平仓
    }
    exchanges[i].SetCurrency(_Positions[i][j][k].Symbol);
    var price_precision = 2;
    var amount_precision = 0;
    [price_precision, amount_precision] = getOrderPrecisionFromBinance(_Positions[i][j][k].Symbol);
    exchanges[i].SetPrecision(price_precision, amount_precision);
    if (_Positions[i][j][k].Type == PD_LONG) {
        coverLong(i, -1, _Positions[i][j][k].Amount);
        Log(exchanges[i].GetLabel(), ":", _Positions[i][j][k].Symbol, ":手动平多单.");
    } else {
        coverShort(i, -1, _Positions[i][j][k].Amount);
        Log(exchanges[i].GetLabel(), ":", _Positions[i][j][k].Symbol, ":手动平空单.");
    }
    calculateProfit(i);
}

// 手动撤单
function cancelOrderByIndex(i, j) {
    if (i > 0) {
        Log(exchanges[i].GetLabel(), ":", _PendingOrders[i][j].Symbol, ":手动撤单");
        exchanges[i].SetCurrency(_PendingOrders[i][j].Symbol);
        exchanges[i].CancelOrder(_PendingOrders[i][j].OrderId, _PendingOrders[i][j]);
    }
}

// 修正币安个别币种返回的价格精度有误
function fixedTheErrorPricePrecision(symbol, price_precision, amount_precision) {
    var real_price_precision = price_precision;
    var real_amount_precison = amount_precision;

    if (symbol == "SOL_USDT" || symbol == "SRM_USDT") {
        real_price_precision = 3;
        real_amount_precison = 0;
    } else if (symbol == "ADA_USDT") {
        real_price_precision = 4;
        real_amount_precison = 0;
    }

    return [real_price_precision, real_amount_precison];
}

// 获取币安的交易信息,保存到本地,策略启动时候运行一次
function getBinanceExchangeInfo() {
    var ret = exchange.IO("api", "GET", "/fapi/v1/exchangeInfo");

    if (!ret || !ret.symbols || ret.symbols.length == 0) {
        throw "币安的交易信息获取失败!";
    }

    _BinanceExchangeInfo = ret;
}

// 获取币安下单价格和数量的精度
function getOrderPrecisionFromBinance(symbol) {
    var symbol_param = symbol.replace("_", "");
    // var ret = exchange.IO("api", "GET", "/fapi/v1/exchangeInfo");
    var ret = _BinanceExchangeInfo;
    var amount_precision = 2;
    var price_precision = 2;

    for (i = 0; i < ret.symbols.length; i++) {
        if (ret.symbols[i].symbol == symbol_param) {
            price_precision = ret.symbols[i].pricePrecision;
            amount_precision = ret.symbols[i].quantityPrecision;
            break;
        }
    }
    [price_precision, amount_precision] = fixedTheErrorPricePrecision(symbol, price_precision, amount_precision);

    return [price_precision, amount_precision];
}

// 获取一个币种(双向)持仓下的同向持仓的下标
function getPositionIndexByType(type, position) {
    var position_index = -1;
    if (position) {
        for (var i = 0; i < position.length; i++) {
            if (position[i].Type == type) {
                position_index = i;
                break;
            }
        }
    }
    return position_index;
}

// 检查下单金额是否满足下单
function checkNotionalAndBalance(num, price, size, level_rate) {
    var real_price = price == -1 ? _C(exchanges[num].GetTicker).Last : price;
    var notional = real_price * size;
    var margin_needed = notional / level_rate;

    if (_Accounts[num].Balance < margin_needed) {
        if (_EnableLogPrint)
            Log(exchanges[num].GetLabel(), ":保证金余额不足,无法下单。");
        return false;
    } else if (notional < _MinimOrderCash) {
        if (_EnableLogPrint)
            Log(exchanges[num].GetLabel(), ":下单金额小于最小下单金额,无法下单。");
        return false;
    }

    return true;
}

// 计算最终的下单价格和数量
function getRealOrderPriceAndOrderSize(num, symbol, type, price, size, position) {
    var real_order_size = _FollowOrderSize ? size : _OrderSize[num - 1];
    var position_size = 0;

    switch (type) {
        case 3:     // 平多
        case 5:     // 多单止损
        case 7:     // 多单止盈
            position_size = isHadLongPosition(position);
            real_order_size = (real_order_size > position_size) ? position_size : real_order_size;
            break;
        case 4:     // 平空
        case 6:     // 空单止损
        case 8:     // 空单止盈
            position_size = isHadShortPosition(position);
            real_order_size = (real_order_size > position_size) ? position_size : real_order_size;
            break;
        default:
            break;
    }

    // 设置下单价格和数量精度
    var price_precision = 2;
    var amount_precision = 0;
    [price_precision, amount_precision]  = getOrderPrecisionFromBinance(symbol);
    exchanges[num].SetPrecision(price_precision, amount_precision);
    real_order_size = real_order_size < _MinimOrderSize ? 0 : real_order_size;

    return [_N(price, price_precision), _N(real_order_size, amount_precision), position_size];
}

// 下单
function trade(num, symbol, type, level_rate, price, size, position) {
    var real_level_rate = _FollowMarginLevel ? level_rate : _MarginLevel;
    exchanges[num].SetCurrency(symbol);
    if (real_level_rate != -1) {
        exchanges[num].SetMarginLevel(real_level_rate);
    }

    var real_order_price = 0;
    var real_order_size = 0;
    var position_size = 0;
    [real_order_price, real_order_size, position_size] = getRealOrderPriceAndOrderSize(num, symbol, type, price, size, position);
    var symbol_param = symbol.replace("_", "");
    if (_EnableLogPrint)
        Log(exchanges[num].GetLabel(), ":", symbol, " real_order_price = ", real_order_price, " real_order_size = ", real_order_size, "real_level_rate = ", real_level_rate, "position_size = ", position_size, "#FF1CAE");

    switch (type) {
        case 1:     // 开多
            if (real_order_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, ":下单数量为0,不下单。");
                break;
            }
            if (!checkNotionalAndBalance(num, price, real_order_size, real_level_rate)) {
                break;
            }
            openLong(num, real_order_price, real_order_size);
            Log(exchanges[num].GetLabel(), ":", symbol, "开多:", real_order_price, " 数量:", real_order_size);
            break;
        case 2:     // 开空
            if (real_order_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, ":下单数量为0,不下单。");
                break;
            }
            if (!checkNotionalAndBalance(num, price, real_order_size, real_level_rate)) {
                break;
            }
            openShort(num, real_order_price, real_order_size);
            Log(exchanges[num].GetLabel(), ":", symbol, "开空:", real_order_price, " 数量:", real_order_size);
            break;
        case 3:     // 平多
            if (real_order_size == 0 || position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, ":没有多单持仓,不做平仓。");
                break;
            }
            if (coverLong(num, real_order_price, real_order_size)) {
                calculateProfit(num);
                Log(exchanges[num].GetLabel(), ":", symbol, "平多:", real_order_price, " 数量:", real_order_size);
            }
            break;
        case 4:     // 平空
            if (real_order_size == 0 || position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, ":没有空单持仓,不做平仓。");
                break;
            }
            if (coverShort(num, real_order_price, real_order_size)) {
                calculateProfit(num);
                Log(exchanges[num].GetLabel(), ":", symbol, "平空:", real_order_price, " 数量:", real_order_size);
            }
            break;
        case 5:     // 挂多单止损
            if (position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, "没有持仓,不挂多单止损。");
                break;
            }
            if (longStopLoss(num, symbol_param, real_order_price, real_order_size)) {
                Log(exchanges[num].GetLabel(), ":", symbol, "挂多单止损:", real_order_price, " 数量:", real_order_size);
            }
            break;
        case 6:     // 挂空单止损
            if (position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, "没有持仓,不挂空单止损。");
                break;
            }
            if (shortStopLoss(num, symbol_param, real_order_price, real_order_size)) {
                Log(exchanges[num].GetLabel(), ":", symbol, "挂空单止损:", real_order_price, " 数量:", real_order_size);
            }
            break;
        case 7:     // 挂多单止盈
            if (position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, "没有持仓,不挂多单止盈。");
                break;
            }
            if (longTakeProfit(num, symbol_param, real_order_price, real_order_size)) {
                Log(exchanges[num].GetLabel(), ":", symbol, "挂多单止盈:", real_order_price, " 数量:", real_order_size);
            }
            break;
        case 8:     // 挂空单止盈
            if (position_size == 0) {
                if (_EnableLogPrint)
                    Log(exchanges[num].GetLabel(), ":", symbol, "没有持仓,不挂空单止盈.");
                break;
            }
            if (shortTakeProfit(num, symbol_param, real_order_price, real_order_size)) {
                Log(exchanges[num].GetLabel(), ":", symbol, "挂空单止盈:", real_order_price, " 数量:", real_order_size);
            }
            break;
        default:
            if (_EnableLogPrint)
                Log(exchanges[num].GetLabel(), ":", symbol, " trade:下单类型有误!", "@");
            break;
    }
}

// 持仓跟随
function followPositions(num, main_positions, follow_positions) {
    var i = 0;
    var j = 0;
    var found_position = null;
    var order_size = 0;
    var order_type = 0;
    var is_ordered = false;
    var position_index = -1;
    var current_time = getCurrentTime();

    // 首次开仓(市价单)、加减仓(市价单)
    for (i = 0; main_positions && i < main_positions.length; i++) {
        found_position = getPositionBySymbol(follow_positions, main_positions[i][0].Symbol);
        // 跟单账号没有该币种的持仓,开仓
        // 跟单账号有该币种的持仓,但是没有持仓方向一致的仓位,需要开仓
        is_ordered = false;
        for (j = 0; main_positions[i] && j < main_positions[i].length; j++) {
            if (getPositionIndexByType(main_positions[i][j].Type, found_position) == -1) {
                // 先判断持仓的更新时间,如果持仓更新时间已经超过最大跟单间隔,则不跟单
                if ((current_time - main_positions[i][j].UpdateTime) / 1000 > _MaxFollowInterval) {
                    continue;
                }
                order_size = main_positions[i][j].Amount * _FollowOrderSizePercent[num - 1];        // 根据跟单百分比计算出实际下单张数
                trade(num, main_positions[i][j].Symbol, main_positions[i][j].Type == PD_LONG ? 1 : 2, main_positions[i][j].LevelRate,
                    -1, order_size, found_position);
                is_ordered = true;
            }
        }
        if (is_ordered) {
            continue;   // 在同一周期同一个币种,不要同时开仓和加减仓
        }
        if (found_position) {
            // 跟单账号有该币种的持仓,且方向一致,加减仓
            for (j = 0; main_positions[i] && j < main_positions[i].length; j++) {
                // 先判断持仓的更新时间,如果持仓更新时间已经超过最大跟单间隔,则不跟单
                if ((current_time - main_positions[i][j].UpdateTime) / 1000 > _MaxFollowInterval) {
                    continue;
                }
                position_index = getPositionIndexByType(main_positions[i][j].Type, found_position);
                order_size = main_positions[i][j].Amount * _FollowOrderSizePercent[num - 1] - found_position[position_index].Amount;
                if (Math.abs(order_size) < _MinimOrderSize) {   // 小于最小下单数量,不下单
                    continue;
                }
                order_type = order_size > 0 ? (main_positions[i][j].Type == PD_LONG ? 1 : 2) : (main_positions[i][j].Type == PD_LONG ? 3 : 4);  // 根据持仓数量来判断是加仓还是减仓
                trade(num, main_positions[i][j].Symbol, order_type, main_positions[i][j].LevelRate, -1, Math.abs(order_size), found_position);
            }
        }
    }
    // 平仓(市价单)
    for (i = 0; follow_positions && i < follow_positions.length; i++) {
        found_position = getPositionBySymbol(main_positions, follow_positions[i][0].Symbol);
        // 主账号没有该币种的持仓,平仓
        // 主账号有该币种的持仓,但是持仓方向不一致,平仓
        for (j = 0; follow_positions[i] && j < follow_positions[i].length; j++) {
            if (getPositionIndexByType(follow_positions[i][j].Type, found_position) == -1) {
                trade(num, follow_positions[i][j].Symbol, follow_positions[i][j].Type == PD_LONG ? 3 : 4, follow_positions[i][j].LevelRate,
                    -1, follow_positions[i][j].Amount, follow_positions[i]);
            }
        }
    }
}

// 挂单跟随
function followPendingOrders(num, main_pending_orders, follow_pending_orders, follow_positions) {
    var i = 0;
    var order_size = 0;
    var order_type = 0;
    var order_price = 0;
    var stop_price = 0;
    var found_pending_order = null;
    var found_position = null;
    var current_time = getCurrentTime();

    // 挂单
    for (i = 0; main_pending_orders && i < main_pending_orders.length; i++) {
        // 先判断挂单时间,如果挂单时间已经超过最大跟单间隔,则不跟单
        if ((current_time - main_pending_orders[i].Time) / 1000 > _MaxFollowInterval) {
            continue;
        }
        // 接着把主账号中的挂单类型、价格、止损价格和数量,根据跟单百分重新计算,并格式价格和数量的精度
        [order_type, order_price, stop_price, order_size] = getMainPendingOrdersInfo(num, main_pending_orders[i]);
        if (order_type == 0) {
            continue;       // 挂单类型没有正确识别,不挂单
        }
        // 寻找跟单账号中币种、类型、价格、数量相同的挂单
        found_pending_order = getTheSamePendigOrdersFromFollowOrders(main_pending_orders[i].Symbol, main_pending_orders[i].Type, order_price, stop_price, order_size, follow_pending_orders);
        if (found_pending_order.length == 0) {
            // 没有找到,跟随挂单
            found_position = getPositionBySymbol(follow_positions, main_pending_orders[i].Symbol);
            trade(num, main_pending_orders[i].Symbol, order_type, -1, stop_price != 0 ? stop_price : order_price, order_size, found_position);
        }
    }
    // 撤单
    for (i = 0; follow_pending_orders && i < follow_pending_orders.length; i++) {
        // 寻找主账号中币种、类型、价格、数量一致的挂单
        found_pending_order = getTheSamePendigOrdersFromMainOrders(num, follow_pending_orders[i].Symbol, follow_pending_orders[i].Type, follow_pending_orders[i].Price, follow_pending_orders[i].StopPrice,
            follow_pending_orders[i].Amount, main_pending_orders);
        if (found_pending_order.length == 0) {
            // 在主账号中没有该挂单,开始撤单
            if (_EnableLogPrint)
                Log(exchanges[num].GetLabel(), ":", follow_pending_orders[i].Symbol, ", ", follow_pending_orders[i].Type, ":主账号没有该挂单,开始撤单.");
            exchanges[num].SetCurrency(follow_pending_orders[i].Symbol);
            exchanges[num].CancelOrder(follow_pending_orders[i].OrderId, follow_pending_orders[i]);
        }
    }
}

// 跟单
function followOrders(num) {
    if (num <= 0) {
        return;
    }
    var main_positions = _Positions[0];
    var main_pending_orders = _PendingOrders[0];
    var follow_positions = _Positions[num];
    var follow_pending_orders = _PendingOrders[num];

    followPositions(num, main_positions, follow_positions);
    followPendingOrders(num, main_pending_orders, follow_pending_orders, follow_positions);
}

// 执行命令
function runCmd() {
    var cmd = GetCommand();
    if (cmd) {
        // 检测交互命令
        Log("接收到的命令:", cmd, "#FF1CAE");
        if (cmd.indexOf("ClearLocalData:") == 0) {
            // 清除本地数据
            var user_num = cmd.replace("ClearLocalData:", "");
            clearUserDataLocal(Number(user_num));
        } else if (cmd.indexOf("SaveLocalData:") == 0) {
            // 保存数据到本地
            saveUserDatasLocal();
        } else if (cmd.indexOf("ClearLog:") == 0) {
            // 清除日志
            var log_reserve = cmd.replace("ClearLog:", "");
            LogReset(Number(log_reserve));
        } else if (cmd.indexOf("ClosePosition:") == 0) {
            // 手动平仓
            var close_cmd = cmd.replace("ClosePosition:", "");
            var index = close_cmd.split(",");
            closePositionByIndex(Number(index[0]), Number(index[1]), Number(index[2]));
        } else if (cmd.indexOf("CancelOrder:") == 0) {
            // 手动撤单
            var cancel_cmd = cmd.replace("CancelOrder:", "");
            var cancel_index = cancel_cmd.split(",");
            cancelOrderByIndex(Number(cancel_index[0]), Number(cancel_index[1]));
        } else if (cmd.indexOf("LogPrint:") == 0) {
            // (部分)日志打印开关
            var log_print_cmd = Number(cmd.replace("LogPrint:", ""));
            if (log_print_cmd == 1) {
                _EnableLogPrint = true;
                Log("已打开(部分)日志打印。");
            } else {
                _EnableLogPrint = false;
                Log("已关闭(部分)日志打印。");
            }
        }
    }
}

// 跟单机器人主函数
function trackOrderRobot() {
    // 获取所有账号的持仓信息和挂单信息
    for (var i = 0; i < exchanges.length; i++) {
        _Positions[i] = getAllPositionFromBinance(i); 
        _PendingOrders[i] = getAllPendingOrders(i);
        [_CurrentAssets[i], _Accounts[i]] = getTotalEquityBinance(i);
        // 跟单
        followOrders(i);
        // 自动移出资产
        autoTransfer(_CurrentAssets[i], 100);
    }
    // 打印状态栏信息
    printLogStatus();
    // 执行命令
    runCmd();
}

// 打印状态栏信息
function printLogStatus() {
    // 打印持仓信息和账户信息
    var table_overview = { type: 'table', title: '策略总览', cols: ['开始时间', '已运行天数', '交易次数', '胜率', '预估月化%', '预估年化%', '策略代写请联系微信'], rows: [] };
    var table_main_position = { type: 'table', title: '主账号持仓', cols: ['序号', '持仓更新时间', '币种', '持仓均价', '方向', '杠杆倍数', '数量', '保证金', '浮动盈亏', '浮动盈亏%'], rows: []};
    var table_main_orders = { type: 'table', title: '主账号挂单', cols: ['序号', '挂单时间', '币种', '类型', '限价价格', '触发价格', '数量', '成交数量'], rows: [] };
    var table_follow_accouts = [];
    var table_follow_positions = [];
    var table_follow_orders = [];
    var i = 0;
    var j = 0;
    var k = 0;
    var serial_number = 1;
    var position_direction = "";
    var position_profit_percent = 0;
    var is_had_position = false;
    var is_had_order = false;

    // 策略总览
    var the_running_days = getDaysFromTimeStamp(_StrategyDatas.start_run_timestamp, Unix());
    var monthly_rate_of_profit = 0;
    var annualized_rate_of_profit = 0;
    if (the_running_days > 1) {
        monthly_rate_of_profit = _ProfitLocal[0] / _InitAsset[0] / the_running_days * 30;
        annualized_rate_of_profit = monthly_rate_of_profit * 12;
    }
    var win_rate = _TradeCount[0] == 0 ? 0 : _TakeProfitCount[0] / _TradeCount[0];
    table_overview.rows.push([_D(_StrategyDatas.start_run_timestamp * 1000), the_running_days, _TradeCount[0], _N(win_rate * 100, 2) + "%",
        _N(monthly_rate_of_profit * 100, 2) + "%", _N(annualized_rate_of_profit * 100, 2) + "%", 'wd1061331106']);

    // 主账号持仓
    if (_Positions.length > 0 && _Positions[0] && _Positions[0].length > 0) {
        for (i = 0; i < _Positions[0].length; i++) {
            for (j = 0; j < _Positions[0][i].length; j++) {
                position_direction = _Positions[0][i][j].Type == PD_LONG ? "多单" : "空单";
                position_profit_percent = _Positions[0][i][j].Profit / _Positions[0][i][j].Margin;
                table_main_position.rows.push([serial_number++, _D(_Positions[0][i][j].UpdateTime), _Positions[0][i][j].Symbol, _N(_Positions[0][i][j].Price, 4), position_direction,
                _N(_Positions[0][i][j].LevelRate, 0), _N(_Positions[0][i][j].Amount, 4), _N(_Positions[0][i][j].Margin, 4), _N(_Positions[0][i][j].Profit, 4), _N(position_profit_percent * 100, 2) + "%"]);
                is_had_position = true;
            }
        }
    }
    if (!is_had_position) {
        table_main_position.rows.push(["-", "-", "无持仓", "-", "-", "-", "-", "-", "-", "-"]);
    }
    // 主账号挂单
    if (_PendingOrders.length > 0 && _PendingOrders[0] && _PendingOrders[0].length > 0) {
        serial_number = 1;
        for (i = 0; i < _PendingOrders[0].length; i++) {
            table_main_orders.rows.push([serial_number++, _D(_PendingOrders[0][i].Time), _PendingOrders[0][i].Symbol, _PendingOrders[0][i].Type, _N(_PendingOrders[0][i].Price, 4),
                _N(_PendingOrders[0][i].StopPrice, 4), _N(_PendingOrders[0][i].Amount, 4), _N(_PendingOrders[0][i].DealAmount, 4)]);
                is_had_order = true;
        }
    }
    if (!is_had_order) {
        table_main_orders.rows.push(["-", "-", "无挂单", "-", "-", "-", "-", "-"]);
    }

    var table_account = { type: 'table', title: '账户资金', cols: ['当前资产', '初始资产', '已移出资产', '可用余额', '冻结余额', '收益', '收益%'], rows: [] };
    var table_position = { type: 'table', title: '持仓情况', cols: ['序号', '币种', '持仓均价', '方向', '杠杆倍数', '数量', '保证金', '浮动盈亏', '浮动盈亏%', '操作'], rows: [] };
    var table_orders = { type: 'table', title: '挂单列表', cols: ['序号', '挂单时间', '币种', '类型', '限价价格', '触发价格', '数量', '成交数量', '操作'], rows: [] };
    var asset_profit = 0;
    var asset_profit_percent = 0;
    for (i = 1; i < exchanges.length; i++) {
        // 跟单账户资金
        asset_profit = _CurrentAssets[i] + _TransferAmount[i - 1] - _InitAsset[i - 1];
        asset_profit_percent = asset_profit / _InitAsset[i - 1];
        table_account.title = exchanges[i].GetLabel() + "资金";
        table_account.rows = [];
        table_account.rows.push([_N(_CurrentAssets[i], 4), _N(_InitAsset[i - 1], 4), _N(_TransferAmount[i - 1], 4), _N(_Accounts[i].Balance, 4), 
            _N(_Accounts[i].FrozenBalance, 4), _N(asset_profit, 4), _N(asset_profit_percent * 100, 2) + "%"]);
        table_follow_accouts.push(JSON.parse(JSON.stringify(table_account)));
        // 跟单账户持仓
        table_position.title = exchanges[i].GetLabel() + "持仓";
        table_position.rows = [];
        is_had_position = false;
        if (_Positions.length > 1 && _Positions[i] && _Positions[i].length > 0) {
            serial_number = 1;
            for (j = 0; j < _Positions[i].length; j++) {
                for (k = 0; k < _Positions[i][j].length; k++) {
                    position_direction = _Positions[i][j][k].Type == PD_LONG ? "多单" : "空单";
                    position_profit_percent = _Positions[i][j][k].Profit / _Positions[i][j][k].Margin;
                    table_position.rows.push([serial_number++, _Positions[i][j][k].Symbol, _N(_Positions[i][j][k].Price, 4), position_direction, _N(_Positions[i][j][k].LevelRate, 0),
                        _N(_Positions[i][j][k].Amount, 4), _N(_Positions[i][j][k].Margin, 4), _N(_Positions[i][j][k].Profit, 4), _N(position_profit_percent * 100, 2) + "%", {
                            'type': 'button',
                            'cmd': "ClosePosition:" + i + "," + j + "," + k,
                            'name': '平仓'
                            }
                        ]);
                    is_had_position = true;
                }
            }
        }
        if (!is_had_position) {
            table_position.rows.push(["-", "无持仓", "-", "-", "-", "-", "-", "-", "-", "-"]);
        }
        table_follow_positions.push(JSON.parse(JSON.stringify(table_position)));
        // 跟单账户挂单
        table_orders.title = exchanges[i].GetLabel() + "挂单";
        table_orders.rows = [];
        is_had_order = false;
        if (_PendingOrders.length > 0 && _PendingOrders[i] && _PendingOrders[i].length > 0) {
            serial_number = 1;
            for (j = 0; j < _PendingOrders[i].length; j++) {
                table_orders.rows.push([serial_number++, _D(_PendingOrders[i][j].Time), _PendingOrders[i][j].Symbol, _PendingOrders[i][j].Type, _N(_PendingOrders[i][j].Price, 4),
                    _N(_PendingOrders[i][j].StopPrice, 4), _N(_PendingOrders[i][j].Amount, 4), _N(_PendingOrders[i][j].DealAmount, 4), {
                    'type': 'button',
                    'cmd': "CancelOrder:" + i + "," + j,
                    'name': '撤销'
                }
                ]);
                is_had_order = true;
            }
        } 
        if (!is_had_order) {
            table_orders.rows.push(["-", "-", "无挂单", "-", "-", "-", "-", "-", "-"]);
        }
        table_follow_orders.push(JSON.parse(JSON.stringify(table_orders)));
    }

    // 打印表格
    LogStatus('`' + JSON.stringify(table_overview) + '`\n'
        + '`' + JSON.stringify(table_main_position) + '`\n'
        + '`' + JSON.stringify(table_main_orders) + '`\n'
        + '`' + JSON.stringify(table_follow_accouts) + '`\n'
        + '`' + JSON.stringify(table_follow_positions) + '`\n'
        + '`' + JSON.stringify(table_follow_orders) + '`\n');
}

// 设置币安持仓模式
function setBinancePositionSide(num, dual) {
    var ret = null;
    
    ret = exchanges[num].IO("api", "GET", "/fapi/v1/positionSide/dual");
    if (dual != ret.dualSidePosition) {
        ret = exchanges[num].IO("api", "POST", "/fapi/v1/positionSide/dual", "dualSidePosition=" + dual + "&timestamp=" + (UnixNano() / 1000000).toString());
        if (ret && ret.msg == "success") {
            Log(exchanges[num].GetLabel(), dual ? ":已经修改为双向持仓模式." : ":已经修改为单向持仓模式.");
        } else {
            Log(exchanges[num].GetLabel(), dual ? ":设置双向持仓模式失败,请手动设置." : ":设置单向持仓模式失败,请手动设置.");
        }
    }
}

// 设置合约
function setContract() {
    for (var i = 0; i < exchanges.length; i++) {
        exchanges[i].SetContractType("swap");
        // 设置双向持仓模式
        if (i >= 1) {
            setBinancePositionSide(i, true);
        }
    }
}

// 初始化数据
function initDatas() {
    _OrderSize = OrderSize.split(",");
    _FollowOrderSizePercent = FollowOrderSizePercent.split(",");
    _AccountMaxBalance = AccountMaxBalance.split(",");
    if (_OrderSize.length < (exchanges.length - 1) 
        || _FollowOrderSizePercent.length < (exchanges.length - 1)
        || (_AccountMaxBalance.length < (exchanges.length - 1) && _UseAutoTransfer)) {
        throw "下单张数(默认)或者跟单张数百分比或者自动转移资产的参数设置有误!";
    }

    saveStrategyRunTime();
    readUserDataLocal();
    for (var i = 0; i < exchanges.length - 1; i++) {
        _OrderSize[i] = Number(_OrderSize[i]);
        _FollowOrderSizePercent[i] = Number(_FollowOrderSizePercent[i]) / 100;
        _AccountMaxBalance[i] = Number(_AccountMaxBalance[i]);
        _UserStartTime[i] = _UserDatas[i].start_time;
        _InitAsset[i] = _UserDatas[i].init_assets;
        _ProfitLocal[i] = _UserDatas[i].profit_local;
        _TakeProfitCount[i] = _UserDatas[i].take_profit_count;
        _TradeCount[i] = _UserDatas[i].trade_count;
        _TransferAmount[i] = _UserDatas[i].transfer_amount;
    }

    getBinanceExchangeInfo();
}

// main
function main() {
    setContract();
    initDatas();
    while (true) {
        trackOrderRobot();
        Sleep(_Interval);
    }
}

More