[TOC]

인공지능(AI) 기술의 급속한 발전으로 많은 분야에서 매우 높은 효율성과 창의성이 입증되었습니다. 양적 거래는 고도로 기술적인 분야이기 때문에 자연스럽게 AI의 적용을 적극적으로 모색하고 있습니다. 그러나 실제로는 AI에만 의존해 완전하고 안정적이며 지속 가능한 수익성 있는 거래 전략을 생성하는 데는 여전히 큰 어려움이 있다는 것을 알게 될 것입니다.
특히 플랫폼을 처음 사용하는 사용자의 경우 프로그래밍 기술이 부족하여 거래 아이디어를 전략 코드로 구현하는 데 어려움을 겪습니다. 현재 우리는 AI에게 아이디어를 전달하고 전략을 출력할 수 있습니다. 하지만 구현 효과는 기대에 미치지 못했습니다. AI가 생성한 코드를 가지고 질문을 하러 오는 사용자를 자주 만나는데, 가끔은 AI가 생성한 전략을 한눈에 볼 수도 있습니다. 이 단계에서는 AI가 생성하는 전략 코드에 여전히 많은 문제점이 있기 때문에, 이런 방식으로 AI를 활용하면 아무런 문제도 해결되지 않을 뿐만 아니라, 초보자에게는 더 큰 혼란과 문제만 안겨줄 뿐입니다. 더 많이 배울수록 더 혼란스러워졌고 결국 “시작하는 것을 포기”했습니다.
저는 개인적으로 AI 직접 출력 전략의 현재 문제에는 두 가지 주요 원인이 있다고 생각합니다.
그렇다면, 더 효율적인 다른 신청 방법은 있을까요? 이 글에서는 새로운 사고방식을 공유하고자 합니다. AI를 통해 기존 전략을 학습하고, 전략 설계를 이해하고, 주요 세부 사항과 기술을 추출하고, 효과와 개선 여지를 더욱 자세히 분석하는 것입니다. 이 방법은 우리가 전략 설계의 본질을 더 빨리 파악하는 데 도움이 될 뿐만 아니라, 체계적으로 우리의 양적 거래 수준을 향상시킬 수도 있습니다.
AI의 장점을 활용하면 AI는 특정 코드 분석을 비교적 정확하게 이해할 수 있습니다. AI에게 코드 데이터는 “1은 1, 2는 2”이고 자연어 설명 요구 사항으로 인해 발생하는 논리적 혼란, 모호함 또는 기타 문제가 발생하지 않기 때문입니다. 그렇다면 AI의 장점을 활용해 수동 작업량을 줄이고 수동 노동의 장점을 최대한 활용하는 건 어떨까요?
이는 다음 단계로 나눌 수 있습니다.
기존 전략을 선택하세요 이는 본인이 직접 작성한 것일 수도 있고, 오픈 소스일 수도 있으며, 발명가의 양적 플랫폼에 있는 훌륭한 전략 샘플일 수도 있습니다.
AI가 전략을 설명하는 데 도움을 주도록 하세요
전반적인 아이디어를 이해하세요
각 부분의 기능 모듈을 정리합니다
사용된 지표, 매개변수 및 거래 논리를 명확히 합니다.
어떤 시장 상황에서 이 전략의 성과가 더 좋은가?
발생 가능한 위험 요소는 무엇인가?
어떤 부분을 최적화하고 개선할 수 있나요?
다양한 제품 및 기간에 대한 백테스팅
추가 필터 또는 위험 관리 조치 추가
성과 변화를 관찰하고 나만의 통찰력을 형성하세요
AI가 전략을 학습하고 이를 우리에게 설명하여 기대에 부응하는지 살펴보겠습니다. 이는 우리가 양적화를 배우는 데 도움이 될 수 있습니다.
EMA 추세 필터링을 기반으로 한 단계별 포지션 증가 거래 전략의 설계 및 구현 전략 주소: https://www.fmz.com/strategy/492116
/*backtest
start: 2024-10-01 00:00:00
end: 2025-04-23 00:00:00
period: 1h
basePeriod: 1m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
args: [["targetProfit",20],["amount",20],["amountPrecision",3],["isAmountForUSDT",true]]
*/
function getTotalEquity_OKEX_V5() {
var totalEquity = null
var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT")
if (ret) {
try {
totalEquity = parseFloat(ret.data[0].details[0].eq)
} catch(e) {
Log("获取账户总权益失败!")
return null
}
}
return totalEquity
}
function getTotalEquity_Binance() {
var totalEquity = null
var ret = exchange.GetAccount()
if (ret) {
try {
totalEquity = parseFloat(ret.Info.totalWalletBalance)
} catch(e) {
Log("获取账户总权益失败!")
return null
}
}
return totalEquity
}
function getTotalEquity() {
var exName = exchange.GetName()
if (exName == "Futures_OKCoin") {
return getTotalEquity_OKEX_V5()
} else if (exName == "Futures_Binance") {
return getTotalEquity_Binance()
} else {
throw "不支持该交易所"
}
}
function ceilToDecimals(value, decimals) {
const factor = Math.pow(10, decimals);
return Math.ceil(value * factor) / factor;
}
function cancelAll() {
while (1) {
var orders = _C(exchange.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
exchange.CancelOrder(orders[i].Id, orders[i])
Sleep(interval)
}
Sleep(interval)
}
}
function trade(distance, price, amount) {
var tradeFunc = null
if (distance == "buy") {
tradeFunc = exchange.Buy
} else if (distance == "sell") {
tradeFunc = exchange.Sell
} else if (distance == "closebuy") {
tradeFunc = exchange.Sell
} else {
tradeFunc = exchange.Buy
}
exchange.SetDirection(distance)
return tradeFunc(price, amount)
}
function openLong(price, amount) {
return trade("buy", price, amount)
}
function openShort(price, amount) {
return trade("sell", price, amount)
}
function coverLong(price, amount) {
return trade("closebuy", price, amount)
}
function coverShort(price, amount) {
return trade("closesell", price, amount)
}
function plotRecords(c, buyOrder, sellOrder, pos) {
var bars = _C(exchange.GetRecords)
if (bars.length == 0) {
return
}
bars.forEach(function(bar, index) {
c.begin(bar)
if (index == bars.length - 1) {
if (buyOrder) {
c.hline(buyOrder.Price, "buy", "rgba(255, 0, 0, 0.2)", "dotted")
}
if (sellOrder) {
c.hline(sellOrder.Price, "sell", "rgba(0, 255, 0, 0.2)", "dotted")
}
if (pos && pos.length == 1) {
c.hline(pos[0].Price, "pos", "rgba(0, 0, 255, 0.2)", "dashed")
}
}
c.close()
})
}
var buyOrderId = null
var sellOrderId = null
var logStatusMsgBuff = ""
function main() {
var exName = exchange.GetName()
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
exchange.SetContractType(contractType)
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("设置精度", pricePrecision, amountPrecision)
exchange.SetMarginLevel(marginLevel)
if (totalEq == -1 && !IsVirtual()) {
var recoverTotalEq = _G("totalEq")
if (!recoverTotalEq) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
totalEq = currTotalEq
_G("totalEq", currTotalEq)
} else {
throw "获取初始权益失败"
}
} else {
totalEq = recoverTotalEq
}
}
var addCounter = _G("addCounter")
if (!addCounter) {
addCounter = 1
if (setAddCounter != -1) {
addCounter = setAddCounter
}
_G("addCounter", addCounter)
} else {
addCounter -= 1
}
let c = KLineChart({
overlay: true
})
var isLock = false
while (true) {
var ticker = _C(exchange.GetTicker)
var pos = _C(exchange.GetPosition)
if (pos.length > 1) {
Log(pos)
throw "同时有多空持仓"
}
var r = _C(exchange.GetRecords, 60 * 60)
var ema = TA.EMA(r, 60)
if (Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2] > 0.03) {
cancelAll()
isLock = true
}
if (Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2] < 0.02) {
isLock = false
}
if (isLock) {
LogStatus(_D(), "暂停, 检测阈值:", _N(Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2], 3), logStatusMsgBuff)
plotRecords(c, null, null, pos)
Sleep(interval)
continue
}
var currentAcc = _C(exchange.GetAccount)
if (currentAcc.Balance < totalEq * reserve) {
throw "no money, stop"
}
if (addCounter > maxAddCounter) {
LogStatus(_D(), "加仓已达到上限", logStatusMsgBuff)
if (isMaxAddCounterClear && pos.length >= 1) {
Log("加仓已达到上限,撤单,清仓")
cancelAll()
if (pos[0].Type == PD_LONG) {
var coverId = coverLong(-1, pos[0].Amount)
} else if (pos[0].Type == PD_SHORT) {
var coverId = coverShort(-1, pos[0].Amount)
}
addCounter = 1
}
continue
}
if (pos.length == 0) {
if (!IsVirtual()) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq)
}
}
var tradeAmountLong = amount
var tradeAmountShort = amount
if (isAmountForUSDT) {
tradeAmountLong = ceilToDecimals(tradeAmountLong * 1.01 / (ticker.Last - targetProfit / 5) / oneCtValue, amountPrecision)
tradeAmountShort = ceilToDecimals(tradeAmountShort * 1.01 / (ticker.Last + targetProfit / 5) / oneCtValue, amountPrecision)
}
buyOrderId = openLong(ticker.Last - targetProfit / 5, tradeAmountLong)
sellOrderId = openShort(ticker.Last + targetProfit / 5, tradeAmountShort)
addCounter = 1
_G("addCounter", addCounter)
} else if (pos[0].Type == PD_LONG) {
var n = ratio
var price = ticker.Last
var addAmount = isDoubling ? pos[0].Amount : (isAmountForUSDT ? (ceilToDecimals(amount * 1.01 / (price - targetProfit * n) / oneCtValue, amountPrecision)) : amount)
buyOrderId = openLong(price - targetProfit * n, addAmount)
sellOrderId = coverLong(pos[0].Price + targetProfit, pos[0].Amount)
addCounter++
_G("addCounter", addCounter)
} else if (pos[0].Type == PD_SHORT) {
var n = ratio
var price = ticker.Last
var addAmount = isDoubling ? pos[0].Amount : (isAmountForUSDT ? (ceilToDecimals(amount * 1.01 / (price + targetProfit * n) / oneCtValue, amountPrecision)) : amount)
buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount)
sellOrderId = openShort(price + targetProfit * n, addAmount)
addCounter++
_G("addCounter", addCounter)
}
if (!sellOrderId || !buyOrderId) {
cancelAll()
buyOrderId = null
sellOrderId = null
continue
}
while (1) {
var isFindBuyId = false
var isFindSellId = false
var orders = _C(exchange.GetOrders)
var buyOrder = null
var sellOrder = null
for (var i = 0 ; i < orders.length ; i++) {
if (buyOrderId == orders[i].Id) {
isFindBuyId = true
buyOrder = orders[i]
}
if (sellOrderId == orders[i].Id) {
isFindSellId = true
sellOrder = orders[i]
}
}
if (!isFindSellId && !isFindBuyId) {
cancelAll()
break
} else if (!isFindBuyId) {
Log("买单成交")
cancelAll()
break
} else if (!isFindSellId) {
Log("卖单成交")
cancelAll()
break
}
var acc = _C(exchange.GetAccount)
var tbl = {"type": "table", "title": "data", "cols": ["data", "symbol", "type", "price", "amount"], "rows": []}
if (buyOrder) {
tbl.rows.push(["订单", buyOrder.Symbol, buyOrder.Type == ORDER_TYPE_BUY ? "买入" : "卖出", buyOrder.Price, buyOrder.Amount])
}
if (sellOrder) {
tbl.rows.push(["订单", sellOrder.Symbol, sellOrder.Type == ORDER_TYPE_BUY ? "买入" : "卖出", sellOrder.Price, sellOrder.Amount])
}
if (pos && pos.length == 1) {
tbl.rows.push(["持仓", pos[0].Symbol, pos[0].Type == PD_LONG ? "多" : "空", pos[0].Price, pos[0].Amount])
}
logStatusMsgBuff = "当前权益:" + acc.Equity + ", 初始权益:" + totalEq + (!IsVirtual() ? ", 浮动盈亏:" + (acc.Equity - totalEq) : "") + ", 加仓次数:" + addCounter + "\n`" + JSON.stringify(tbl) + "`"
LogStatus(_D(), "当前权益:", acc.Equity, ", 初始权益:", totalEq, !IsVirtual() ? ", 浮动盈亏:" + (acc.Equity - totalEq) : "", ", 加仓次数:" + addCounter, "\n`" + JSON.stringify(tbl) + "`")
plotRecords(c, buyOrder, sellOrder, pos)
Sleep(interval)
}
Sleep(interval)
}
}
function onexit() {
Log("扫尾,撤销所有挂单")
cancelAll()
}
FMZ 플랫폼을 처음 사용하는 사람이라면 이렇게 많은 코드를 보면 당황스러울 수도 있습니다. 코드를 줄마다 공부하는 대신, (게으름을 피우며) AI에게 코드를 던져주고, AI가 설명해 주게 하면 학습 효율이 향상될 수도 있습니다.
여기서는 GPT를 사용하고 있으며, 다음 내용은 AI가 설명합니다.
1 개요 이 글에서는 EMA(지수 이동 평균) 추세 필터링을 기반으로 한 단계별 포지션 증가 거래 전략을 소개합니다. 이 전략은 주로 바이낸스 퓨처스(Futures_Binance)에서 사용되고, 1시간 백테스팅을 지원하며, 1분 K-라인 데이터 집계를 기반으로 합니다. 가격과 EMA의 편차를 실시간으로 모니터링하여, 거래 모드(정상 거래/거래 중지)를 동적으로 전환하고, 안정적인 시장에서는 포지션을 점진적으로 늘리고, 비정상적인 변동이 발생하는 경우 자동으로 포지션을 중지하여 위험을 회피하는 전략입니다.
flowchart TD
开始 --> 初始化参数
初始化参数 --> 账户余额检查
账户余额检查 --> 计算EMA
计算EMA --> 判断是否锁仓
判断是否锁仓 -->|是| 暂停交易
判断是否锁仓 -->|否| 正常交易
正常交易 --> 是否有持仓
是否有持仓 -->|无| 首次挂单
是否有持仓 -->|有| 按持仓方向加仓与止盈挂单
首次挂单 --> 等待成交
按持仓方向加仓与止盈挂单 --> 等待成交
等待成交 --> 挂单成交检测
挂单成交检测 --> 成交后调整
成交后调整 --> 更新加仓次数
更新加仓次数 --> 是否超出最大加仓次数
是否超出最大加仓次数 -->|是| 平仓
是否超出最大加仓次数 -->|否| 继续交易
다섯째, 요약 EMA 필터링을 기반으로 한 이 단계별 포지션 증가 전략은 수익과 위험 관리를 모두 고려하며, 변동성이 큰 시장과 변동성이 약한 시장에서 운영하는 데 적합합니다. 포지션 추가와 추세 일시 정지 메커니즘을 유연하게 활용하면 청산 가능성을 효과적으로 줄이고 자본 사용 효율성을 개선할 수 있습니다. 백테스팅과 실제 거래를 모두 지원하며, 모듈식 설계로 확장이 쉽습니다. 예를 들어, targetProfit과 amount를 더욱 동적으로 조정하거나 다른 기술 지표를 추가하여 필터링 효과를 강화할 수 있습니다.
GPT 질문에 관하여:

다음 내용은 AI가 설명합니다.
이 전략에서는 다음을 통해 추세 필터링이 달성됩니다.
var ema = TA.EMA(r, 60)
if (Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2] > 0.03) {
cancelAll()
isLock = true
}
if (Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2] < 0.02) {
isLock = false
}
if (isLock) {
LogStatus(_D(), "暂停, 检测阈值:", _N(Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2], 3), logStatusMsgBuff)
plotRecords(c, null, null, pos)
Sleep(interval)
continue
}
트렌드 잠금 및 해제: 현재 가격(티커 마지막)과 이전 시간의 EMA 값의 차이를 비교하여 가격이 EMA에서 벗어난 비율을 계산합니다. Math.abs(ticker.Last - ema[ema.length - 2]) / ema[[ema.length - 2]는 현재 가격과 이전 시간의 EMA의 백분율 편차를 계산합니다. 편차가 3%(> 0.03)를 초과하면 시장 추세가 상당히 변화한 것으로 간주되어 cancelAll() 함수가 호출되어 보류 중인 모든 주문을 취소하고 isLock이 true로 설정되어 포지션이 잠기고 추가 거래가 불가능합니다. 가격이 EMA에서 2% 미만(< 0.02)으로 벗어나면 잠금 상태가 해제되고 isLock이 false로 설정됩니다.
거래 중단: isLock이 true(포지션이 현재 잠겨 있음을 나타냄)인 경우 더 이상 거래 작업이 수행되지 않습니다. 이 프로그램은 로그 레코드를 출력하고 plotRecords()를 호출하여 현재 추세와 위치 상태를 보여주는 차트를 그립니다. Sleep(interval)을 사용하면 특정 시간 동안 일시 중지한 다음 루프를 계속 진행할 수 있습니다.

일부 추세는 걸러질 수 있는 것 같습니다. 코드를 직접 읽어보니 AI의 설명이 매우 명확했고, 제가 직접 설명했을 때보다 더 명확했습니다.
AI가 전반적인 전략을 분석하고 설명하게 한 다음, AI가 전반적인 전략을 설명한 것을 바탕으로 점진적으로 전략의 지역적 세부 사항을 분석, 분해, 설명하고 디자인 아이디어를 설명합니다. 우리가 받은 설명은 비교적 정확했습니다. AI는 전략에 설계된 세부 사항을 거의 모두 누락 없이 나열하고, 추가적인 질문을 통해 세부적인 분석을 수행했습니다. 이런 식으로 전략적 사고 전반을 배우고, 코드 설계 세부 사항을 익히고, 전략 설계 경험을 늘리는 건 모두 매우 도움이 됩니다.
양적 거래의 길에서 AI는 우리에게 매우 강력한 학습 및 성장 파트너가 될 수 있습니다. AI가 한 번의 클릭으로 완성된 전략을 생성하는 대신, AI는 다음을 수행할 수 있습니다.
오직 이 길을 통해서만 우리의 양적 거래 역량을 진정으로 강화하고, 우리만의 체계적인 거래 시스템을 확립할 수 있습니다. Inventor 양적 거래 플랫폼을 이용하면 AI의 힘을 최대한 활용하고 이를 우리의 실무와 결합하여 더 멀리 나아가 더 높이 날아오를 수 있습니다.