[TOC]
随着人工智能(AI)技术飞速发展,已经在多个领域展现了极高的效率和创造力。量化交易作为一个高度技术化的领域,自然也在积极探索AI的应用。然而,实践中我们会发现,单纯依赖AI直接生成完整、稳定且可持续盈利的交易策略,仍然存在较大的挑战。
特别是对于平台上的新加入用户来说,由于编程技术薄弱,很难把自己的交易思路实现成策略代码。虽然目前可以把思路告诉AI,让AI输出策略。但是实施效果却不太符合预期。我也经常会遇到用户拿着AI生成的代码来咨询问题,对于AI生成的策略有时候一眼就能看出来。因为目前阶段AI生成的策略代码还有很多问题,对于AI的这样使用不仅没有解决任何问题,还给新手带来了更多的困惑、问题。可谓越学习越迷茫,最后就「从入门到放弃了」。
小编个人认为目前AI直接产出策略的问题,有主要的2个原因:
那么,是否有其他更高效的应用方式?本文想分享一种新的思考方向:让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讲解:
一、概述 本文将介绍一套基于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
}
趋势锁定与解除: 通过比较当前价格(ticker.Last)与前一个小时的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:
这样的路径,才能真正提升我们的量化交易能力,建立属于自己的系统化交易体系。在发明者量化交易平台上,我们可以充分利用AI的力量,结合自己的实践,走得更远、飞得更高。