[TOC]

최근 FMZ 양적 플랫폼이 공식적으로 통합되었습니다.Lighter DEX솔직히 처음 이 소식을 접했을 때는 별로 신경 쓰지 않았습니다. 시장에는 수많은 탈중앙화 거래소(DEX)가 있으니까요. 하지만 좀 더 자세히 알아보니 한 가지 특징이 눈에 띄었습니다. 바로 거래 수수료가 없다는 점입니다.
네, 맞습니다. Lighter DEX는 일반 거래자에게 수수료가 전혀 없습니다. 이 소식을 듣자마자 문득 예전에 수수료 때문에 접어두었던 전략들을 다시 활용해 볼 수 있지 않을까 하는 생각이 들었습니다. 그래서 FMZ를 개설했습니다…전략 스퀘어“고대”의 전략들을 찾아보며…
이번에 새롭게 추가된 탈중앙화 거래소(DEX)에 대해서는 자세히 설명하지 않겠습니다. 관심 있는 분들은 관련 정보를 찾아보시기 바랍니다. 현재 사용자 경험은 상당히 좋습니다. Lighter는 탈중앙화 거래소의 투명성을 유지하면서도 중앙화 거래소와 유사한 성능을 제공하며, API 접근 빈도가 높음에도 비교적 안정적으로 작동합니다.
다음은 몇 가지 정보를 정리한 것입니다.
다음으로 FMZ 설정 방법에 대해 간략하게 소개하겠습니다.LighterFMZ 플랫폼의 거래 페이지에 Lighter API KEY 구성 정보를 추가하세요.

다음 세 가지 사항을 설정해야 합니다.

만들어진서명 개인 키해당 설정은 FMZ 교환 추가 페이지의 해당 제어 편집 상자에서 수행할 수 있습니다.
중요 사항: 라이터 선물 계약 거래를 지원하려면 최신 버전의 수탁 관리 프로그램을 다운로드해야 합니다.
API 인덱스 서명용 개인 키에 해당하는 인덱스를 입력하십시오.
계정 인덱스 Lighter의 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=0x123xxxx,0x123xxxx이 주소는 예시일 뿐이며, 실제 사용 시에는 내용을 바꿔야 합니다.
반환된 데이터:{
"code": 200,
"total": 1,
"accounts": [
{
"code": 0,
"account_type": 0,
"index": 1234567,
"l1_address": "0x123xxxx",
~에"index": 1234567그것이 바로 귀하의 계정 인덱스입니다.
초기 생성 과정은 USDC와 같은 자산이 이체될 때만 시작됩니다.Lighter계정
메인넷 엔드포인트를 사용하여 얻은 인터페이스는 메인넷 계정 인덱스를 가져오고, 테스트넷 엔드포인트를 사용하여 얻은 인터페이스는 테스트넷 계정 인덱스를 가져온다는 점에 유의하십시오.
이 계정 색인을 작성해야 하는 이유는 무엇입니까?Lighter계정 인덱스를 기준으로 여러 계정을 구분하는 하위 계정 시스템이 있습니다. 하위 계정을 사용해야 하는 경우 여기에서 하위 계정의 계정 인덱스를 구성할 수 있습니다(하위 계정의 계정 인덱스는 위 링크를 통해 조회한 결과 반환되는 JSON 데이터에 표시됩니다).
이 기능이 저를 가장 설레게 합니다. Lighter는 일반 거래자에게 수수료를 전혀 부과하지 않습니다.
고빈도 거래 전략은 더 이상 거래 수수료로 인해 수익이 “삭감”되지 않습니다. 메쉬 전략을 사용하면 더 조밀한 메쉬 간격을 설정할 수 있습니다. 차익거래 전략의 손익분기점이 상당히 낮아졌습니다. 이론적으로는 가능했지만 실제 거래 수수료 때문에 무산되었던 전략들이 마침내 활용될 자리를 찾았습니다.
“수수료 없이 다시 활용할 수 있는 전략은 무엇일까?“라는 질문을 염두에 두고 FMZ 전략 라이브러리와 커뮤니티를 살펴보았습니다. 그러던 중 익숙한 전략 하나가 눈에 띄었습니다.
이 전략은 간단하고 직관적이지만, 거래 수수료가 있는 환경에서는 거의 항상 손실로 이어집니다. 하지만 Lighter처럼 거래 수수료가 없는 환경에서는 시도해 볼 만합니다.
문제는 코드가 너무 오래되었다는 것입니다. 전략을 살펴보면 코드가 단순하고 무차별 대입 방식이며, UI 콘텐츠가 거의 없고 접근성 기능도 부족합니다. 누락된 기능의 몇 가지 예는 다음과 같습니다.
이 코드를 수동으로 리팩토링한다고요? 솔직히 좀 부담스럽네요. 하지만 저는 “편리한” 해결책을 생각해냈습니다. 바로 AI에게 리팩토링을 맡기는 거죠.
먼저, 안전장치를 마련해 두겠습니다. 이 글은 신흥 탈중앙화 거래소(DEX)에서 AI를 이용해 전략을 재구성하려는 탐색적 시도일 뿐이며, 재구성된 전략의 수익성을 보장하지는 않습니다. 자, 그럼 Claude를 사용하여 이 오래된 전략을 재구성해 보겠습니다. 코드를 처음부터 작성할 생각은 없으며, 제가 원하는 것은 다음과 같습니다.
원본 전략 코드를 복사하여 요구 사항과 함께 AI에 보냈습니다. 여러 차례의 대화와 조정 과정을 거쳐 AI는 재구성된 코드를 생성해 주었습니다.
- bash: while :; do cat PROMPT.md | claude-code ; done
- ) (배울 점들)
/*
高频做市策略 - 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("----------------------------------------");
}

FMZ의 매개변수 편집 규칙을 AI에 알려주면 AI가 인터페이스의 모든 매개변수를 직접 설정해 줄 수도 있습니다. 설명을 위해 위 그림과 같이 전략 인터페이스의 매개변수를 수동으로 설정했습니다.



솔직히 말해서, AI가 작성한 코드가 제 것보다 훨씬 낫고, 오류도 거의 없어요 (요구사항만 명확하게 설명해주면 3~4번만 수정하면 안정적으로 작동하거든요). 생산성이 폭발적으로 증가한 것 같아요 💥 지금은 당장 수익이 나지는 않지만 😂, 거래량은 꽤 많이 늘었어요.


이 경험을 되돌아보니 몇 가지 생각이 듭니다.
수수료가 없다는 것은 확실히 판도를 바꾸는 요소입니다. 과거에는 이론적으로 타당했던 많은 전략들이 거래 수수료라는 “보이지 않는 살인자” 때문에 무용지물이 되었습니다. 라이터의 수수료 무료 정책은 이러한 전략들이 다시 살아날 기회를 제공했습니다. 만약 여러분도 이와 유사한 “잠재적 전략”을 가지고 있다면, 다시 꺼내서 시도해 보는 것도 좋을 것입니다.
인공지능은 전략 개발의 진입 장벽을 크게 낮췄습니다. 이번에는 직접 코드를 작성한 부분이 거의 없었고, 전략 재구축 작업 전체를 AI를 이용해 완료했습니다. 이전에는 상상도 할 수 없었던 일입니다. 트레이딩 아이디어는 있지만 프로그래밍 실력이 부족한 사람들에게 AI와 FMZ의 조합은 분명 큰 도움이 될 것입니다.
FMZ가 새로운 거래소와 신속하게 통합된 것의 가치 FMZ는 Lighter와 같은 신흥 거래소와 빠르게 통합되어 사용자가 기회를 즉시 포착할 수 있도록 지원합니다. 또한 통합 API를 통해 기존 전략을 새로운 플랫폼으로 손쉽게 이전할 수 있습니다.
다음으로, 저는 다음과 같은 계획을 가지고 있습니다.
다른 고대 전략으로는 OK 리크 하베스터 등이 있습니다.
읽어주셔서 감사합니다. 의견이나 요청 사항이 있으시면 언제든지 말씀해 주세요.
본 글은 기술적 정보 교환 및 학습 목적으로만 작성되었으며 투자 조언을 구성하지 않습니다.
암호화폐 거래는 높은 위험을 수반합니다. 위험 관리에 유의하시고 합리적인 거래를 하시기 바랍니다.