With the rapid development of artificial intelligence (AI) technology, it has demonstrated extremely high efficiency and creativity in many fields. As a highly technical field, quantitative trading is also exploring the application of AI actively. However, in practice, we will find that there are still great challenges in simply relying on AI to directly generate complete, stable and sustainable profitable trading strategies.
Especially for new users on the platform, due to weak programming skills, it is difficult to implement their trading ideas into strategy codes. Although it is currently possible to tell AI the ideas and let AI output strategies. But the implementation effect is not quite as expected. I often encounter users who come to consult with AI-generated codes, and sometimes the AI-generated strategies can be seen through at a glance. Because there are still many problems with the strategy codes generated by AI at this stage, the use of AI in this way not only does not solve any problems, but also brings more confusion and problems to novices. It can be said that the more you learn, the more confused you become, and finally "from entry to giving up".
The editor personally believes that there are two main reasons for the current problem of AI directly outputting strategies:
- The logic, accuracy, and process clarity of each person's description of strategic requirements vary greatly, and the AI model's understanding of these natural languages will be biased.
- The AI model has a limited understanding of the platform's functions, APIs, etc., because the general model is not trained on the platform's information, and the accuracy of the information generated is limited.
So, are there other more efficient application methods? This article wants to share a new way of thinking: let AI help us learn existing strategies, understand strategy design, extract key details and techniques, and further analyze their effectiveness and room for improvement. This method can not only help us grasp the essence of strategy design more quickly, but also systematically improve our quantitative trading level.
Taking advantage of AI, AI can understand specific code analysis more accurately, because for AI, code data is "1 is 1, 2 is 2" and there will be no logical confusion, ambiguity, or other problems caused by natural language description requirements. So why not use the advantages of AI to reduce manual workload and give full play to the advantages of manual labor?
How to Use AI Reverse Learning Strategy?
It can be divided into the following steps:
- Choose an existing strategy
It can be one you wrote yourself, open source, or an excellent strategy sample on the FMZ Quant Platform. - Let AI help us explain the strategy
Understand the overall idea
Sort out the functional modules of each part
Define the indicators, parameters, and trading logic used clearly - Ask further questions to guide AI to analyze the pros and cons of the strategy
In what market conditions does the strategy perform better?
What are the possible risk points?
What areas can be optimized and improved? - Verify and adjust based on your own thinking
Backtest on different products and different periods
Add additional filtering conditions or risk control measures
Observe performance changes and form your own insights
Practice
Let AI learn a strategy and explain it to us to see if it meets our expectations. It can help us learn quantitative analysis.
Strategy Sample
Design and implementation of step-by-step position increase trading strategy based on EMA trend filtering
Strategy address: 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]] */functiongetTotalEquity_OKEX_V5() { var totalEquity = nullvar 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("Failed to obtain the total account equity!") returnnull } } return totalEquity } functiongetTotalEquity_Binance() { var totalEquity = nullvar ret = exchange.GetAccount() if (ret) { try { totalEquity = parseFloat(ret.Info.totalWalletBalance) } catch(e) { Log("Failed to obtain the total account equity!") returnnull } } return totalEquity } functiongetTotalEquity() { var exName = exchange.GetName() if (exName == "Futures_OKCoin") { returngetTotalEquity_OKEX_V5() } elseif (exName == "Futures_Binance") { returngetTotalEquity_Binance() } else { throw"The exchange is not supported" } } functionceilToDecimals(value, decimals) { const factor = Math.pow(10, decimals); returnMath.ceil(value * factor) / factor; } functioncancelAll() { 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) } } functiontrade(distance, price, amount) { var tradeFunc = nullif (distance == "buy") { tradeFunc = exchange.Buy } elseif (distance == "sell") { tradeFunc = exchange.Sell } elseif (distance == "closebuy") { tradeFunc = exchange.Sell } else { tradeFunc = exchange.Buy } exchange.SetDirection(distance) returntradeFunc(price, amount) } functionopenLong(price, amount) { returntrade("buy", price, amount) } functionopenShort(price, amount) { returntrade("sell", price, amount) } functioncoverLong(price, amount) { returntrade("closebuy", price, amount) } functioncoverShort(price, amount) { returntrade("closesell", price, amount) } functionplotRecords(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 = nullvar sellOrderId = nullvar logStatusMsgBuff = ""functionmain() { var exName = exchange.GetName() if (isSimulate && exName == "Futures_OKCoin") { exchange.IO("simulate", true) } if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("Reset all data", "#FF0000") } exchange.SetContractType(contractType) exchange.SetPrecision(pricePrecision, amountPrecision) Log("Setting the precision", 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"Failed to obtain initial equity" } } else { totalEq = recoverTotalEq } } var addCounter = _G("addCounter") if (!addCounter) { addCounter = 1if (setAddCounter != -1) { addCounter = setAddCounter } _G("addCounter", addCounter) } else { addCounter -= 1 } let c = KLineChart({ overlay: true }) var isLock = falsewhile (true) { var ticker = _C(exchange.GetTicker) var pos = _C(exchange.GetPosition) if (pos.length > 1) { Log(pos) throw"Long and short positions at the same time" } 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(), "Pause, detection threshold:", _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(), "The position increase limit has been reached", logStatusMsgBuff) if (isMaxAddCounterClear && pos.length >= 1) { Log("The position increase has reached the upper limit, cancel the order, clear the position") cancelAll() if (pos[0].Type == PD_LONG) { var coverId = coverLong(-1, pos[0].Amount) } elseif (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, "Current total equity:", 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) } elseif (pos[0].Type == PD_LONG) { var n = ratio var price = ticker.Lastvar 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) } elseif (pos[0].Type == PD_SHORT) { var n = ratio var price = ticker.Lastvar 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 = nullcontinue } while (1) { var isFindBuyId = falsevar isFindSellId = falsevar orders = _C(exchange.GetOrders) var buyOrder = nullvar sellOrder = nullfor (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 } elseif (!isFindBuyId) { Log("Buy order completed") cancelAll() break } elseif (!isFindSellId) { Log("Sell order completed") 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(["order", buyOrder.Symbol, buyOrder.Type == ORDER_TYPE_BUY ? "buy" : "sell", buyOrder.Price, buyOrder.Amount]) } if (sellOrder) { tbl.rows.push(["order", sellOrder.Symbol, sellOrder.Type == ORDER_TYPE_BUY ? "buy" : "sell", sellOrder.Price, sellOrder.Amount]) } if (pos && pos.length == 1) { tbl.rows.push(["position", pos[0].Symbol, pos[0].Type == PD_LONG ? "long" : "short", pos[0].Price, pos[0].Amount]) } logStatusMsgBuff = "Current equity:" + acc.Equity + ", Initial equity:" + totalEq + (!IsVirtual() ? ", Floating profit and loss:" + (acc.Equity - totalEq) : "") + ", Number of positions added:" + addCounter + "\n`" + JSON.stringify(tbl) + "`"LogStatus(_D(), "Current equity:", acc.Equity, ", Initial equity:", totalEq, !IsVirtual() ? ", Floating profit and loss:" + (acc.Equity - totalEq) : "", ", Number of positions added:" + addCounter, "\n`" + JSON.stringify(tbl) + "`") plotRecords(c, buyOrder, sellOrder, pos) Sleep(interval) } Sleep(interval) } } functiononexit() { Log("Clean up and cancel all pending orders") cancelAll() }
For novices of the FMZ platform, seeing such a pile of code may be overwhelming. Instead of studying the code line by line, it is better to throw it to AI (to be lazy) and let AI explain it to us to improve learning efficiency.
AI Explanation
I am using GPT here, and the following content is explained by AI:
- I. Overview
This article will introduce a set of step-by-step position-adding trading strategies based on EMA (Exponential Moving Average) trend filtering.
This strategy is mainly used on Futures_Binance, supports 1-hour period backtesting, and is based on 1-minute K-line data aggregation.
By real-time monitoring of the deviation between price and EMA, the strategy dynamically switches trading modes (normal trading/suspended trading), gradually adds positions in a stable market, and suspends automatically when there is abnormal fluctuation to avoid risks. - II. Strategy logic
- Trend filtering (lock position control)
Calculate the 60-period EMA of the K-line data of the last hour.
When the current price deviates from the EMA by more than 3%, suspend the order and cancel the pending order.
When the deviation falls back to within 2%, resume normal trading.
Through trend filtering, avoid the risk of liquidation caused by frequent increase of positions in turbulent market conditions. - Position adding logic (step-by-step position adding)
When there is no position initially, place two pending orders, buy low and sell high at the same time.
Pending order price:
Buy order is placed below the current price (targetProfit/5 distance)
Sell order is placed above the current price (targetProfit/5 distance)
If the pending order is executed, a step-by-step position adding is performed:
If the buy order is executed, continue to place the next buy order at a lower price, and set the sell stop profit order for the existing position at the same time.
If the sell order is executed, continue to place the next sell order at a higher price, and set the buy stop profit order for the existing position at the same time.
The position adding amount can be set to double or fixed.
After the number of position adding reaches maxAddCounter, the strategy can choose to force liquidation. - Position control and capital protection
Set the amount of each position opened based on the initial total equity (totalEq).
Support estimating the number of contracts based on the USDT amount (calculating the order amount based on the underlying price automatically).
Ensure that the remaining balance in the account is greater than a certain proportion (reserve). If the balance is insufficient, stop trading.
- III. Introduction to core functions and modules
- Get total account equity
The getTotalEquity function is encapsulated for Futures_Binance and OKEX V5 (Futures_OKCoin).
Compatible with API differences of different exchanges, return the current total assets of the account (denominated in USDT) uniformly.
- Order placement and cancellation module
trade(direction, price, amount): encapsulates a unified order placement interface.
cancelAll(): cancel all pending orders to prevent risk accumulation caused by too many pending orders. - Open long, open short and close position operations
openLong(price, amount): open long.
openShort(price, amount): open short.
coverLong(price, amount): close long.
coverShort(price, amount): close short. - Number of position additions and status records
Use _G() to persistently record the number of position additions addCounter to avoid loss of position addition status after disconnection and restart.
Use LogStatus to dynamically display real-time account information, pending order status and position information. - PLot chart
Use KLineChart to draw key lines such as buy order price, sell order price, average position price, etc. to assist in visual backtesting and real-time monitoring.
- IV. Strategy Flowchart
flowchart TDStart --> Initialize parameters Initialize parameters --> Check account balance Check account balance --> CalculateEMACalculateEMA --> Determine whether to lock positions Determine whether to lock positions -->|Yes| Suspend trading Determine whether to lock positions -->|No| Normal trading Normal trading --> Is there a positionIs there a position -->|No| First pending orderIs there a position -->|Yes| Add positions and take-profit pending orders according to the direction of positions First pending order --> Waitfor transaction Add positions and take-profit pending orders according to the direction of positions --> Waitfor transaction Waitfor transaction --> Order transaction detection Order transaction detection --> Adjust after transaction Adjust after transaction --> Update the number of positions added Update the number of positions added --> Whether the maximum number of positions added has been exceeded Whether the maximum number of positions added has been exceeded -->|Yes| Close the positionWhether the maximum number of positions added has been exceeded -->|No| Continue trading
- V. Summary
This step-by-step position-adding strategy based on EMA filtering takes into account both profits and risk control, and is suitable for operating in a market with weak fluctuations.
Through flexible position-adding methods and trend pause mechanisms, the probability of liquidation can be effectively reduced and the efficiency of fund use can be improved.
It supports both backtesting and live trading, and the modular design is easy to expand. For example, dynamic adjustment of targetProfit and amount can be further introduced, or other technical indicators can be added to enhance the filtering effect.
Do you want me to add an advanced version of the suggestion for you? For example:
How to improve to bidirectional position-adding (position bidirection)?
How to adjust the position-adding ratio dynamically?
How to embed risk control stop loss logic?
Local Questions/Explanations
For GPT questions:
Please explain to me in detail how this strategy is designed to filter trends, based on specific code analysis.
OK, let me explain in detail how this strategy designs trend filtering and how it is implemented in the code. We will focus on the following aspects:
- EMA (Exponential Moving Average) trend filtering principle
- How to calculate EMA and decide whether to suspend trading based on its deviation
- Code implementation and strategy flow
The following content is explained by AI:
In this strategy, trend filtering is achieved by:
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(), "Pause, detection threshold:", _N(Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2], 3), logStatusMsgBuff) plotRecords(c, null, null, pos) Sleep(interval) continue }
- Code analysis
EMA trend filter:
TA.EMA(r, 60) is used to calculate the exponential moving average (EMA) of the past 60 hours. r is the historical K-line data obtained by exchange.GetRecords, and each data point represents the price information of one hour. The ema array stores the EMA value of each hour, and ema[ema.length - 2] represents the EMA value of the previous hour at the current time point.
Trend lock and release:
By comparing the difference between the current price (ticker.Last) and the EMA value of the previous hour, calculate the percentage of price deviation from EMA.
Math.abs(ticker.Last - ema[ema.length - 2]) / ema[ema.length - 2] calculates the percentage of deviation between the current price and the EMA of the previous hour.
If the deviation exceeds 3% (> 0.03), it is considered that the market trend has changed significantly, and the cancelAll() function is called to cancel all pending orders, and isLock is set to true, that is, the locked position state, preventing further trading.
If the price deviates from EMA by less than 2% (< 0.02), the locked position state is released and isLock is set to false.
Suspend trading:
If isLock is true (indicating that it is currently in the locked position state), no further trading operations are performed.
The program will output log records and call plotRecords() to draw a chart to show the current trend and position status.
Sleep(interval) is used to pause for a certain period of time and then continue the loop.
- Backtesting

It seems that some trends can be filtered out. By reading the code myself, I found that the AI explanation was very clear, even clearer than me explaining it.
Summary
By letting AI analyze and explain the overall strategy, and then based on AI's explanation of the overall strategy, analyze, disassemble, and explain the local details of the strategy gradually, and explain the design ideas. The explanation content obtained is accurate. AI lists almost all the design details in the strategy without omissions, and will also conduct detailed analysis through further questions. In this way, learning the ideas of the entire strategy, code design details, and increasing strategy design experience is improved.
On the road of quantitative trading, AI can become our extremely powerful learning and growth partner. Instead of relying on AI to generate finished strategies with one click, it is better to use AI:
- Deep learning existing strategies
- Understand complex logic quickly
- Systematic design experience
- Continuous optimization and innovation
Only by following this path can we truly improve our quantitative trading capabilities and establish our own systematic trading system. On the FMZ Quant Trading Platform, we can make full use of the power of AI and combine it with our own practice to go further.
From: Using AI to Learn Strategy Design Reversely: A New Way to Improve Quantitative Trading Skills