avatar of 发明者量化-小小梦 发明者量化-小小梦
关注 私信
4
关注
1136
关注者

用AI反向学习策略设计:提升量化交易技能的新思路

创建于: 2025-04-27 10:14:30, 更新于: 2025-04-27 13:34:44
comments   2
hits   180

[TOC]

用AI反向学习策略设计:提升量化交易技能的新思路

随着人工智能(AI)技术飞速发展,已经在多个领域展现了极高的效率和创造力。量化交易作为一个高度技术化的领域,自然也在积极探索AI的应用。然而,实践中我们会发现,单纯依赖AI直接生成完整、稳定且可持续盈利的交易策略,仍然存在较大的挑战。

特别是对于平台上的新加入用户来说,由于编程技术薄弱,很难把自己的交易思路实现成策略代码。虽然目前可以把思路告诉AI,让AI输出策略。但是实施效果却不太符合预期。我也经常会遇到用户拿着AI生成的代码来咨询问题,对于AI生成的策略有时候一眼就能看出来。因为目前阶段AI生成的策略代码还有很多问题,对于AI的这样使用不仅没有解决任何问题,还给新手带来了更多的困惑、问题。可谓越学习越迷茫,最后就「从入门到放弃了」。

小编个人认为目前AI直接产出策略的问题,有主要的2个原因:

  • 每个人对于策略需求描述的逻辑性、准确性、流程清晰程度等差别很大,AI大模型对于这些自然语言的理解就会有偏差。
  • AI大模型对于平台的功能、API等理解程度有限,因为通用的模型并没有针对平台的信息进行训练,产生的信息准确度有限。

那么,是否有其他更高效的应用方式?本文想分享一种新的思考方向:让AI辅助我们学习已有策略,理解策略设计,提取关键细节与技巧,并进一步分析其有效性与改进空间。 这种方法不仅能帮助我们更快掌握策略设计的精髓,还能系统提升自己的量化交易水平。

发挥AI的优势,AI对于具体的代码分析理解程度是比较准确的,因为代码数据对于AI来说「1就是1,2就是2」不会有逻辑混乱、歧义、等自然语言描述需求时带来的问题。所以何不使用AI的优势来降低人工的工作量,充分发挥人工的优势。

如何通过AI反向学习策略?

具体可以分为以下几个步骤:

  • 选择一个已有的策略 可以是自己编写的、开源的,或是发明者量化平台上的优秀策略样本。

  • 让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给我们讲解,提升学习效率。

AI讲解

我这里使用的是GPT,以下内容为AI讲解:

  • 一、概述 本文将介绍一套基于EMA(指数移动平均线)趋势过滤的逐级加仓交易策略。 该策略主要应用在币安合约(Futures_Binance)上运行,支持1小时周期回测,基于1分钟K线数据聚合。 通过实时监控价格与EMA的偏离度,策略动态切换交易模式(正常交易/暂停交易),在平稳行情中逐步加仓,在波动异常时自动暂停以规避风险。

  • 二、策略逻辑

      1. 趋势过滤(锁仓控制) 计算最近一小时的K线数据的60周期EMA。 当现价与EMA的偏离超过3%时,暂停下单并撤销挂单。 当偏离回落到2%以内时,恢复正常交易。 通过趋势过滤,避免在剧烈行情中频繁加仓导致爆仓风险。
      1. 加仓逻辑(逐级加仓) 初始无仓位时,同时挂出低买与高卖两个挂单。 挂单价格: 买单挂在现价下方(targetProfit/5距离) 卖单挂在现价上方(targetProfit/5距离) 若挂单成交,则进行一次逐级加仓: 若买单成交,继续在更低价格挂入下一笔买单,同时设置已有持仓的卖出止盈单。 若卖单成交,继续在更高价格挂入下一笔卖单,同时设置已有持仓的买入止盈单。 加仓量可以设置是否翻倍或固定数量。 加仓次数达到maxAddCounter后,策略可以选择强制平仓清仓。
      1. 仓位控制与资金保护 根据初始总权益(totalEq)设置每次开仓的金额。 支持以USDT金额估算张数(自动按标的价格计算下单数量)。 保证账户剩余余额大于一定比例(reserve),若余额不足则停止交易。
  • 三、核心函数与模块介绍

      1. 获取账户总权益 对币安合约(Futures_Binance)和OKEX V5(Futures_OKCoin)分别封装了getTotalEquity函数。 兼容不同交易所API差异,统一返回账户当前总资产(USDT计价)。
      1. 下单与撤单模块 trade(direction, price, amount):封装统一的下单接口。 cancelAll():取消所有挂单,防止挂单过多导致风险积累。
      1. 开多、开空与平仓操作 openLong(price, amount):开多。 openShort(price, amount):开空。 coverLong(price, amount):平多。 coverShort(price, amount):平空。
      1. 加仓次数与状态记录 使用 _G() 持久化记录加仓次数addCounter,避免断线重启后加仓状态丢失。 利用 LogStatus 动态展示实时账户信息、挂单情况和持仓信息。
      1. 图表绘制 使用 KLineChart 绘制买入挂单价、卖出挂单价、持仓均价等关键线,辅助可视化回测与实时监控。
  • 四、策略流程图

    flowchart TD
      开始 --> 初始化参数
      初始化参数 --> 账户余额检查
      账户余额检查 --> 计算EMA
      计算EMA --> 判断是否锁仓
      判断是否锁仓 -->|是| 暂停交易
      判断是否锁仓 -->|否| 正常交易
      正常交易 --> 是否有持仓
      是否有持仓 -->|无| 首次挂单
      是否有持仓 -->|有| 按持仓方向加仓与止盈挂单
      首次挂单 --> 等待成交
      按持仓方向加仓与止盈挂单 --> 等待成交
      等待成交 --> 挂单成交检测
      挂单成交检测 --> 成交后调整
      成交后调整 --> 更新加仓次数
      更新加仓次数 --> 是否超出最大加仓次数
      是否超出最大加仓次数 -->|是| 平仓
      是否超出最大加仓次数 -->|否| 继续交易
    
  • 五、总结 这套基于EMA过滤的逐级加仓策略,兼顾了收益和风险控制,适合在震荡偏弱波动的市场中运行。 通过灵活的加仓方式与趋势暂停机制,可以有效降低爆仓概率,并提升资金使用效率。 同时支持回测和实盘,模块化设计易于扩展,比如可以进一步引入动态调整targetProfit、amount,或者增加其他技术指标来增强过滤效果。

    • 要不要我顺便帮你加一个进阶版建议?比如: 如何改进成双向加仓(双向持仓) 如何动态调节加仓比例 如何嵌入风控止损逻辑?

局部提问/讲解

对于GPT提问:

用AI反向学习策略设计:提升量化交易技能的新思路

以下内容为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 趋势过滤: 使用了 TA.EMA(r, 60) 计算过去60个小时的指数移动平均线(EMA)。r 是由 exchange.GetRecords 获取的历史 K 线数据,每个数据点代表一个小时的价格信息。ema 数组存储了每个小时的EMA值,ema[ema.length - 2] 代表当前时间点的前一个小时的EMA值。

趋势锁定与解除: 通过比较当前价格(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:

  • 深度学习已有策略
  • 快速理解复杂逻辑
  • 系统提炼设计经验
  • 不断优化与创新

这样的路径,才能真正提升我们的量化交易能力,建立属于自己的系统化交易体系。在发明者量化交易平台上,我们可以充分利用AI的力量,结合自己的实践,走得更远、飞得更高。

相关推荐
全部留言
avatar of qq813380629
qq813380629
cursor可以成熟的写出可以运行的策略,现在缺一个好思路,站内的高频我都复刻了一下,是正常运行的,但是模拟盘都没法盈利是为啥呢
2025-04-27 18:56:07
avatar of 发明者量化-小小梦
发明者量化-小小梦
是回测吗?还是交易所的模拟盘?
2025-04-27 21:00:25