Type/to search
8
Follow
1365
Followers
零手续费重启远古策略:FMZ + Lighter DEX + AI 实战
Discussions
Created 2026-01-22 09:30:47  Updated 2026-01-22 18:12:51
 9
 976

img

前言

最近FMZ量化平台正式接入了Lighter DEX。说实话,刚看到这个消息时我并没有太在意——毕竟市面上的DEX多如牛毛。但当我仔细了解后,一个特性让我眼前一亮:零手续费。

对,你没看错。Lighter DEX对普通交易者实行零手续费政策。这让我立刻想到了一件事:那些年因为手续费损耗而被我"雪藏"的策略,是不是可以翻出来重见天日了?于是,我打开了FMZ的策略广场,翻找那些"远古时代"的策略...

1. Lighter

关于这个新增的DEX,我也不做过多介绍,有兴趣的可以查阅一些资料了解,目前使用体验是比较好的。使用体验方面感受到Lighter在拥有中心化交易所级别的性能,同时保持了去中心化的透明性,对于API接口高频访问时也比较稳定。

资料汇总

这里整理了一些资料:

配置

接下来简单介绍一下如何在FMZ上配置Lighter,在FMZ平台添加交易所页面添加Lighter的API KEY配置信息。

img

需要配置3个内容:

  • 签名API私钥
    这个并非是钱包私钥,这个签名私钥是在登录Lighter后创建的一个用于下单交易的私钥,登录 https://app.lighter.xyz 链接钱包后即可创建。

    img

    • 如果登录的是 Lighter 的测试环境,创建的就是测试环境的KEY,这里不再赘述。
    • 创建签名私钥时需要指定索引,0~2是保留位,从3开始指定即可。
    • 这些登录、创建步骤中都需要钱包签名,我使用的是币安钱包体验还不错。

    将创建好的签名私钥配置在FMZ添加交易所页面中对应控件编辑框中即可。

  • 重要提示:
    需要下载最新版本的托管者程序才支持 Lighter 期货合约交易所。

  • API索引
    填写签名私钥对应的索引。

  • 账户索引
    账户索引可以通过Lighter的API接口获取:

    • API接口:https://mainnet.zklighter.elliot.ai/api/v1/account?by=l1_address&value={钱包地址}
      例如直接在浏览器中访问地址:https://mainnet.zklighter.elliot.ai/api/v1/account?by=l1_address&value=0x123xxxx0x123xxxx是我举例的地址,具体使用时替换这个内容。
      返回数据:

      json
      { "code": 200, "total": 1, "accounts": [ { "code": 0, "account_type": 0, "index": 1234567, "l1_address": "0x123xxxx",

      其中"index": 1234567就是你的账户索引。

    • 转入资产(例如USDC),才会初始化创建Lighter账号

    • 注意,用主网端点的接口获取的是主网的账户索引,用测试网端点则是获取测试网上账户的索引。

    为什么要填写这个账户索引,因为Lighter有子账户系统,根据账户索引区分不同账户,如果需要使用子账户,在这里配置子账户的账户索引即可(子账户的账户索引会出现在以上查询链接返回的JSON数据中)。

2. FMZ上的远古策略

零手续费:改变游戏规则

这是最让我兴奋的特性。Lighter对普通交易者实行零手续费政策。

  • 零手续费意味着什么?

    高频策略不再被手续费"吃掉"利润
    网格策略可以设置更密的网格间距
    套利策略的盈亏平衡点大幅降低
    那些"理论上可行但实际被手续费打败"的策略,终于有了用武之地

翻出远古策略:零手续费的诱惑

策略库考古

带着"零手续费能让什么策略复活"的想法,我开始在FMZ策略库和社区里翻找。很快,一个熟悉的策略进入了我的视野:

https://www.fmz.com/digest-topic/1211

这个策略简单暴力,但是在有手续费的环境下,基本100%亏损。但在Lighter的零手续费环境下,是可以尝试一下的。

那么问题来了

那么问题来了,代码太老了。翻出策略一看,代码简单暴力,UI显示内容几乎没有,缺少一些辅助功能等,以下列举几个缺少的内容:

  • 部分API接口已经过时。
  • 缺少现代化的风控模块。
  • 缺少显示信息。
  • 缺少收益统计。

要手动重构这些代码?说实话,有点头大。不过,我想到了一个"偷懒"的办法——让AI来帮我重构。

3. 重构策略

先开个“护盾”,本文只是对于新兴DEX交易所+AI重构策略做一次探索尝试,不确保重构后的策略具有多强的盈利能力。那么我们开始使用Claude重构这个远古策略,我不想从零开始写代码,我的需求是:

  • 保留原策略的核心逻辑
  • 适配FMZ最新的API规范
  • 添加必要的风控和日志模块
  • 有较为完整清晰的信息显示
  • 适当优化代码逻辑

把原始代码丢给AI

我把原始策略代码复制出来,连同我的需求一起发给了AI。经过几轮对话和调整,AI帮我生成了重构后的代码:

和AI叨叨的内容就不发了,总之就是提需求、测试、不满意了再继续提需求。
甚至可以使用:
bash: while :; do cat PROMPT.md | claude-code ; done
: )
(借鉴思路)

javascript
/* 高频做市策略 - FMZ期货版(带仓位矫正) 策略参数: - sleeptime: 休眠时间(毫秒) - 默认3500 - floatamountbuy: 买单深度阈值 - 默认20 - floatamountsell: 卖单深度阈值 - 默认20 - diffprice: 最小套利差价 - 默认50 - baseAmount: 基础下单量 - 默认0.1 - maxPosition: 最大单边持仓量 - 默认10 - stopLossRatio: 止损比例 - 默认0.9 (90%) - closeOnExit: 退出时清仓 - 默认false */ // 全局变量 var pricePrecision = 2; var amountPrecision = 3; var tickSize = 0.01; var minQty = 0.001; var symbol = "ETH_USDT.swap"; // 统计信息 var stats = { startTime: 0, cycleCount: 0, orderCount: 0, cancelCount: 0, initialEquity: 0, currentEquity: 0, maxEquity: 0, maxDrawdown: 0, isStopLoss: false, lastLogCleanTime: 0, lastEmergencyTime: 0 }; // 日志清理配置 var LOG_CLEAN_INTERVAL = 60 * 60 * 1000; var LOG_RESERVE_COUNT = 10000; var EMERGENCY_COOLDOWN = 5000; // ==================== 精度工具函数 ==================== /** * 从数值推断精度(小数位数) * 例如: 0.005 -> 3, 0.0001 -> 4, 0.1 -> 1, 1 -> 0 */ function GetPrecisionFromValue(value) { if (!value || value >= 1) return 0; var str = value.toString(); // 处理科学计数法 (如 1e-4) if (str.indexOf('e') !== -1) { var match = str.match(/e-(\d+)/); return match ? parseInt(match[1]) : 0; } // 处理普通小数 if (str.indexOf('.') === -1) return 0; return str.split('.')[1].length; } /** * 规范化下单量 * 1. 按精度取整 * 2. 确保是 minQty 的整数倍 * 3. 不小于 minQty */ function NormalizeAmount(amount) { if (amount <= 0) return 0; // 按精度取整 var normalized = _N(amount, amountPrecision); // 确保是 minQty 的整数倍 if (minQty > 0) { normalized = Math.floor(normalized / minQty) * minQty; normalized = _N(normalized, amountPrecision); } // 检查最小下单量 if (normalized < minQty) { return 0; } return normalized; } /** * 规范化价格 */ function NormalizePrice(price) { if (tickSize > 0) { price = Math.round(price / tickSize) * tickSize; } return _N(price, pricePrecision); } // 取消全部订单 function CancelPendingOrders() { var orders = _C(exchange.GetOrders, symbol); var count = 0; for (var j = 0; j < orders.length; j++) { exchange.CancelOrder(orders[j].Id, orders[j]); count++; } if (count > 0) { stats.cancelCount += count; } return count; } // 计算将要下单的价格 function GetPrice(Type, depth) { var amountBids = 0; var amountAsks = 0; if (Type == "Buy") { for (var i = 0; i < 20 && i < depth.Bids.length; i++) { amountBids += depth.Bids[i].Amount; if (amountBids > floatamountbuy) { return NormalizePrice(depth.Bids[i].Price + tickSize); } } } if (Type == "Sell") { for (var j = 0; j < 20 && j < depth.Asks.length; j++) { amountAsks += depth.Asks[j].Amount; if (amountAsks > floatamountsell) { return NormalizePrice(depth.Asks[j].Price - tickSize); } } } return NormalizePrice(depth.Asks[0].Price); } // 获取持仓信息 function GetPosition() { var positions = _C(exchange.GetPositions, symbol); var pos = { long: { amount: 0, price: 0, profit: 0 }, short: { amount: 0, price: 0, profit: 0 } }; for (var i = 0; i < positions.length; i++) { var p = positions[i]; if (p.Type === PD_LONG || p.Type === 0) { pos.long.amount = p.Amount; pos.long.price = p.Price; pos.long.profit = p.Profit || 0; } else { pos.short.amount = p.Amount; pos.short.price = p.Price; pos.short.profit = p.Profit || 0; } } return pos; } // 计算账户权益 function GetEquity(account, pos) { var equity = account.Balance + account.FrozenBalance + pos.long.profit + pos.short.profit; return equity; } // 检查资金是否足够下单 function CheckFunds(account, price, amount, leverage) { leverage = leverage || 10; var requiredMargin = (price * amount) / leverage; var availableBalance = account.Balance; var safeBalance = availableBalance * 0.9; return safeBalance >= requiredMargin; } // ==================== 仓位矫正机制 ==================== /** * 计算动态开仓量(负反馈机制) */ function CalcOpenAmount(pos, direction) { var currentPos = (direction === "long") ? pos.long.amount : pos.short.amount; var posRatio = currentPos / maxPosition; if (currentPos >= maxPosition) { return 0; } var amount = baseAmount; if (posRatio > 0.8) { amount = baseAmount * 0.2; } else if (posRatio > 0.6) { amount = baseAmount * 0.4; } else if (posRatio > 0.4) { amount = baseAmount * 0.6; } else if (posRatio > 0.2) { amount = baseAmount * 0.8; } var netPos = pos.long.amount - pos.short.amount; if (direction === "long" && netPos > maxPosition * 0.3) { amount = amount * 0.5; } else if (direction === "short" && netPos < -maxPosition * 0.3) { amount = amount * 0.5; } return NormalizeAmount(amount); } /** * 计算动态平仓量 */ function CalcCloseAmount(pos, direction) { var currentPos = (direction === "long") ? pos.long.amount : pos.short.amount; if (currentPos <= 0) return 0; var posRatio = currentPos / maxPosition; var amount = baseAmount; if (posRatio > 1.0) { amount = currentPos; } else if (posRatio > 0.8) { amount = baseAmount * 3.0; } else if (posRatio > 0.6) { amount = baseAmount * 2.0; } else if (posRatio > 0.4) { amount = baseAmount * 1.5; } amount = Math.min(amount, currentPos); return NormalizeAmount(amount); } /** * 计算平仓价格(仓位重时更激进) */ function CalcClosePrice(pos, depth, direction) { var currentPos = (direction === "long") ? pos.long.amount : pos.short.amount; var posRatio = currentPos / maxPosition; if (direction === "long") { if (posRatio > 1.0) { return NormalizePrice(depth.Bids[0].Price - tickSize * 3); } else if (posRatio > 0.8) { return NormalizePrice(depth.Bids[0].Price); } else if (posRatio > 0.5) { var midPrice = (depth.Bids[0].Price + depth.Asks[0].Price) / 2; return NormalizePrice(midPrice); } return NormalizePrice(depth.Asks[0].Price - tickSize); } else { if (posRatio > 1.0) { return NormalizePrice(depth.Asks[0].Price + tickSize * 3); } else if (posRatio > 0.8) { return NormalizePrice(depth.Asks[0].Price); } else if (posRatio > 0.5) { var midPrice = (depth.Bids[0].Price + depth.Asks[0].Price) / 2; return NormalizePrice(midPrice); } return NormalizePrice(depth.Bids[0].Price + tickSize); } } /** * 紧急减仓 */ function EmergencyReduce(pos, depth) { var now = new Date().getTime(); if (now - stats.lastEmergencyTime < EMERGENCY_COOLDOWN) { return false; } var needReduce = false; if (pos.long.amount > maxPosition * 1.2) { var reduceAmount = NormalizeAmount(pos.long.amount - maxPosition); if (reduceAmount > 0) { Log("⚠️ 多头严重超标 (" + pos.long.amount + "/" + maxPosition + "),市价减仓: " + reduceAmount, "#FF0000"); var orderId = exchange.CreateOrder(symbol, "closebuy", -1, reduceAmount); if (orderId) { stats.orderCount++; stats.lastEmergencyTime = now; } needReduce = true; } } if (pos.short.amount > maxPosition * 1.2) { var reduceAmount = NormalizeAmount(pos.short.amount - maxPosition); if (reduceAmount > 0) { Log("⚠️ 空头严重超标 (" + pos.short.amount + "/" + maxPosition + "),市价减仓: " + reduceAmount, "#FF0000"); var orderId = exchange.CreateOrder(symbol, "closesell", -1, reduceAmount); if (orderId) { stats.orderCount++; stats.lastEmergencyTime = now; } needReduce = true; } } if (needReduce) { Sleep(1000); } return needReduce; } function IsPositionOverload(pos) { return pos.long.amount > maxPosition || pos.short.amount > maxPosition; } // ==================== 日志清理 ==================== function CleanLogs() { var now = new Date().getTime(); if (now - stats.lastLogCleanTime > LOG_CLEAN_INTERVAL) { LogReset(LOG_RESERVE_COUNT); stats.lastLogCleanTime = now; Log("日志已清理,保留最近 " + LOG_RESERVE_COUNT + " 条", "#0000FF"); } } // ==================== 清仓相关 ==================== function CloseAllPositions(reason) { reason = reason || "手动触发"; Log("========== 触发清仓 [" + reason + "] ==========", "#FF0000"); CancelPendingOrders(); Sleep(500); var pos = GetPosition(); if (pos.long.amount > 0) { var orderId = exchange.CreateOrder(symbol, "closebuy", -1, pos.long.amount); if (orderId) { Log("市价平多: " + pos.long.amount, "#FF0000"); } } if (pos.short.amount > 0) { var orderId = exchange.CreateOrder(symbol, "closesell", -1, pos.short.amount); if (orderId) { Log("市价平空: " + pos.short.amount, "#FF0000"); } } Sleep(2000); var finalPos = GetPosition(); if (finalPos.long.amount > 0 || finalPos.short.amount > 0) { Log("⚠️ 警告:仍有未平仓位!多头: " + finalPos.long.amount + ", 空头: " + finalPos.short.amount, "#FF0000"); if (finalPos.long.amount > 0) { exchange.CreateOrder(symbol, "closebuy", -1, finalPos.long.amount); } if (finalPos.short.amount > 0) { exchange.CreateOrder(symbol, "closesell", -1, finalPos.short.amount); } Sleep(1000); } else { Log("✅ 所有仓位已清空", "#00FF00"); } Log("========== 清仓完成 ==========", "#FF0000"); } function CheckStopLoss(equity) { if (stats.isStopLoss) { return true; } var threshold = stats.initialEquity * stopLossRatio; if (equity < threshold) { stats.isStopLoss = true; var lossPercent = ((stats.initialEquity - equity) / stats.initialEquity * 100).toFixed(2); Log("⚠️ 触发止损! 当前权益: " + _N(equity, 4) + " USDT, 损失: " + lossPercent + "%", "#FF0000"); return true; } return false; } function UpdateProfitChart(equity) { var profit = equity - stats.initialEquity; if (equity > stats.maxEquity) { stats.maxEquity = equity; } var drawdown = (stats.maxEquity - equity) / stats.maxEquity * 100; if (drawdown > stats.maxDrawdown) { stats.maxDrawdown = drawdown; } LogProfit(_N(profit, 4), "&"); } function UpdateStatus(account, pos, depth, buyPrice, sellPrice, equity) { var runTime = (new Date().getTime() - stats.startTime) / 1000 / 60; var hours = Math.floor(runTime / 60); var mins = Math.floor(runTime % 60); var spread = sellPrice - buyPrice; var marketSpread = depth.Asks[0].Price - depth.Bids[0].Price; var profit = equity - stats.initialEquity; var profitPercent = (profit / stats.initialEquity * 100).toFixed(2); var drawdown = stats.maxEquity > 0 ? ((stats.maxEquity - equity) / stats.maxEquity * 100).toFixed(2) : 0; var longRatio = (pos.long.amount / maxPosition * 100).toFixed(1); var shortRatio = (pos.short.amount / maxPosition * 100).toFixed(1); var netPos = _N(pos.long.amount - pos.short.amount, amountPrecision); var table1 = { type: "table", title: "💰 账户信息", cols: ["项目", "数值"], rows: [ ["可用余额", _N(account.Balance, 4) + " USDT"], ["冻结余额", _N(account.FrozenBalance, 4) + " USDT"], ["当前权益", _N(equity, 4) + " USDT"], ["初始权益", _N(stats.initialEquity, 4) + " USDT"], ["最高权益", _N(stats.maxEquity, 4) + " USDT"] ] }; var table2 = { type: "table", title: "📈 收益统计", cols: ["项目", "数值"], rows: [ ["累计收益", _N(profit, 4) + " USDT"], ["收益率", profitPercent + " %"], ["最大回撤", drawdown + " %"], ["止损阈值", (stopLossRatio * 100) + " %"], ["止损状态", stats.isStopLoss ? "⚠️ 已触发" : "✅ 正常"] ] }; var longStatus, shortStatus; if (longRatio > 120) { longStatus = "🔴 紧急减仓"; } else if (longRatio > 100) { longStatus = "🟠 超标"; } else if (longRatio > 80) { longStatus = "🟡 控量"; } else { longStatus = "🟢 正常"; } if (shortRatio > 120) { shortStatus = "🔴 紧急减仓"; } else if (shortRatio > 100) { shortStatus = "🟠 超标"; } else if (shortRatio > 80) { shortStatus = "🟡 控量"; } else { shortStatus = "🟢 正常"; } var table3 = { type: "table", title: "📊 持仓信息(仓位矫正)", cols: ["方向", "数量", "上限", "使用率", "状态", "浮盈"], rows: [ ["多头", pos.long.amount, maxPosition, longRatio + "%", longStatus, _N(pos.long.profit, 4)], ["空头", pos.short.amount, maxPosition, shortRatio + "%", shortStatus, _N(pos.short.profit, 4)], ["净仓", netPos, "-", "-", netPos > 0 ? "偏多" : (netPos < 0 ? "偏空" : "均衡"), "-"] ] }; var table4 = { type: "table", title: "🎯 做市信息", cols: ["项目", "数值"], rows: [ ["买一价", _N(depth.Bids[0].Price, pricePrecision)], ["卖一价", _N(depth.Asks[0].Price, pricePrecision)], ["盘口价差", _N(marketSpread, pricePrecision)], ["挂单买价", _N(buyPrice, pricePrecision)], ["挂单卖价", _N(sellPrice, pricePrecision)], ["做市价差", _N(spread, pricePrecision)] ] }; var table5 = { type: "table", title: "⏱️ 运行统计", cols: ["项目", "数值"], rows: [ ["运行时间", hours + "时" + mins + "分"], ["循环次数", stats.cycleCount], ["下单次数", stats.orderCount], ["撤单次数", stats.cancelCount], ["休眠时间", sleeptime + " ms"] ] }; var table6 = { type: "table", title: "⚙️ 精度与参数", cols: ["参数", "值"], rows: [ ["交易符号", symbol], ["价格精度", pricePrecision], ["数量精度", amountPrecision], ["最小下单量", minQty], ["价格步长", tickSize], ["基础下单量", baseAmount], ["最大持仓量", maxPosition], ["退出时清仓", closeOnExit ? "✅ 是" : "❌ 否"] ] }; var statusIcon = stats.isStopLoss ? "🛑 已止损" : (IsPositionOverload(pos) ? "⚠️ 仓位超标" : "🤖 运行中"); var statusStr = statusIcon + " | " + _D() + " | 收益: " + _N(profit, 2) + " USDT (" + profitPercent + "%)\n"; statusStr += "多仓: " + pos.long.amount + "/" + maxPosition + " (" + longRatio + "%) | "; statusStr += "空仓: " + pos.short.amount + "/" + maxPosition + " (" + shortRatio + "%) | 净: " + netPos + "\n"; statusStr += "`" + JSON.stringify([table1, table2, table3, table4, table5, table6]) + "`"; LogStatus(statusStr); } // 主交易逻辑 function onTick() { stats.cycleCount++; CleanLogs(); var depth = _C(exchange.GetDepth); if (!depth || !depth.Bids || !depth.Asks || depth.Bids.length < 20 || depth.Asks.length < 20) { Log("深度数据不足,跳过本轮"); Sleep(1000); return; } var account = _C(exchange.GetAccount); var pos = GetPosition(); var equity = GetEquity(account, pos); stats.currentEquity = equity; if (stats.cycleCount % 10 === 0) { UpdateProfitChart(equity); } var buyPrice = GetPrice("Buy", depth); var sellPrice = GetPrice("Sell", depth); if ((sellPrice - buyPrice) <= diffprice) { // buyPrice = NormalizePrice(buyPrice - 10 * tickSize); // sellPrice = NormalizePrice(sellPrice + 10 * tickSize); buyPrice = NormalizePrice(buyPrice - diffprice/2); sellPrice = NormalizePrice(sellPrice + diffprice/2); } UpdateStatus(account, pos, depth, buyPrice, sellPrice, equity); if (CheckStopLoss(equity)) { if (pos.long.amount > 0 || pos.short.amount > 0) { CloseAllPositions("止损触发"); } else { CancelPendingOrders(); } Log("策略已止损,停止交易。如需继续,请手动重启策略。", "#FF0000"); return; } CancelPendingOrders(); EmergencyReduce(pos, depth); pos = GetPosition(); var openLongAmount = CalcOpenAmount(pos, "long"); var openShortAmount = CalcOpenAmount(pos, "short"); var closeLongAmount = CalcCloseAmount(pos, "long"); var closeShortAmount = CalcCloseAmount(pos, "short"); var closeLongPrice = CalcClosePrice(pos, depth, "long"); var closeShortPrice = CalcClosePrice(pos, depth, "short"); if (openLongAmount > 0 && CheckFunds(account, buyPrice, openLongAmount)) { var orderId = exchange.CreateOrder(symbol, "buy", buyPrice, openLongAmount); if (orderId) stats.orderCount++; } if (pos.short.amount > 0 && closeShortAmount > 0) { var orderId = exchange.CreateOrder(symbol, "closesell", closeShortPrice, closeShortAmount); if (orderId) stats.orderCount++; } if (openShortAmount > 0 && CheckFunds(account, sellPrice, openShortAmount)) { var orderId = exchange.CreateOrder(symbol, "sell", sellPrice, openShortAmount); if (orderId) stats.orderCount++; } if (pos.long.amount > 0 && closeLongAmount > 0) { var orderId = exchange.CreateOrder(symbol, "closebuy", closeLongPrice, closeLongAmount); if (orderId) stats.orderCount++; } } // 初始化交易精度 function InitPrecision() { try { var markets = exchange.GetMarkets(); if (markets && markets[symbol]) { var market = markets[symbol]; // 获取基础精度信息 pricePrecision = market.PricePrecision || 2; tickSize = market.TickSize || 0.01; minQty = market.MinQty || 0.001; // 从 MinQty 推断数量精度 // 例如: MinQty=0.005 -> 精度3, MinQty=0.0001 -> 精度4 var minQtyPrecision = GetPrecisionFromValue(minQty); // 从 TickSize 推断价格精度(作为备选验证) var tickSizePrecision = GetPrecisionFromValue(tickSize); // 数量精度取 AmountPrecision 和 MinQty推断精度 的较小值 var declaredAmountPrecision = market.AmountPrecision || 8; amountPrecision = Math.min(declaredAmountPrecision, minQtyPrecision); // 价格精度取 PricePrecision 和 TickSize推断精度 的较小值 pricePrecision = Math.min(pricePrecision, tickSizePrecision); Log("========== 精度信息 =========="); Log("市场返回 - PricePrecision:", market.PricePrecision, ", AmountPrecision:", market.AmountPrecision); Log("市场返回 - TickSize:", tickSize, ", MinQty:", minQty); Log("推断精度 - TickSize精度:", tickSizePrecision, ", MinQty精度:", minQtyPrecision); Log("最终使用 - 价格精度:", pricePrecision, ", 数量精度:", amountPrecision); Log("=============================="); // 检查 baseAmount 是否满足最小下单量 if (baseAmount < minQty) { Log("⚠️ 警告: baseAmount(" + baseAmount + ") 小于最小下单量(" + minQty + "),已自动调整", "#FF9900"); baseAmount = minQty; } // 规范化 baseAmount baseAmount = NormalizeAmount(baseAmount); if (baseAmount === 0) { baseAmount = minQty; } Log("规范化后 baseAmount:", baseAmount); } } catch (e) { Log("获取精度失败,使用默认值: " + e.message, "#FF9900"); } } // 初始化收益图表 function InitChart() { var chart = { __isStock: true, tooltip: { xDateFormat: '%Y-%m-%d %H:%M:%S, %A' }, title: { text: '高频做市策略收益曲线' }, xAxis: { type: 'datetime' }, yAxis: { title: { text: '收益 (USDT)' }, opposite: false }, series: [{ name: '收益', data: [] }] }; Chart(chart); } // 程序入口 function main() { LogReset(LOG_RESERVE_COUNT); LogProfitReset(); InitChart(); exchange.SetContractType("swap"); symbol = exchange.GetCurrency() + ".swap"; Log("交易符号:", symbol); // 测试是否支持Lighter if (!exchange.GetAccount()) { Log("账户初始化失败,请检查配置是否正确,托管者是否为最新版本!") } InitPrecision(); stats.startTime = new Date().getTime(); stats.lastLogCleanTime = stats.startTime; var initAccount = _C(exchange.GetAccount); var initPos = GetPosition(); stats.initialEquity = GetEquity(initAccount, initPos); stats.maxEquity = stats.initialEquity; stats.currentEquity = stats.initialEquity; Log("========== 策略启动 ==========", "#00FF00"); Log("初始权益:", _N(stats.initialEquity, 4), "USDT"); Log("止损阈值:", _N(stats.initialEquity * stopLossRatio, 4), "USDT", "(" + (stopLossRatio * 100) + "%)"); Log("最大持仓:", maxPosition); Log("退出时清仓:", closeOnExit ? "是" : "否"); Log("初始账户:", initAccount); Log("初始持仓:", _C(exchange.GetPositions, symbol)); while (true) { try { onTick(); } catch (e) { Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message, "#FF0000"); } Sleep(sleeptime); } } // 退出清理 function onexit() { Log("========== 策略停止 ==========", "#FF0000"); if (closeOnExit) { Log("closeOnExit=true,执行退出清仓...", "#FF9900"); CloseAllPositions("策略退出"); } else { Log("closeOnExit=false,保留当前持仓和挂单", "#0000FF"); } Log("----------------------------------------"); Log("总循环: " + stats.cycleCount + ", 下单: " + stats.orderCount + ", 撤单: " + stats.cancelCount); Log("初始权益: " + _N(stats.initialEquity, 4) + " USDT"); Log("最终权益: " + _N(stats.currentEquity, 4) + " USDT"); Log("总收益: " + _N(stats.currentEquity - stats.initialEquity, 4) + " USDT"); Log("收益率: " + _N((stats.currentEquity - stats.initialEquity) / stats.initialEquity * 100, 2) + " %"); Log("最大回撤: " + _N(stats.maxDrawdown, 2) + " %"); Log("----------------------------------------"); }

img

你把FMZ上参数的编辑规则告诉AI,甚至AI直接帮你把界面参数都配置好,为了便于讲解我是手动去配置的策略界面上的参数,如上图。

4. 实盘运行

img

img

img

说句实话,AI写的代码真的比我写得好太多,而且很少错的(只要需求描述清楚,我也就迭代了3~4次就跑稳定了),瞬间感觉生产力爆炸💥虽然目前看起来好像策略不赚钱😂,交易量倒是刷了不少。

img

img

5. 总结

FMZ + AI + Lighter的化学反应

回顾这次实践,我有几点感触:

  • 零手续费确实是game changer
    以前很多"理论很美好"的策略,都被手续费这个"隐形杀手"打败了。Lighter的零手续费政策,让这些策略有了重生的机会。如果你也有类似的"雪藏策略",不妨翻出来试试。

  • AI大幅降低了策略开发门槛
    这次我几乎没有手写代码,全程依靠AI完成了策略重构。这在以前是不可想象的。对于有交易思路但编程能力有限的朋友来说,AI+FMZ的组合无疑是一个福音。

  • FMZ快速接入新交易所的价值
    FMZ能够快速接入Lighter这样的新兴交易所,让用户可以第一时间抓住机会。统一的API封装也意味着,你的策略可以很容易地迁移到新平台。

下一步计划

接下来,我打算:

  • 继续优化这个策略的参数。
  • 尝试更多适合零手续费环境的策略类型。
  • 探索Lighter的适用的相关策略。

其它远古策略,还有:OK韭菜收割机等。

感谢支持

感谢您的阅读,如果您有好的需求和建议欢迎提出。

6. 免责声明

本文仅供技术交流与学习参考,不构成任何投资建议。

  • 策略风险:本文展示的策略代码仅作为技术演示,不保证盈利能力。量化交易存在固有风险,历史回测或短期实盘表现不代表未来收益。请在充分理解策略逻辑的前提下谨慎使用。
  • DEX 风险:去中心化交易所(DEX)涉及智能合约风险、流动性风险及网络拥堵风险。Lighter DEX 作为新兴平台,其长期稳定性和安全性尚待市场验证。
  • 零手续费政策:交易所的费率政策可能随时调整,请以 Lighter 官方最新公告为准。即使交易免手续费,链上 Gas 费用仍可能产生。
  • AI 生成代码:本文策略代码由 AI 辅助生成,虽经测试但不排除存在潜在 Bug 或逻辑缺陷,仅用于学习研究。
  • 自担风险:使用本文提供的任何信息、代码或策略所产生的一切后果,由用户自行承担。作者及 FMZ 平台不对任何直接或间接损失负责。

加密货币交易具有高风险性,请务必做好风险管理,理性交易。

Comment
All comments (9)

    高频的话没意义,强制延迟300Ms

    2 months ago

    梦神,你的这个直接可以回测吗?我回测时候,提醒需求太多。GetAccount: {"msg":"Too Many Requests","code":"50011"} at onTick (FILE:541:21) at main (FILE:709:19)
    策略的参数怎么调整会比较好。可以给点建议吗?谢谢

    5 months ago

    写个WebSocket模板订阅公共频道访问深度,然后替换exchange.GetDepth接口

    5 months ago

    具体有没有代码,可以帮忙提供一下。我卡在这里了。谢谢

    5 months ago

    对于这个策略,回测测不出来什么,只能实盘测,实盘还需要优化,我实盘测试不能盈利,但是量倒是刷了不少。。。

    5 months ago

    我就是实盘测试的,出现这种问题。

    5 months ago

    有些接口前增加一些Sleep(1000),交易所有些限频,不能访问接口太快。可以让AI帮你改,给AI说访问频率过高,降低频率或者合理减少接口调用。

    5 months ago

    好东西,大道至简

    5 months ago

    感谢老铁的支持,继续分享有趣的量化交易实践经验。

    5 months ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)