avatar of 我想量化躺着挣钱 我想量化躺着挣钱
关注 私信
9
关注
6
关注者

永续无限合约网格多空双向持仓下单精度和执行交易混乱的问题,弄了半个月了实在不会了,有偿求解决办法。各位大佬

创建于: 2025-12-08 11:00:34, 更新于: 2025-12-08 11:27:50
comments   11
hits   104

”`js /*backtest start: 2024-01-01 00:00:00 end: 2024-12-31 23:59:00 period: 1m basePeriod: 1m exchanges: [{“eid”:“OKX”,“currency”:“BTC_USDT”,“balance”:10000}] */

// ==================== FMZ平台参数配置(从界面获取,禁止硬编码)==================== // 注意:FMZ平台中,界面设置的参数名称会自动成为JavaScript全局变量 // 直接使用变量名即可访问参数值,不需要使用GetConfig()函数 // 界面参数名必须与代码中的变量名一致 function getConfigParams() { var config = { // 币种选择(从FMZ界面选择,界面参数名:symbol) symbol: symbol || “BTC/USDT”,

    // 网格参数(全部从界面输入,禁止硬编码)
    longGridSpacing: parseFloat(longGridSpacing) || 1.0,      // 多头网格间距(%),界面参数名:longGridSpacing
    shortGridSpacing: parseFloat(shortGridSpacing) || 1.0,     // 空头网格间距(%),界面参数名:shortGridSpacing
    orderAmount: parseFloat(orderAmount) || 100,               // 单个网格下单金额,界面参数名:orderAmount
    upperPrice: parseFloat(upperPrice),                        // 网格上边界价格,界面参数名:upperPrice
    lowerPrice: parseFloat(lowerPrice),                        // 网格下边界价格,界面参数名:lowerPrice

    // 可选:手动覆盖最小步长/数量精度(界面参数名:forceMinLotSize, forceAmountPrecision)
    forceMinLotSize: forceMinLotSize ? parseFloat(forceMinLotSize) : null,
    forceAmountPrecision: forceAmountPrecision ? parseInt(forceAmountPrecision, 10) : null,
};

// ❌ 禁止硬编码示例:
// config.longGridSpacing = 1.0;  // 错误!
// config.orderAmount = 100;       // 错误!

return config;

}

// ==================== 状态持久化修正版 ==================== // 使用FMZ平台的_G函数实现状态持久化 // 关机或重启后可以自动恢复状态

function saveState() { /** 保存策略状态到全局存储 */ try { var state = { // 多头相关 MANY_NEXT_BUY_PRICE: MANY_NEXT_BUY_PRICE, MANY_ORDER_LIST: MANY_ORDER_LIST,

        // 空头相关
        SHORT_NEXT_BUY_PRICE: SHORT_NEXT_BUY_PRICE,
        SHORT_ORDER_LIST: SHORT_ORDER_LIST,

        // 状态信息相关
        INIT_TIME: INIT_TIME,
        INITIAL_BALANCE: INITIAL_BALANCE,

        // 配置信息(用于参数迁移检测)
        config: config,

        // 收益历史(保留最近1000条)
        PROFIT_HISTORY: PROFIT_HISTORY.slice(-1000),

        // 参数迁移相关
        oldConfig: oldConfig,
        isMigrating: isMigrating,

        // 时间戳
        lastSaveTime: Unix()
    };

    // ✅ 修正:使用正确的FMZ全局存储函数
    _G("strategy_state", JSON.stringify(state));
    return true;
} catch (e) {
    Log("保存状态失败:", e);
    return false;
}

}

function loadState() { /** 从全局存储恢复策略状态 */ try { // ✅ 修正:使用正确的FMZ全局存储函数 var stateStr = _G(“strategy_state”); if (!stateStr) { Log(“未找到保存的状态,使用初始状态”); return false; }

    var state = JSON.parse(stateStr);

    // 恢复多头相关
    if (state.MANY_NEXT_BUY_PRICE !== undefined) {
        MANY_NEXT_BUY_PRICE = state.MANY_NEXT_BUY_PRICE;
    }
    if (state.MANY_ORDER_LIST && Array.isArray(state.MANY_ORDER_LIST)) {
        MANY_ORDER_LIST = state.MANY_ORDER_LIST;
    }

    // 恢复空头相关
    if (state.SHORT_NEXT_BUY_PRICE !== undefined) {
        SHORT_NEXT_BUY_PRICE = state.SHORT_NEXT_BUY_PRICE;
    }
    if (state.SHORT_ORDER_LIST && Array.isArray(state.SHORT_ORDER_LIST)) {
        SHORT_ORDER_LIST = state.SHORT_ORDER_LIST;
    }

    // 恢复状态信息
    if (state.INIT_TIME) {
        INIT_TIME = state.INIT_TIME;
    }
    if (state.INITIAL_BALANCE) {
        INITIAL_BALANCE = state.INITIAL_BALANCE;
    }

    // 恢复收益历史
    if (state.PROFIT_HISTORY && Array.isArray(state.PROFIT_HISTORY)) {
        PROFIT_HISTORY = state.PROFIT_HISTORY;
    }

    // 恢复参数迁移状态
    if (state.oldConfig) {
        oldConfig = state.oldConfig;
    }
    if (state.isMigrating !== undefined) {
        isMigrating = state.isMigrating;
    }

    Log("状态恢复成功 - 多头持仓数:", MANY_ORDER_LIST.length, "空头持仓数:", SHORT_ORDER_LIST.length);
    if (state.lastSaveTime) {
        var downtime = Unix() - state.lastSaveTime;
        Log("上次保存时间:", new Date(state.lastSaveTime * 1000).toLocaleString(), "停机时长:", Math.floor(downtime / 60), "分钟");
    }

    return true;
} catch (e) {
    Log("恢复状态失败:", e);
    return false;
}

}

function saveHistory(record) { /** 保存历史记录到全局存储 */ try { var historyKey = “strategy_history”; var historyStr = _G(historyKey); var history = [];

    if (historyStr) {
        try {
            history = JSON.parse(historyStr);
        } catch (e) {
            history = [];
        }
    }

    // 添加新记录
    record.timestamp = Unix();
    history.push(record);

    // 只保留最近5000条记录
    if (history.length > 5000) {
        history = history.slice(-5000);
    }

    _G(historyKey, JSON.stringify(history));
    return true;
} catch (e) {
    Log("保存历史记录失败:", e);
    return false;
}

}

function getHistory(limit) { /** 获取历史记录 */ try { var historyKey = “strategy_history”; var historyStr = _G(historyKey); if (!historyStr) { return []; }

    var history = JSON.parse(historyStr);
    limit = limit || 100;

    // 返回最近的记录
    return history.slice(-limit);
} catch (e) {
    Log("获取历史记录失败:", e);
    return [];
}

}

// ==================== 安全的状态保存 ==================== function safeSaveState() { /** 安全保存状态,带错误处理 */ try { if (saveState()) { LAST_SAVE_TIME = Unix(); return true; } return false; } catch (e) { Log(“安全保存状态失败:”, e); return false; } }

// ==================== 持仓对账 ==================== function reconcilePositions() { try { var positions = exchange.GetPosition(); if (!positions) { return; } var hasLong = false; var hasShort = false; for (var i = 0; i < positions.length; i++) { if (positions[i].Amount > 0) { if (positions[i].Type === 0) hasLong = true; if (positions[i].Type === 1) hasShort = true; } } // 防抖:连续多次空才清空本地 if (!hasLong && !hasShort) { RECONCILE_EMPTY_STREAK += 1; } else { RECONCILE_EMPTY_STREAK = 0; } var shouldClear = RECONCILE_EMPTY_STREAK >= RECONCILE_EMPTY_THRESHOLD;

    if (shouldClear) {
        if (!hasLong && MANY_ORDER_LIST.length > 0) {
            Log("对账:连续空结果,实际多头为空,清空本地多头持仓列表");
            MANY_ORDER_LIST = [];
            MANY_BUYING = false;
        }
        if (!hasShort && SHORT_ORDER_LIST.length > 0) {
            Log("对账:连续空结果,实际空头为空,清空本地空头持仓列表");
            SHORT_ORDER_LIST = [];
            SHORT_BUYING = false;
        }
        if ((!hasLong && MANY_ORDER_LIST.length === 0) || (!hasShort && SHORT_ORDER_LIST.length === 0)) {
            safeSaveState();
        }
    }
} catch (e) {
    Log("持仓对账失败:", e);
}

}

// ==================== 双轨制参数迁移 ==================== // 实现渐进式参数迁移,保持原有持仓的平仓逻辑,新开仓使用新参数

// ==================== 增强的参数迁移检测 ==================== function checkConfigChange(newConfig) { /** 检查配置是否发生变化,如果变化则启动参数迁移 */ if (!oldConfig) { // 首次运行,保存当前配置 oldConfig = JSON.parse(JSON.stringify(newConfig)); isMigrating = false; return false; }

// 详细检查参数变化
var configChanged = false;
var changes = [];

if (oldConfig.longGridSpacing !== newConfig.longGridSpacing) {
    configChanged = true;
    changes.push("多头间距: " + oldConfig.longGridSpacing + "% → " + newConfig.longGridSpacing + "%");
}
if (oldConfig.shortGridSpacing !== newConfig.shortGridSpacing) {
    configChanged = true;
    changes.push("空头间距: " + oldConfig.shortGridSpacing + "% → " + newConfig.shortGridSpacing + "%");
}
if (oldConfig.orderAmount !== newConfig.orderAmount) {
    configChanged = true;
    changes.push("下单金额: " + oldConfig.orderAmount + " → " + newConfig.orderAmount);
}
if (oldConfig.upperPrice !== newConfig.upperPrice) {
    configChanged = true;
    changes.push("上边界: " + oldConfig.upperPrice + " → " + newConfig.upperPrice);
}
if (oldConfig.lowerPrice !== newConfig.lowerPrice) {
    configChanged = true;
    changes.push("下边界: " + oldConfig.lowerPrice + " → " + newConfig.lowerPrice);
}

if (configChanged && !isMigrating) {
    Log("========== 🚀 检测到参数变化,启动双轨制迁移 ==========");
    changes.forEach(function(change) {
        Log("📝 " + change);
    });
    Log("🔄 迁移模式:旧持仓使用旧参数平仓,新开仓使用新参数");
    Log("📊 当前持仓 - 多头:", MANY_ORDER_LIST.length, "空头:", SHORT_ORDER_LIST.length);
    isMigrating = true;
    safeSaveState(); // 立即保存迁移状态
}

return configChanged;

}

function shouldUseOldConfig(side) { /** 判断是否应该使用旧配置(用于平仓逻辑) */ if (!isMigrating || !oldConfig) { return false; }

// 如果还有旧持仓,使用旧配置平仓
if (side === LONG_SIDE) {
    return MANY_ORDER_LIST.length > 0;
} else if (side === SHORT_SIDE) {
    return SHORT_ORDER_LIST.length > 0;
}

return false;

}

function getGridSpacing(side, isClose) { /** 获取网格间距(支持双轨制) */ if (isClose && shouldUseOldConfig(side)) { // 平仓时,如果有旧持仓,使用旧参数 return side === LONG_SIDE ? oldConfig.longGridSpacing : oldConfig.shortGridSpacing; } else { // 开仓时,或旧持仓已平完,使用新参数 return side === LONG_SIDE ? config.longGridSpacing : config.shortGridSpacing; } }

function getOrderAmount(isClose) { /** 获取下单金额(支持双轨制) */ if (isClose && (shouldUseOldConfig(LONG_SIDE) || shouldUseOldConfig(SHORT_SIDE))) { // 平仓时,如果有旧持仓,使用旧参数 return oldConfig.orderAmount; } else { // 开仓时,使用新参数 return config.orderAmount; } }

function checkMigrationComplete() { /** 检查参数迁移是否完成 */ if (!isMigrating) { return true; }

// 如果旧持仓已全部平完,迁移完成
var oldLongPositions = 0;
var oldShortPositions = 0;

// 这里需要根据实际持仓判断,简化处理:如果持仓列表为空,认为迁移完成
// 实际应该检查持仓是否是用旧参数开的
if (MANY_ORDER_LIST.length === 0 && SHORT_ORDER_LIST.length === 0) {
    Log("========== ✅ 参数迁移完成 ==========");
    oldConfig = null;
    isMigrating = false;
    safeSaveState(); // 保存迁移完成状态
    return true;
}

return false;

}

// ==================== 精度管理 ==================== function getDecimalPlaces(num) { /** 获取数字的小数位数 */ var str = num.toString(); if (str.indexOf(‘.’) > -1) { return str.split(‘.’)[1].length; } return 0; }

function getPrecision() { /** * 获取下单精度 * 1) 若已从规则获取 tickSz/lotSz,则直接使用 DEFAULT_PRICE_PREC / MIN_LOT_DECIMALS * 2) 否则从深度数据推断 * 3) 否则使用默认 */ if (DEFAULT_PRICE_PREC && MIN_LOT_DECIMALS) { return { pricePrecision: DEFAULT_PRICE_PREC, amountPrecision: forceAmountPrecision || MIN_LOT_DECIMALS }; } try { var depth = exchange.GetDepth(); if (depth && depth.Bids && depth.Bids.length > 0) { var bidPrice = depth.Bids[0].Price; var bidAmount = depth.Bids[0].Amount;

        var pricePrecision = getDecimalPlaces(bidPrice);
        var amountPrecision = getDecimalPlaces(bidAmount);

        Log("从深度数据获取精度 - 买一价格:", bidPrice, "价格精度:", pricePrecision, "买一数量:", bidAmount, "数量精度:", amountPrecision);

        return {
            pricePrecision: Math.max(1, pricePrecision || DEFAULT_PRICE_PREC || 1),
            amountPrecision: Math.max(MIN_LOT_DECIMALS, amountPrecision || MIN_LOT_DECIMALS)
        };
    }
} catch (e) {
    Log("从深度数据获取精度失败:", e);
}

Log("使用默认精度 - 价格精度:", DEFAULT_PRICE_PREC, "数量精度:", MIN_LOT_DECIMALS);
return {
    pricePrecision: DEFAULT_PRICE_PREC,
    amountPrecision: forceAmountPrecision || MIN_LOT_DECIMALS
};

}

function formatPrice(price, precision) { return parseFloat(price.toFixed(precision)); }

function formatAmount(amount, precision) { return parseFloat(amount.toFixed(precision)); }

function calculateOrderAmount(price, orderValue, amountPrecision) { /** * 计算订单数量 * 确保数量是合约最小单位的整数倍 * ETH永续合约最小单位:0.01 */ var minLotSize = MIN_LOT_SIZE; // ETH永续合约最小单位

// 计算基础数量
var amount = orderValue / price;

Log("计算订单数量 - 基础计算: 目标金额=" + orderValue + ", 价格=" + price + ", 基础数量=" + amount.toFixed(6));

// 确保数量是minLotSize的整数倍(向上取整)
amount = Math.round(amount / minLotSize) * minLotSize;
if (amount < minLotSize) {
    amount = minLotSize;
}
Log("计算订单数量 - 对齐到步长: " + amount.toFixed(amtPrecision));

// 应用精度格式化(数量精度改为2位)
var amtPrecision = Math.max(amountPrecision || (forceAmountPrecision || MIN_LOT_DECIMALS), MIN_LOT_DECIMALS);
amount = formatAmount(amount, amtPrecision);  // 按传入精度格式化

// 再次确保是minLotSize的整数倍(防止精度格式化后丢失)
amount = Math.ceil(amount / minLotSize) * minLotSize;
amount = parseFloat(amount.toFixed(amtPrecision));  // 确保符合数量精度

// 计算实际订单金额
var actualValue = amount * price;

// 容错处理:允许订单金额与目标金额有较大偏差(允许10-20%的偏差)
// 如果实际金额太小(小于目标金额的80%),则增加一个最小单位
if (actualValue < orderValue * 0.8) {
    amount = amount + minLotSize;
    amount = parseFloat(amount.toFixed(amtPrecision));
    actualValue = amount * price;
    Log("计算订单数量 - 金额太小,增加一个最小单位: " + amount.toFixed(amtPrecision));
}

var deviation = ((actualValue - orderValue) / orderValue * 100).toFixed(2);
Log("计算订单数量 - 最终结果: 目标金额=" + orderValue + ", 价格=" + price + ", 计算数量=" + amount.toFixed(2) + ", 实际金额=" + actualValue.toFixed(2) + ", 偏差=" + deviation + "%");

return amount;

}

// ==================== 工具函数 ==================== function safeAmount(raw) { var val = Number(raw); return (isNaN(val) || val <= 0) ? 0.001 : Math.max(val, 0.001); }

function safePrice(raw) { var val = Number(raw); return (isNaN(val) || val <= 0) ? 1 : val; }

// 统一判断EOF错误,便于回测数据结束或连接断开时优雅退出 function isEOFError(e) { var msg = “; try { msg = (e && e.toString) ? e.toString() : String(e); } catch (_) { msg = “; } return msg && msg.indexOf(‘EOF’) !== -1; }

// ==================== 仓位记录 ==================== const LONG_SIDE = 1; const SHORT_SIDE = -1;

function addPosition(list, price, amount, side) { list.push({ price: price, amount: amount, side: side }); }

function removePosition(list, idx) { list.splice(idx, 1); }

// ==================== 全局变量 ==================== var config = null; var precision = null; // OKX ETH USDT 永续合约最小数量单位 0.01 var MIN_LOT_SIZE = 0.01; // 默认最小数量步长(OKX ETH 永续) var MIN_LOT_DECIMALS = 2; // 默认数量小数位(与步长匹配) var DEFAULT_PRICE_PREC = 1; // 默认价格精度 var forceMinLotSize = null; // 可选覆盖:最小步长 var forceAmountPrecision = null; // 可选覆盖:数量精度

function decimalPlaces(n) { var s = n.toString(); return s.indexOf(‘.’) > -1 ? s.split(‘.’)[1].length : 0; }

// ==================== 交易规则获取(OKX API) ==================== function loadInstrumentSpec() { try { // 仅当交易所支持 IO 方式直连 OKX V5 时生效 var symbolParts = (config && config.symbol ? config.symbol : “ETH/USDT”).replace(“_”, “/”).split(“/”); var base = symbolParts[0]; var quote = symbolParts[1]; var uly = base + “-” + quote; var params = “instType=SWAP&uly=” + encodeURIComponent(uly); var res = exchange.IO(“api”, “GET”, “/api/v5/public/instruments”, params); if (res && res.data && res.data.length > 0) { var inst = res.data[0]; if (inst.lotSz) { MIN_LOT_SIZE = parseFloat(inst.lotSz); MIN_LOT_DECIMALS = decimalPlaces(inst.lotSz); } if (inst.tickSz) { DEFAULT_PRICE_PREC = decimalPlaces(inst.tickSz); } Log(“获取规则成功 lotSz:”, inst.lotSz, “tickSz:”, inst.tickSz); return true; } else { Log(“获取规则失败:无数据返回”); } } catch (e) { Log(“获取规则异常(可能托管者/交易所不支持 IO):”, e); } return false; }

// 应用用户覆盖的步长/精度(若提供)。若已获取到交易所规则,则忽略覆盖以防违规。 function applyForceLot() { if (config && config.forceMinLotSize && !isNaN(config.forceMinLotSize)) { var f = parseFloat(config.forceMinLotSize); // 只有在未成功获取交易所规则时才采用用户覆盖 if (!DEFAULT_PRICE_PREC || DEFAULT_PRICE_PREC === 1) { MIN_LOT_SIZE = f; MIN_LOT_DECIMALS = decimalPlaces(MIN_LOT_SIZE); Log(“使用手动覆盖的最小步长:”, MIN_LOT_SIZE, “小数位:”, MIN_LOT_DECIMALS); } else { Log(“已从交易所规则获取 lotSz,忽略手动最小步长覆盖”); } } if (config && config.forceAmountPrecision && !isNaN(config.forceAmountPrecision)) { forceAmountPrecision = parseInt(config.forceAmountPrecision, 10); Log(“使用手动覆盖的数量精度:”, forceAmountPrecision); } }

// 多头相关 var MANY_BUYING = false; var MANY_NEXT_BUY_PRICE = 0; var MANY_ORDER_LIST = [];

// 空头相关 var SHORT_BUYING = false; var SHORT_NEXT_BUY_PRICE = 0; var SHORT_ORDER_LIST = [];

// 状态信息相关 var INIT_TIME = null; var INITIAL_BALANCE = 0; var LAST_PROFIT_TIME = null; var PROFIT_HISTORY = []; var CYCLE_START_TIME = 0;

// 参数迁移相关 var oldConfig = null; var isMigrating = false;

// 状态保存间隔(每5分钟保存一次) var LAST_SAVE_TIME = 0; var SAVE_INTERVAL = 300; // 5分钟 var LAST_RECONCILE_TIME = 0; var RECONCILE_INTERVAL = 60; // 每60秒对账一次 var RECONCILE_EMPTY_STREAK = 0; // 连续空结果计数,用于防抖 var RECONCILE_EMPTY_THRESHOLD = 2; // 连续2次空才清空本地持仓

// ==================== 下单函数 ==================== function placeOrder(dir, price, amount) { /** 下单函数,带错误处理和重试机制 */ try { exchange.SetDirection(dir); var finalPrice = safePrice(price); var finalAmount = safeAmount(amount);

    // 按交易所精度要求格式化价格与数量
    var pricePrecision = (precision && precision.pricePrecision) ? precision.pricePrecision : 2;
var amountPrecision = (precision && precision.amountPrecision) ? Math.max(precision.amountPrecision, MIN_LOT_DECIMALS) : (forceAmountPrecision || MIN_LOT_DECIMALS);
    finalPrice = formatPrice(finalPrice, pricePrecision);

    // 确保数量是0.01的整数倍(ETH永续合约最小单位)
var minLotSize = MIN_LOT_SIZE;
    finalAmount = Math.round(finalAmount / minLotSize) * minLotSize;
    if (finalAmount < minLotSize) {
        finalAmount = minLotSize;
    }
    finalAmount = parseFloat(finalAmount.toFixed(amountPrecision));  // 按数量精度保留小数

    // 确保数量至少是最小单位
    if (finalAmount < minLotSize) {
        finalAmount = minLotSize;
    }

    var orderValue = finalPrice * finalAmount;
    Log("下单 - 方向:", dir, "价格:", finalPrice, "数量:", finalAmount, "金额:", orderValue.toFixed(2), "数量验证: 是0.01的", (finalAmount / minLotSize).toFixed(0), "倍");

    var orderId = dir === 'buy' || dir === 'closebuy'
        ? exchange.Buy(finalPrice, finalAmount)
        : exchange.Sell(finalPrice, finalAmount);

    if (!orderId) {
        Log("下单失败 - 方向:", dir, "价格:", finalPrice, "数量:", finalAmount, "返回订单ID为空");
        // 重试一次(如果数量不是最小单位的整数倍,尝试调整)
        if (finalAmount % minLotSize !== 0) {
            finalAmount = Math.ceil(finalAmount / minLotSize) * minLotSize;
            finalAmount = parseFloat(finalAmount.toFixed(2));
            Log("下单重试 - 调整数量为:", finalAmount);
            orderId = dir === 'buy' || dir === 'closebuy'
                ? exchange.Buy(finalPrice, finalAmount)
                : exchange.Sell(finalPrice, finalAmount);
        }
    } else {
        Log("下单成功 - 方向:", dir, "订单ID:", orderId, "价格:", finalPrice, "数量:", finalAmount);
    }

    return orderId;
} catch (e) {
    Log("下单异常 - 方向:", dir, "价格:", price, "数量:", amount, "错误:", e);
    return null;
}

}

// 轮询订单成交,带超时和自定义回调,避免重复代码 function waitOrderFilled(orderId, timeoutSec, onFilled, onTimeout) { var deadline = Unix() + timeoutSec; while (true) { if (Unix() > deadline) { if (onTimeout) onTimeout(orderId); return false; } var order = exchange.GetOrder(orderId); if (order && (order.Status === 1 || order.Status === 2)) { onFilled(order); return true; } Sleep(100); } }

// ==================== 多头网格策略 ==================== function firstManyBuy() { /** 首次多头买入,带超时和重试机制 */ if (MANY_BUYING) return;

var ticker = exchange.GetTicker();
if (!ticker) {
    Log("首次多头买入失败:无法获取行情");
    return;
}

var price = safePrice(ticker.Last);
var orderValue = getOrderAmount(false);  // 使用新参数
Log("准备首次多头买入 - 当前价格:", price, "目标金额:", orderValue);

var amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
Log("首次多头买入 - 计算出的数量:", amount, "价格:", price);

var orderId = placeOrder('buy', price, amount);
if (!orderId) {
    Log("首次多头买入失败:下单返回空,尝试重试...");
    // 重试一次,使用调整后的数量
    Sleep(1000);
    amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
    orderId = placeOrder('buy', price, amount);
    if (!orderId) {
        Log("首次多头买入重试失败:下单返回空");
        return;
    }
}

Log("首次多头买入 - 订单ID:", orderId, "价格:", price, "数量:", amount);

MANY_BUYING = true;
var timeoutSec = 60;  // 60秒超时
var filled = waitOrderFilled(
    orderId,
    timeoutSec,
    function(order) {
        var gridSpacing = getGridSpacing(LONG_SIDE, false);
        MANY_NEXT_BUY_PRICE = formatPrice(
            order.Price * (1 - gridSpacing / 100),
            precision.pricePrecision
        );
        MANY_BUYING = false;
        addPosition(MANY_ORDER_LIST, order.Price, order.Amount, LONG_SIDE);
        Log("多头首次买入成功,价格:", order.Price, "数量:", order.Amount, "下一个买入价格:", MANY_NEXT_BUY_PRICE);

        saveHistory({
            type: 'first_many_buy',
            price: order.Price,
            amount: order.Amount,
            value: order.Price * order.Amount
        });

        safeSaveState();
    },
    function(orderIdTimeout) {
        Log("首次多头买入超时,取消订单:", orderIdTimeout);
        exchange.CancelOrder(orderIdTimeout);
        MANY_BUYING = false;
    }
);
if (!filled) {
    return;
}

}

function manyBuy() { /** 多头加仓(网格买入) */ if (MANY_BUYING) return;

var ticker = exchange.GetTicker();
if (!ticker) {
    Log("多头加仓失败:无法获取行情");
    return;
}

if (ticker.Last > MANY_NEXT_BUY_PRICE) {
    Log("多头加仓跳过:当前价格", ticker.Last, "高于下一个买入价格", MANY_NEXT_BUY_PRICE);
    return;
}

var price = safePrice(ticker.Last);
var orderValue = getOrderAmount(false);  // 使用新参数
Log("准备多头加仓 - 当前价格:", price, "下一个买入价格:", MANY_NEXT_BUY_PRICE, "目标金额:", orderValue);

var amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
Log("多头加仓 - 计算出的数量:", amount, "价格:", price);

var orderId = placeOrder('buy', price, amount);
if (!orderId) {
    Log("多头加仓失败:下单返回空");
    return;
}

Log("多头加仓 - 订单ID:", orderId, "价格:", price, "数量:", amount);

MANY_BUYING = true;
var timeoutSec = 60 * 30;  // 30分钟超时
var filled = waitOrderFilled(
    orderId,
    timeoutSec,
    function(order) {
        var gridSpacing = getGridSpacing(LONG_SIDE, false);
        MANY_NEXT_BUY_PRICE = formatPrice(
            order.Price * (1 - gridSpacing / 100),
            precision.pricePrecision
        );
        MANY_BUYING = false;
        addPosition(MANY_ORDER_LIST, order.Price, order.Amount, LONG_SIDE);
        Log("多头加仓成功,价格:", order.Price, "数量:", order.Amount, "持仓数:", MANY_ORDER_LIST.length, "下一个买入价格:", MANY_NEXT_BUY_PRICE);

        saveHistory({
            type: 'many_buy',
            price: order.Price,
            amount: order.Amount,
            value: order.Price * order.Amount
        });

        safeSaveState();
    },
    function(orderIdTimeout) {
        exchange.CancelOrder(orderIdTimeout);
        MANY_BUYING = false;
        Log("多头加仓订单超时,已取消订单:", orderIdTimeout);
    }
);
if (!filled) {
    return;
}

}

function manySell() { /** 多头平仓(网格卖出)- 支持双轨制 */ var ticker = exchange.GetTicker(); if (!ticker) return;

// 从最新持仓开始检查(倒序)
for (var i = MANY_ORDER_LIST.length - 1; i >= 0; i--) {
    var pos = MANY_ORDER_LIST[i];

    // 计算盈利价格(支持双轨制:旧持仓用旧参数,新持仓用新参数)
    var gridSpacing = getGridSpacing(LONG_SIDE, true);  // 平仓时检查是否用旧参数
    var profitPrice = formatPrice(
        pos.price * (1 + gridSpacing / 100),
        precision.pricePrecision
    );

    // 如果当前价格达到盈利价格,平仓
    if (ticker.Last >= profitPrice) {
        var position = exchange.GetPosition();
        var longPos = null;
        if (position && position.length > 0) {
            for (var j = 0; j < position.length; j++) {
                if (position[j].Type === 0 && position[j].Amount > 0) {
                    longPos = position[j];
                    break;
                }
            }
        }

        if (!longPos || longPos.Amount <= 0) break;

        var qty = safeAmount(Math.min(pos.amount, longPos.Amount));
        // 确保数量是0.01的整数倍(ETH永续合约最小单位)
        var minLotSize = MIN_LOT_SIZE;
        qty = Math.floor(qty / minLotSize) * minLotSize;
        qty = parseFloat(qty.toFixed(2));  // 保留2位小数

        if (qty < minLotSize) {
            Log("多头平仓跳过:数量太小", qty, "最小单位:", minLotSize);
            break;
        }

        Log("准备多头平仓 - 持仓价格:", pos.price, "当前价格:", ticker.Last, "盈利价格:", profitPrice, "平仓数量:", qty, "使用参数:", shouldUseOldConfig(LONG_SIDE) ? "旧参数" : "新参数");

        // 多头平仓应使用 closesell(卖出平多)
        var orderId = placeOrder('closesell', ticker.Last, qty);

        if (!orderId) continue;

        while (true) {
            var order = exchange.GetOrder(orderId);
            if (!order) {
                Sleep(100);
                continue;
            }

            if (order.Status === 1 || order.Status === 2) {
                // 计算下一个买入价格(基于新参数的多头网格间距)
                var nextGridSpacing = getGridSpacing(LONG_SIDE, false);
                MANY_NEXT_BUY_PRICE = formatPrice(
                    ticker.Last * (1 - nextGridSpacing / 100),
                    precision.pricePrecision
                );
                removePosition(MANY_ORDER_LIST, i);
                Log("多头平仓成功,价格:", order.Price, "数量:", order.Amount, "剩余持仓:", MANY_ORDER_LIST.length, "下一个买入价格:", MANY_NEXT_BUY_PRICE);

                // 保存历史记录
                var profit = (order.Price - pos.price) * order.Amount;
                saveHistory({
                    type: 'many_sell',
                    price: order.Price,
                    amount: order.Amount,
                    value: order.Price * order.Amount,
                    entryPrice: pos.price,
                    profit: profit
                });

                // ✅ 成交后立即保存状态
                safeSaveState();

                // 检查迁移是否完成
                checkMigrationComplete();

                break;
            }
            Sleep(100);
        }
    }
}

}

// ==================== 空头网格策略 ==================== function firstShortBuy() { /** 首次空头卖出,带超时和重试机制 */ if (SHORT_BUYING) return;

var ticker = exchange.GetTicker();
if (!ticker) {
    Log("首次空头卖出失败:无法获取行情");
    return;
}

var price = safePrice(ticker.Last);
var orderValue = getOrderAmount(false);  // 使用新参数
Log("准备首次空头卖出 - 当前价格:", price, "目标金额:", orderValue);

var amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
Log("首次空头卖出 - 计算出的数量:", amount, "价格:", price);

var orderId = placeOrder('sell', price, amount);
if (!orderId) {
    Log("首次空头卖出失败:下单返回空,尝试重试...");
    // 重试一次,使用调整后的数量
    Sleep(1000);
    amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
    orderId = placeOrder('sell', price, amount);
    if (!orderId) {
        Log("首次空头卖出重试失败:下单返回空");
        return;
    }
}

Log("首次空头卖出 - 订单ID:", orderId, "价格:", price, "数量:", amount);

SHORT_BUYING = true;
var timeoutSec = 60;  // 60秒超时
var filled = waitOrderFilled(
    orderId,
    timeoutSec,
    function(order) {
        var gridSpacing = getGridSpacing(SHORT_SIDE, false);
        SHORT_NEXT_BUY_PRICE = formatPrice(
            order.Price * (1 + gridSpacing / 100),
            precision.pricePrecision
        );
        SHORT_BUYING = false;
        addPosition(SHORT_ORDER_LIST, order.Price, order.Amount, SHORT_SIDE);
        Log("空头首次卖出成功,价格:", order.Price, "数量:", order.Amount, "下一个卖出价格:", SHORT_NEXT_BUY_PRICE);

        saveHistory({
            type: 'first_short_sell',
            price: order.Price,
            amount: order.Amount,
            value: order.Price * order.Amount
        });

        safeSaveState();
    },
    function(orderIdTimeout) {
        Log("首次空头卖出超时,取消订单:", orderIdTimeout);
        exchange.CancelOrder(orderIdTimeout);
        SHORT_BUYING = false;
    }
);
if (!filled) {
    return;
}

}

function shortBuy() { /** 空头加仓(网格卖出) */ if (SHORT_BUYING) return;

var ticker = exchange.GetTicker();
if (!ticker) {
    Log("空头加仓失败:无法获取行情");
    return;
}

if (ticker.Last < SHORT_NEXT_BUY_PRICE) {
    Log("空头加仓跳过:当前价格", ticker.Last, "低于下一个卖出价格", SHORT_NEXT_BUY_PRICE);
    return;
}

var price = safePrice(ticker.Last);
var orderValue = getOrderAmount(false);  // 使用新参数
Log("准备空头加仓 - 当前价格:", price, "下一个卖出价格:", SHORT_NEXT_BUY_PRICE, "目标金额:", orderValue);

var amount = calculateOrderAmount(price, orderValue, precision.amountPrecision);
Log("空头加仓 - 计算出的数量:", amount, "价格:", price);

var orderId = placeOrder('sell', price, amount);
if (!orderId) {
    Log("空头加仓失败:下单返回空");
    return;
}

Log("空头加仓 - 订单ID:", orderId, "价格:", price, "数量:", amount);

SHORT_BUYING = true;
var timeoutSec = 60 * 30;  // 30分钟超时
var filled = waitOrderFilled(
    orderId,
    timeoutSec,
    function(order) {
        var gridSpacing = getGridSpacing(SHORT_SIDE, false);
        SHORT_NEXT_BUY_PRICE = formatPrice(
            order.Price * (1 + gridSpacing / 100),
            precision.pricePrecision
        );
        SHORT_BUYING = false;
        addPosition(SHORT_ORDER_LIST, order.Price, order.Amount, SHORT_SIDE);
        Log("空头加仓成功,价格:", order.Price, "数量:", order.Amount, "持仓数:", SHORT_ORDER_LIST.length, "下一个卖出价格:", SHORT_NEXT_BUY_PRICE);

        saveHistory({
            type: 'short_sell',
            price: order.Price,
            amount: order.Amount,
            value: order.Price * order.Amount
        });

        safeSaveState();
    },
    function(orderIdTimeout) {
        exchange.CancelOrder(orderIdTimeout);
        SHORT_BUYING = false;
        Log("空头加仓订单超时,已取消订单:", orderIdTimeout);
    }
);
if (!filled) {
    return;
}

}

function shortSell() { /** 空头平仓(网格买入)- 支持双轨制 */ var ticker = exchange.GetTicker(); if (!ticker) return;

// 从最新持仓开始检查(倒序)
for (var i = SHORT_ORDER_LIST.length - 1; i >= 0; i--) {
    var pos = SHORT_ORDER_LIST[i];

    // 计算盈利价格(支持双轨制:旧持仓用旧参数,新持仓用新参数)
    var gridSpacing = getGridSpacing(SHORT_SIDE, true);  // 平仓时检查是否用旧参数
    var profitPrice = formatPrice(
        pos.price * (1 - gridSpacing / 100),
        precision.pricePrecision
    );

    // 如果当前价格达到盈利价格,平仓
    if (ticker.Last <= profitPrice) {
        var position = exchange.GetPosition();
        var shortPos = null;
        if (position && position.length > 0) {
            for (var j = 0; j < position.length; j++) {
                if (position[j].Type === 1 && position[j].Amount > 0) {
                    shortPos = position[j];
                    break;
                }
            }
        }

        if (!shortPos || shortPos.Amount <= 0) break;

        var qty = safeAmount(Math.min(pos.amount, shortPos.Amount));
        // 确保数量是0.01的整数倍(ETH永续合约最小单位)
        var minLotSize = MIN_LOT_SIZE;
        qty = Math.floor(qty / minLotSize) * minLotSize;
        qty = parseFloat(qty.toFixed(2));  // 保留2位小数

        if (qty < minLotSize) {
            Log("空头平仓跳过:数量太小", qty, "最小单位:", minLotSize);
            break;
        }

        Log("准备空头平仓 - 持仓价格:", pos.price, "当前价格:", ticker.Last, "盈利价格:", profitPrice, "平仓数量:", qty, "使用参数:", shouldUseOldConfig(SHORT_SIDE) ? "旧参数" : "新参数");

        // 空头平仓应使用 closebuy(买入平空)
        var orderId = placeOrder('closebuy', ticker.Last, qty);

        if (!orderId) continue;

        while (true) {
            var order = exchange.GetOrder(orderId);
            if (!order) {
                Sleep(100);
                continue;
            }

            if (order.Status === 1 || order.Status === 2) {
                // 计算下一个卖出价格(基于新参数的空头网格间距)
                var nextGridSpacing = getGridSpacing(SHORT_SIDE, false);
                SHORT_NEXT_BUY_PRICE = formatPrice(
                    ticker.Last * (1 + nextGridSpacing / 100),
                    precision.pricePrecision
                );
                removePosition(SHORT_ORDER_LIST, i);
                Log("空头平仓成功,价格:", order.Price, "数量:", order.Amount, "剩余持仓:", SHORT_ORDER_LIST.length, "下一个卖出价格:", SHORT_NEXT_BUY_PRICE);

                // 保存历史记录
                var profit = (pos.price - order.Price) * order.Amount;
                saveHistory({
                    type: 'short_buy',
                    price: order.Price,
                    amount: order.Amount,
                    value: order.Price * order.Amount,
                    entryPrice: pos.price,
                    profit: profit
                });

                // ✅ 成交后立即保存状态
                safeSaveState();

                // 检查迁移是否完成
                checkMigrationComplete();

                break;
            }
            Sleep(100);
        }
    }
}

}

// ==================== 边界检查 ==================== function checkPriceBounds() { /** 检查价格是否在网格边界内 */ var ticker = exchange.GetTicker(); if (!ticker) return true;

var currentPrice = ticker.Last;

// 如果价格超出上边界,暂停多头买入
if (currentPrice >= config.upperPrice) {
    return false;
}

// 如果价格低于下边界,暂停空头卖出
if (currentPrice <= config.lowerPrice) {
    return false;
}

return true;

}

// ==================== 状态信息计算 ==================== function calculateLiquidationPrice(account, positions, leverage) { /** 计算爆仓价 */ var longLiquidationPrice = null; var shortLiquidationPrice = null;

if (!account || !positions || positions.length === 0) {
    return { long: longLiquidationPrice, short: shortLiquidationPrice };
}

var margin = account.Margin || 0;
var balance = account.Balance || 0;
var available = account.Available || 0;

// 计算多头和空头持仓
var longAmount = 0;
var longPrice = 0;
var shortAmount = 0;
var shortPrice = 0;

for (var i = 0; i < positions.length; i++) {
    var pos = positions[i];
    if (pos.Type === 0 && pos.Amount > 0) {
        // 多头
        longAmount += pos.Amount;
        longPrice = pos.Price;
    } else if (pos.Type === 1 && pos.Amount > 0) {
        // 空头
        shortAmount += pos.Amount;
        shortPrice = pos.Price;
    }
}

// 计算多头爆仓价(价格下跌导致爆仓)
if (longAmount > 0 && longPrice > 0 && leverage > 0) {
    // 简化计算:爆仓价 = 持仓价 * (1 - 维持保证金率)
    // 维持保证金率通常为 0.5% - 1%
    var maintenanceMarginRate = 0.01; // 1%
    longLiquidationPrice = longPrice * (1 - maintenanceMarginRate);
}

// 计算空头爆仓价(价格上涨导致爆仓)
if (shortAmount > 0 && shortPrice > 0 && leverage > 0) {
    var maintenanceMarginRate = 0.01; // 1%
    shortLiquidationPrice = shortPrice * (1 + maintenanceMarginRate);
}

return { long: longLiquidationPrice, short: shortLiquidationPrice };

}

function calculateProfit(account, initialBalance) { /** 计算收益信息 */ if (!account || !initialBalance) { return { totalProfit: 0, totalProfitRate: 0, dailyProfit: 0, avgDailyReturn: 0, estimatedMonthlyReturn: 0, estimatedYearlyReturn: 0 }; }

var currentBalance = account.Balance || 0;
var totalProfit = currentBalance - initialBalance;
var totalProfitRate = initialBalance > 0 ? (totalProfit / initialBalance) : 0;

// 计算当日收益(简化处理,使用最近一次收益记录)
var dailyProfit = 0;
if (PROFIT_HISTORY.length > 0) {
    var today = new Date();
    today.setHours(0, 0, 0, 0);
    var todayTimestamp = Math.floor(today.getTime() / 1000);

    var yesterdayProfit = 0;
    for (var i = PROFIT_HISTORY.length - 1; i >= 0; i--) {
        if (PROFIT_HISTORY[i].time < todayTimestamp) {
            yesterdayProfit = PROFIT_HISTORY[i].profit;
            break;
        }
    }
    dailyProfit = totalProfit - yesterdayProfit;
}

// 计算平均日化收益率
var avgDailyReturn = 0;
var estimatedMonthlyReturn = 0;
var estimatedYearlyReturn = 0;

if (INIT_TIME && INIT_TIME > 0) {
    var days = (Unix() - INIT_TIME) / (24 * 3600);
    if (days > 0) {
        avgDailyReturn = totalProfitRate / days;
        estimatedMonthlyReturn = avgDailyReturn * 30;
        estimatedYearlyReturn = avgDailyReturn * 365;
    }
}

return {
    totalProfit: totalProfit,
    totalProfitRate: totalProfitRate,
    dailyProfit: dailyProfit,
    avgDailyReturn: avgDailyReturn,
    estimatedMonthlyReturn: estimatedMonthlyReturn,
    estimatedYearlyReturn: estimatedYearlyReturn
};

}

function getPositionInfo() { /** 获取持仓详细信息 */ var positions = []; var ticker = exchange.GetTicker(); var currentPrice = ticker ? ticker.Last : 0;

// 获取实际持仓
var exchangePositions = exchange.GetPosition();
if (!exchangePositions || exchangePositions.length === 0) {
    return positions;
}

for (var i = 0; i < exchangePositions.length; i++) {
    var pos = exchangePositions[i];
    if (pos.Amount <= 0) continue;

    var direction = pos.Type === 0 ? 'long' : 'short';
    var positionValue = pos.Amount * pos.Price;
    var unrealizedPnl = pos.Profit || 0;

    // 计算预估回归收益(基于网格间距)
    var gridSpacing = direction === 'long' ? config.longGridSpacing : config.shortGridSpacing;
    var estimatedReturn = positionValue * (gridSpacing / 100);
    var estimatedTotalReturn = estimatedReturn * (direction === 'long' ? MANY_ORDER_LIST.length : SHORT_ORDER_LIST.length);

    // 网格初始价格和转向间距
    var gridInitialPrice = currentPrice;
    var turnSpacing = gridSpacing;

    // 挂单价格
    var buyOrderPrice = direction === 'long' ? MANY_NEXT_BUY_PRICE : 0;
    var sellOrderPrice = direction === 'short' ? SHORT_NEXT_BUY_PRICE : 0;

    // 计算成交额(简化处理)
    var tradeVolume = positionValue;

    positions.push({
        symbol: config.symbol,
        direction: direction,
        quantity: pos.Amount,
        positionPrice: pos.Price,
        positionValue: positionValue,
        estimatedReturn: estimatedReturn,
        estimatedTotalReturn: estimatedTotalReturn,
        gridInitialPrice: gridInitialPrice,
        turnSpacing: turnSpacing,
        currentPrice: currentPrice,
        buyOrderPrice: buyOrderPrice,
        sellOrderPrice: sellOrderPrice,
        unrealizedPnl: unrealizedPnl,
        tradeVolume: tradeVolume
    });
}

return positions;

}

function updateStatusInfo() { /** 更新并输出状态信息(FMZ原生表格格式,框架式布局) */ try { var account = exchange.GetAccount(); var positions = exchange.GetPosition(); var ticker = exchange.GetTicker();

    if (!account) return;

    // 计算爆仓价
    var leverage = 1;
    var liquidationPrices = calculateLiquidationPrice(account, positions, leverage);

    // 计算收益
    var profitInfo = calculateProfit(account, INITIAL_BALANCE);

    // 计算循环延时
    var cycleDelay = 0;
    if (CYCLE_START_TIME > 0) {
        cycleDelay = (Unix() - CYCLE_START_TIME) * 1000;
    }
    CYCLE_START_TIME = Unix();

    // 获取持仓信息
    var positionInfo = getPositionInfo();

    // 格式化时间
    var initTimeStr = '-';
    if (INIT_TIME) {
        var initDate = new Date(INIT_TIME * 1000);
        var pad = function(n) { return n < 10 ? '0' + n : n; };
        initTimeStr = initDate.getFullYear() + '-' + 
                     pad(initDate.getMonth() + 1) + '-' + 
                     pad(initDate.getDate()) + ' ' +
                     pad(initDate.getHours()) + ':' + 
                     pad(initDate.getMinutes()) + ':' + 
                     pad(initDate.getSeconds());
    }

    // 计算运行时长
    var runTime = '-';
    if (INIT_TIME) {
        var seconds = Unix() - INIT_TIME;
        var days = Math.floor(seconds / 86400);
        var hours = Math.floor((seconds % 86400) / 3600);
        var minutes = Math.floor((seconds % 3600) / 60);
        runTime = days + '天' + hours + '小时' + minutes + '分钟';
    }

    // 构造纯文本表格(按行输出),在移动端也能清晰显示
    var lines = [];
    var addTextSection = function(title, rows) {
        lines.push(title);
相关推荐
全部留言
avatar of ianzeng123
ianzeng123
```javascript function main(){ const marketinfo = exchange.GetMarkets() const symbolinfo = marketinfo['BTC_USDT.swap'] Log(symbolinfo) //返回币种的各种信息 AmountPrecision 为3 let quantity = 5.776123 quantity = _N(quantity, symbolinfo.AmountPrecision) Log(quantity) //返回数量精度处理后的 5.776 } ```
2025-12-08 14:14:31
avatar of ianzeng123
ianzeng123
```javascript let statusDisplay = '`' + JSON.stringify(accountTable) + '`\n\n'; statusDisplay += '`' + JSON.stringify(positionTable) + '`\n\n'; statusDisplay += '`' + JSON.stringify(analysisTable) + '`\n\n'; statusDisplay += '`' + JSON.stringify(executionTable) + '`'; // 添加更新时间提示 if (lastUpdateTime) { const timeSinceUpdate = Math.floor((Date.now() - lastUpdateTime) / 1000); statusDisplay += `\n\n⏱️ 上次决策: ${timeSinceUpdate}秒前`; } // 显示状态 LogStatus(statusDisplay); ``` 参考一下
2025-12-09 10:04:54
avatar of 我想量化躺着挣钱
我想量化躺着挣钱
/upload/asset/2dfc70258713f23aedc1d.png让状态信息栏变成表格的模式需要引用什么函数,我这搞半天还都是这种字体的,根本看不懂
2025-12-08 18:12:02
avatar of ianzeng123
ianzeng123
可以输入一些成熟的策略喂给他,让它模仿着写
2025-12-08 17:45:56
avatar of 我想量化躺着挣钱
我想量化躺着挣钱
https://www.fmz.com/robot/636663好像能跑了,不知道还会不会出错。
2025-12-08 17:36:57
avatar of 发明者量化-小小梦
发明者量化-小小梦
看起来问题挺多的,可以扔给AI,审核一下策略代码。
2025-12-08 13:05:35
avatar of 我想量化躺着挣钱
我想量化躺着挣钱
ETH在欧易下单精度可以0.001就这一个问题我熬夜五天到现在都不能解决AI也不能解决
2025-12-08 13:26:00
avatar of 我想量化躺着挣钱
我想量化躺着挣钱
扔给AI AI就骗我。半个月了AI我都换冒烟了,还是不能解决。还的是人工,求助各位大佬
2025-12-08 13:20:11
avatar of 发明者量化-小小梦
发明者量化-小小梦
代码看着有点像AI写的。
2025-12-08 11:28:29
avatar of 发明者量化-小小梦
发明者量化-小小梦
精度计算中的变量作用域问题 ```javascript function calculateOrderAmount(price, orderValue, amountPrecision) { // ... Log("计算订单数量 - 对齐到步长: " + amount.toFixed(amtPrecision)); // ↑ amtPrecision 在这行之后才定义 var amtPrecision = Math.max(amountPrecision || ...); } ``` amtPrecision 在使用前未定义,JavaScript会变量提升但值为 undefined。
2025-12-08 13:05:10
avatar of 我想量化躺着挣钱
我想量化躺着挣钱
是的梦老板,我怎么都弄不好下单精度还有多空网格同时运行出现混乱的问题,弄了半个月了太难了
2025-12-08 12:08:11