<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dream</title>
    <description>The latest articles on Forem by Dream (@quant001).</description>
    <link>https://forem.com/quant001</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3732259%2Fd089f60c-f2a4-4ba4-a84d-6c79ada78de3.png</url>
      <title>Forem: Dream</title>
      <link>https://forem.com/quant001</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/quant001"/>
    <language>en</language>
    <item>
      <title>Designing and Implementing a Multi-Role AI-Powered Trading System for Polymarket</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Tue, 14 Apr 2026 06:49:54 +0000</pubDate>
      <link>https://forem.com/quant001/designing-and-implementing-a-multi-role-ai-powered-trading-system-for-polymarket-2ceb</link>
      <guid>https://forem.com/quant001/designing-and-implementing-a-multi-role-ai-powered-trading-system-for-polymarket-2ceb</guid>
      <description>&lt;h2&gt;
  
  
  Strategy Background
&lt;/h2&gt;

&lt;p&gt;Earlier this year, stories about someone making a fortune overnight on Polymarket were circulating widely across communities. Curious about the actual trading framework behind it, I went digging—only to find nothing but clickbait headlines without a single line of code. So I had no choice but to build one myself.&lt;/p&gt;

&lt;p&gt;Honestly, I had no clue where to start. Polymarket is currently the largest decentralized prediction market platform, with over 60,000 listed markets covering politics, sports, macroeconomics, and virtually every forecastable event. Each market is essentially a binary options contract—if the outcome occurs, the price converges to 1; if not, it goes to zero. The most profitable edge? Insider information, obviously. But regular people don't know how many tweets Elon will post next week, don't know whether the Fed will cut rates next time, and certainly don't have the time or energy to monitor markets, analyze data, and make decisions around the clock.&lt;/p&gt;

&lt;p&gt;So I thought: what if we flip the approach—instead of chasing information, chase the money. Those with an information edge will position themselves early, and their buying activity inevitably leaves traces in price and volume data. Even without knowing the "inside story," you can infer whether someone is quietly building a position by observing capital behavior. Following this logic, I built a multi-role AI-powered analysis and trading system that strings together K-line signal detection, news verification, AI-driven judgment, and automated order execution with risk controls into a single end-to-end pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qbga4fglji8ltotw97j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qbga4fglji8ltotw97j.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture
&lt;/h2&gt;

&lt;p&gt;The entire system runs on FMZ Quant's workflow engine, split into two parallel scheduling paths—a main line and a secondary line—fully decoupled and non-blocking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f56zc4y8mz8v0ds5uz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f56zc4y8mz8v0ds5uz8.png" alt=" " width="800" height="80"&gt;&lt;/a&gt;&lt;br&gt;
The main line handles "finding opportunities and opening positions." Each cycle consists of four sequential stages: initial screening → K-line anomaly detection → news search → multi-role AI analysis, ultimately producing a buy signal that feeds into order execution. The secondary line only manages "existing positions," running continuously on a 30-second cycle regardless of whether the main line has any in-flight tasks.&lt;/p&gt;

&lt;p&gt;This separation addresses a practical problem: prediction market prices move continuously, but stock-picking analysis is computationally intensive and high-latency. If stop-loss monitoring were crammed into the main line, stop-loss response would severely lag whenever AI analysis runs slow. Independent scheduling is a necessary engineering trade-off.&lt;/p&gt;
&lt;h2&gt;
  
  
  Module Deep Dive
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Initial Screening: Filtering Out the Noise&lt;/strong&gt;&lt;br&gt;
With 60,000 markets, analyzing each one individually is impossible. The first gate is pure rule-based filtering with a single objective: rapidly eliminate obviously unworthy candidates and reserve downstream computational resources for truly valuable targets.&lt;/p&gt;

&lt;p&gt;Screening criteria include: minimum liquidity (too low means trading difficulty), minimum 24-hour volume (markets with virtually no trading lack reference value), maximum bid-ask spread (excessive spread means high slippage costs), maximum competition, and a price range—only markets with implied probabilities between 5% and 50% are considered. Anything too high is already fully priced in with limited upside; anything too low is questionable in credibility, closer to lottery tickets than arbitrage opportunities.&lt;/p&gt;

&lt;p&gt;After filtering, the same event typically has both Yes and No directions listed simultaneously. The strategy keeps only the lower-priced side, because the low-price end offers greater odds elasticity and higher potential returns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let truePrice = parseFloat(outcomePrices[outcomeIndex] || 0)
if (truePrice &amp;lt; CONFIG.MIN_TRUE_PRICE || truePrice &amp;gt; CONFIG.MAX_TRUE_PRICE) continue

let isActive = (
    info.active === true &amp;amp;&amp;amp;
    info.approved === true &amp;amp;&amp;amp;
    info.closed === false &amp;amp;&amp;amp;
    info.archived === false &amp;amp;&amp;amp;
    info.acceptingOrders === true &amp;amp;&amp;amp;
    info.pendingDeployment === false
)
if (!isActive) continue

let spreadPct = (ask - bid) / ask
if (spreadPct &amp;gt; CONFIG.MAX_SPREAD_PCT) continue

// For the same event, keep only the lower-priced direction
if (!marketMap[baseSymbol]) {
    marketMap[baseSymbol] = entry
} else if (truePrice &amp;lt; marketMap[baseSymbol].price) {
    marketMap[baseSymbol] = entry
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All screening thresholds are exposed as external parameters via $vars, allowing free adjustment based on your own risk appetite and capital size without modifying code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydsrsyp7cn7q9e0y56f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydsrsyp7cn7q9e0y56f4.png" alt=" " width="800" height="318"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;K-Line Anomaly Detection: Finding Traces Left by Capital&lt;/strong&gt;&lt;br&gt;
After rough filtering, the system pulls 240 recent candlesticks at the 60-minute level for each passing market and runs five categories of anomaly detection. This is the strategy's core signal layer, and the fundamental difference between this system and "just throwing an event to AI for analysis"—we don't guess event outcomes; we look for capital behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slow Climb:&lt;/strong&gt; Total gain exceeds 5% over the past 120 bars, but the maximum single-bar gain stays below 1.5%. This pattern describes capital deliberately suppressing pace to avoid drawing attention—a stealth accumulation posture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear Volume Growth:&lt;/strong&gt; Linear regression on the most recent 60 bars' volume series yields a positive slope with R² above 0.5, indicating volume is expanding steadily and consistently rather than in random bursts. R² is the key filter—it screens out those "one massive spike then silence" noise patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pullback Narrowing:&lt;/strong&gt; Tracks the maximum depth of each pullback from historical highs. If recent average pullback depth falls below 60% of early-period pullbacks, it indicates selling pressure is declining and chips are stabilizing—someone is absorbing supply. Each pullback's lowest price is tracked throughout, using the complete pullback depth for calculation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consolidation Breakout:&lt;/strong&gt; MA60 and MA120 divergence is less than 2% (consolidation state) while the current price exceeds MA60 by more than 3%, indicating a breakout above prior resistance.&lt;/p&gt;

&lt;p&gt;**Volume Surge: **Recent 5-bar average volume exceeds the 60-bar baseline volume by more than 2.5x, indicating accelerating capital inflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function linearRegression(values) {
    let n = values.length
    let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0
    for (let i = 0; i &amp;lt; n; i++) {
        sumX += i; sumY += values[i]
        sumXY += i * values[i]; sumX2 += i * i
    }
    let slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
    let intercept = (sumY - slope * sumX) / n
    let meanY = sumY / n
    let ssTot = 0, ssRes = 0
    for (let i = 0; i &amp;lt; n; i++) {
        ssTot += Math.pow(values[i] - meanY, 2)
        ssRes += Math.pow(values[i] - (slope * i + intercept), 2)
    }
    let r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot
    return { slope, intercept, r2 }
}

function detectAnomaly(closes, volumes) {
    if (closes.length &amp;lt; 60) return null
    let anomalies = [], score = 0
    let slice120 = closes.slice(-Math.min(120, closes.length))

    // ── Anomaly 1: Slow Climb ──────────────────────────────────
    let totalChange = (slice120[slice120.length - 1] - slice120[0]) / slice120[0]
    let maxSingle = 0
    for (let i = 1; i &amp;lt; slice120.length; i++) {
        let change = Math.abs(slice120[i] - slice120[i - 1]) / slice120[i - 1]
        if (change &amp;gt; maxSingle) maxSingle = change
    }
    if (totalChange &amp;gt; 0.05 &amp;amp;&amp;amp; maxSingle &amp;lt; 0.015) {
        score++
        anomalies.push(`Slow climb: total +${(totalChange * 100).toFixed(1)}%, max single bar ${(maxSingle * 100).toFixed(2)}%`)
    }

    // ── Anomaly 2: Linear Volume Growth ────────────────────────
    let volSlice = volumes.slice(-60)
    let volReg = linearRegression(volSlice)
    if (volReg.slope &amp;gt; 0 &amp;amp;&amp;amp; volReg.r2 &amp;gt; 0.5) {
        score++
        anomalies.push(`Linear volume growth: slope ${volReg.slope.toFixed(4)}, R² ${volReg.r2.toFixed(2)}`)
    }

    // ── Anomaly 3: Pullback Narrowing (tracks lowest price per pullback) ──
    let pullbacks = [], localHigh = closes[0]
    let inPullback = false, pullbackLow = closes[0]
    for (let i = 1; i &amp;lt; slice120.length; i++) {
        if (closes[i] &amp;gt; localHigh) {
            if (inPullback) {
                pullbacks.push((localHigh - pullbackLow) / localHigh)
                inPullback = false
            }
            localHigh = closes[i]
        } else if (closes[i] &amp;lt; localHigh * 0.99) {
            if (!inPullback) {
                inPullback = true; pullbackLow = closes[i]
            } else if (closes[i] &amp;lt; pullbackLow) {
                pullbackLow = closes[i]
            }
        }
    }
    if (pullbacks.length &amp;gt;= 3) {
        let first = pullbacks.slice(0, Math.floor(pullbacks.length / 2))
        let last = pullbacks.slice(Math.floor(pullbacks.length / 2))
        let firstAvg = first.reduce((a, b) =&amp;gt; a + b, 0) / first.length
        let lastAvg = last.reduce((a, b) =&amp;gt; a + b, 0) / last.length
        if (lastAvg &amp;lt; firstAvg * 0.6) {
            score++
            anomalies.push(`Pullback narrowing: early ${(firstAvg * 100).toFixed(2)}%, recent ${(lastAvg * 100).toFixed(2)}%`)
        }
    }

    // ── Anomaly 4: Consolidation Breakout ──────────────────────
    let ma60 = closes.slice(-60).reduce((a, b) =&amp;gt; a + b, 0) / 60
    let ma120 = closes.length &amp;gt;= 120
        ? closes.slice(-120).reduce((a, b) =&amp;gt; a + b, 0) / 120
        : ma60
    let currentPrice = closes[closes.length - 1]
    if (Math.abs(ma60 - ma120) / ma120 &amp;lt; 0.02 &amp;amp;&amp;amp; currentPrice &amp;gt; ma60 * 1.03) {
        score++
        anomalies.push(`Consolidation breakout: MA60=${ma60.toFixed(3)}, MA120=${ma120.toFixed(3)}, current=${currentPrice.toFixed(3)}`)
    }

    // ── Anomaly 5: Volume Surge ────────────────────────────────
    let recentVol = volumes.slice(-5).reduce((a, b) =&amp;gt; a + b, 0) / 5
    let baseVol = volumes.slice(-65, -5).reduce((a, b) =&amp;gt; a + b, 0) / 60
    if (baseVol &amp;gt; 0 &amp;amp;&amp;amp; recentVol &amp;gt; baseVol * 2.5) {
        score++
        anomalies.push(`Volume surge: recent 5-bar avg is ${(recentVol / baseVol).toFixed(1)}x baseline`)
    }

    return { score, anomalies }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each triggered anomaly adds one point, for a maximum of 5. Only candidates scoring 2 or above advance to the next stage. A single signal may be random noise; stacking signals lends credibility to the capital behavior hypothesis—this is the core assumption behind the entire K-line detection logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;News Search: Finding a Fundamental Explanation for the Anomaly&lt;/strong&gt;&lt;br&gt;
K-line signals are evidence that "money is moving," but why it's moving and whether there's a basis for it requires the information side for verification. This step searches news for each K-line anomaly candidate to check for corresponding event support.&lt;/p&gt;

&lt;p&gt;This uses the Brave Search API, which offers 2,000 free requests per month—sufficient for the main line's pace of one cycle every 10 minutes. Search keywords are extracted from the symbol, stripping direction suffixes and delimiters to reconstruct readable event description terms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const q = item.symbol
    .replace("_USDC.No", "")
    .replace("_USDC.Yes", "")
    .replace(/-/g, "+")

const rawResponse = HttpQuery(
    "https://api.search.brave.com/res/v1/web/search?q=" + q,
    { method: "GET", headers: { "X-Subscription-Token": CONFIG.BRAVE_API_KEY } }
)

// Iterate all returned sections (web, news, etc.), extract structured fields uniformly
Object.keys(data).forEach(key =&amp;gt; {
    const section = data[key]
    if (section &amp;amp;&amp;amp; section.results &amp;amp;&amp;amp; Array.isArray(section.results)) {
        section.results.forEach(r =&amp;gt; {
            news.push({ type: key, title: r.title, description: r.description, age: r.age })
        })
    }
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retrieved news is packaged together with K-line data and fed into the next step's AI analysis—validating along two tracks simultaneously is far more reliable than looking at either one alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Role AI Analysis: Four Perspectives to Prevent Self-Persuasion&lt;/strong&gt;&lt;br&gt;
With data prepared, the pipeline enters the AI analysis node, where the model can be selected according to your needs. The prompt defines four independent roles—this is the system's single most critical design decision and deserves its own explanation.&lt;/p&gt;

&lt;p&gt;If you let a single AI simultaneously view K-line data and news, it easily cross-validates between the two, arriving at a superficially reasonable but actually independently unverified conclusion—using capital signals to endorse the news, then using the news to endorse the capital signals, ultimately producing a self-reinforcing "I think we should buy." Role isolation forces information source independence: each role sees only its own data, outputs only its own dimensional conclusion, and the final decision role sees only conclusions—never the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role 1: K-Line Capital Analyst&lt;/strong&gt; — Sees no news; only examines price and volume anomalies to determine whether real capital is consistently buying. Outputs capital signal strength (Strong / Moderate / Baseline).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role 2: News &amp;amp; Sentiment Analyst&lt;/strong&gt; — Sees no K-line data; only evaluates the information landscape to determine whether a positive catalyst exists that hasn't been priced in. Outputs news support level (Strong / Weak / None).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role 3: Price Assessment Analyst&lt;/strong&gt; — Synthesizes conclusions from the first two roles, estimates the event's true probability range, compares it to the current market price, and outputs an Undervalued / Fair / Overvalued assessment. This role has a built-in expiry date weighting rule: for events more than 7 days out, normal estimation applies; for events within 3 days where market price exceeds 0.15, the probability range is forcibly narrowed and "Significantly Undervalued" output is prohibited. The reasoning is straightforward—close to expiry, market participants possess more complete real-time information than the AI's training data, so the AI's judgment should defer to market pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role 4: Final Decision Analyst&lt;/strong&gt; — Aggregates the three preceding roles' conclusions according to a decision matrix and outputs the final action. The matrix logic: capital signal is the prerequisite, news provides verification, and together they determine confidence level; but regardless of confidence, if the price assessment doesn't reach "Undervalued," no action is taken.&lt;/p&gt;

&lt;p&gt;The news verification here is not about chasing information—it's about determining whether capital behavior already has fundamental support. When insider capital accumulates, the information won't appear in public news. But when capital signals coincide with news support, it indicates information is in the mid-diffusion stage and the market still has a pricing lag—this is the window the strategy is truly trying to capture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa56fi7pcimrll0bywp9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa56fi7pcimrll0bywp9j.png" alt=" " width="749" height="148"&gt;&lt;/a&gt;&lt;br&gt;
The AI's output format is strictly JSON, containing each role's conclusion, true probability range, price assessment, final decision, confidence level, and risk list—consumed directly by the downstream execution node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade Execution: Only Buy the Single Best Candidate&lt;/strong&gt;&lt;br&gt;
After the AI scores each batch of candidates individually, the execution layer doesn't place orders for every buy signal—the system picks only the highest-scoring one. Ranking criteria in order: confidence (High &amp;gt; Medium &amp;gt; Low) → price assessment (Significantly Undervalued &amp;gt; Undervalued) → true probability range width (narrower means more certain judgment).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const buySignals = signals.filter(s =&amp;gt; s.signal === 'buy')
bestSignal = buySignals.sort((a, b) =&amp;gt; {
    const confDiff = (confidenceRank[b.confidence] || 0) - (confidenceRank[a.confidence] || 0)
    if (confDiff !== 0) return confDiff
    const assessDiff = (assessmentRank[b.priceAssessment] || 0) - (assessmentRank[a.priceAssessment] || 0)
    if (assessDiff !== 0) return assessDiff
    // Narrower range = more certain, prioritize narrow
    const aWidth = (a.trueProb.max || 0) - (a.trueProb.min || 0)
    const bWidth = (b.trueProb.max || 0) - (b.trueProb.min || 0)
    return aWidth - bWidth
})[0]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After selecting a candidate, the system fetches the real-time ask price from the order book, confirms sufficient balance, and submits a limit order. After submission, order status is polled with a maximum wait of 30 seconds; if it times out, the order is canceled to prevent hanging order buildup. If the current cycle's ask price multiplied by the configured share amount falls below the 1 USDC minimum order threshold, the system automatically rounds up to ceil(1/askPrice) for the minimum executable shares.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop-Loss Module: Trailing Stop + Absolute Floor&lt;/strong&gt;&lt;br&gt;
The secondary line executes every 30 seconds, with logic in two steps: first handle expiry redemptions, then check active positions for trailing stop-loss.&lt;/p&gt;

&lt;p&gt;Polymarket has a special mechanism—after contract expiry, funds don't automatically return to your account; you must actively call redeem. The strategy scans for redeemable positions at the beginning of each secondary line cycle, batch-releasing funds to prevent capital from being locked up long-term.&lt;/p&gt;

&lt;p&gt;The trailing stop-loss core is a dual-track stop price design: the trailing stop line (historical highest bid price − fixed drop amount LOSS_AMOUNT) and the absolute floor line (entry price × 50%). The effective stop price takes whichever is higher.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function calcStopPrice(entryPrice, maxPrice) {
    const trailingStopPrice = maxPrice - CONFIG.LOSS_AMOUNT
    const absoluteStopPrice = entryPrice * CONFIG.FALLBACK_STOP_RATIO  // fixed 0.50
    const usingFallback = trailingStopPrice &amp;lt; absoluteStopPrice
    const effectiveStopPrice = usingFallback ? absoluteStopPrice : trailingStopPrice
    return { effectiveStopPrice, trailingStopPrice, absoluteStopPrice, usingFallback }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dual-track design addresses two typical position scenarios: during the profit phase, the trailing stop line rises with price, locking in floating gains; when the position moves against you immediately after entry, the trailing stop line may drop to near-zero or negative territory, at which point the absolute floor (entry price × 50%) takes over, preventing a position from riding all the way to zero before exiting. The two lines each serve their purpose—neither is dispensable.&lt;/p&gt;

&lt;p&gt;After a stop-loss triggers, a market sell order is placed, the historical high price cache for that market is cleared, and the system waits for the next entry signal. The dashboard displays each position's remaining distance to stop-loss in real time, color-coded by alert severity, enabling manual review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Design Decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why Multi-Role Instead of a Single Comprehensive Prompt?&lt;/strong&gt;&lt;br&gt;
This was partially addressed in the analysis module section, but it's worth elaborating. The problem with a single prompt isn't just "self-persuasion"—there's a more subtle risk: when AI simultaneously processes K-line data and news text, it tends to use the latter to explain the former, "rationalizing" K-line anomalies as inevitable reflections of news rather than evaluating both as independent signals. Role isolation fundamentally introduces two independent information chains. Only when both chains converge on the same direction during final aggregation is the credibility genuine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Only Buy the Single Best Per Cycle Instead of Diversifying?&lt;/strong&gt;&lt;br&gt;
Diversified holding has mature theoretical support in traditional finance, but the logic doesn't fully translate to prediction markets. Prediction market outcomes are binary—zero-out risk is real. If you simultaneously hold three moderate-confidence positions and two go to zero while one appreciates, you may not break even overall. The current strategy's choice: concentrate bets on accuracy rather than dilute quality-inconsistent signals with quantity. This trade-off isn't necessarily optimal, but it's internally consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Make the Expiry Date Weighting Rule a Hard Constraint?&lt;/strong&gt;&lt;br&gt;
Close to expiry, market prices reflect participants' real-time expectations, and a significant proportion of these participants possess fresher information than the AI's training data. If the AI still outputs "price is significantly undervalued, should buy" at this point, it's very likely that its information is already stale. Making this a constraint rather than a suggestion is necessary because relying on an AI to recognize "my information isn't fresh enough" is unrealistic—external enforcement is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Trading Observations
&lt;/h2&gt;

&lt;p&gt;Based on actual running logs, the strategy's funnel ratio is quite steep. In one particular run, 141 markets passed initial screening, K-line anomaly detection reduced them to 3, and ultimately only 1 met all three criteria—capital signal, news support, and price assessment—triggering an order. The other two were skipped due to "Fair" price assessment and insufficient composite scoring respectively. This demonstrates the strategy's overall conservatism: it would rather stay flat than lower standards. During calm market periods with no notable news events, the system may go an entire day without producing a single buy signal. This is a design choice, not a bug.&lt;/p&gt;

&lt;p&gt;There have also been cases where K-line anomalies were later proven to be false breakouts—capital briefly entered then withdrew, with the trailing stop intervening to exit. News analysis likewise contains noise; coverage of relevant events may itself be lagging or biased, and AI alone cannot fully identify these issues.&lt;/p&gt;

&lt;p&gt;The dashboard displays account equity, position status (current Bid/Ask, historical high, drawdown distance to stop-loss, stop-loss mode), and AI decision details in real time for manual review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogpjfm72ymoq9bm6guta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogpjfm72ymoq9bm6guta.png" alt=" " width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Honestly, this system is more of a functional framework of ideas than a plug-and-play money printer. The core logic—capital behavior tracing, dual-track news verification, multi-role AI judgment—has reasonable foundations conceptually, but in real markets, signal quality is inconsistent, AI judgment makes mistakes, and price convergence risk near expiry is ever-present.&lt;/p&gt;

&lt;p&gt;There's plenty of room for improvement: trading targets could be further focused on domains you're familiar with, AI prompts could be redesigned to match your trading style, the weights and thresholds for five K-line anomaly types all have tuning potential, and stop-loss coefficients deserve iteration based on actual position performance. The code is fully open-source—feel free to fork and modify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk Disclaimer
&lt;/h2&gt;

&lt;p&gt;Prediction market positions carry binary outcome characteristics with real zero-out risk. K-line anomaly signals and AI analysis conclusions do not constitute investment advice, and historical signal performance does not represent future returns. This strategy has not been validated over long time periods in live trading, and parameter settings significantly impact results. Before using this strategy for live trading, please fully understand Polymarket's platform rules, contract expiry mechanisms, and liquidity risks, and independently assess your tolerance for principal loss.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>tradingsystem</category>
      <category>ai</category>
      <category>strategy</category>
    </item>
    <item>
      <title>The Layering Rule of Vibe Trading: What to Leave to Moving Averages, What to Leave to AI</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Thu, 02 Apr 2026 07:26:10 +0000</pubDate>
      <link>https://forem.com/quant001/the-layering-rule-of-vibe-trading-what-to-leave-to-moving-averages-what-to-leave-to-ai-2k0o</link>
      <guid>https://forem.com/quant001/the-layering-rule-of-vibe-trading-what-to-leave-to-moving-averages-what-to-leave-to-ai-2k0o</guid>
      <description>&lt;p&gt;A reflection on Vibe Trading: when to use AI, and when not to.&lt;/p&gt;

&lt;p&gt;There's a concept gaining traction lately called Vibe Trading — describe your trading intent in natural language, and let AI execute it for you. Say "conservative strategy, prioritize low-volatility assets," and AI handles the allocation automatically. Sounds great.&lt;/p&gt;

&lt;p&gt;But before we talk about Vibe Trading, let me share something that happened recently — it illustrates "where AI belongs" better than any theory.&lt;/p&gt;

&lt;p&gt;On March 31st, Anthropic's Claude Code accidentally exposed its source code during an npm update — roughly 512,000 lines of TypeScript (note: this is total bundled code including dependencies and generated files). The community's reverse engineering kicked off immediately, with developers worldwide digging through the code for new features.&lt;/p&gt;

&lt;p&gt;But the most surprising finding had nothing to do with AI. And it has nothing to do with trading either — but the engineering philosophy behind it should resonate with anyone in quant trading.&lt;/p&gt;

&lt;p&gt;In a module labeled userPromptKeywords by analysts, there was a regex pattern matching profanity like shit, wtf, and fucking broken — used to quickly determine if a user was swearing.&lt;/p&gt;

&lt;p&gt;The world's most advanced large language model company uses regex to detect sentiment.&lt;/p&gt;

&lt;p&gt;Not calling Claude for sentiment analysis. Not training a classifier. Just a microsecond-level string match.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why Anthropic Doesn't Use Its Own AI
&lt;/h2&gt;

&lt;p&gt;This isn't laziness. It's a deliberate engineering decision.&lt;/p&gt;

&lt;p&gt;Claude Code handles hundreds of thousands of user interactions daily. Each one requires a judgment: "Is the user expressing frustration?" to adjust response strategy. If every check called an LLM:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency:&lt;/strong&gt; One LLM inference takes at least hundreds of milliseconds; regex matching takes microseconds&lt;br&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; LLM calls are billed per token; regex matching is virtually free&lt;br&gt;
&lt;strong&gt;Determinism:&lt;/strong&gt; Regex either matches or doesn't — 100% deterministic; LLM outputs are non-deterministic, the same input may yield different results&lt;br&gt;
So Anthropic's choice: &lt;strong&gt;use regex for fast filtering (low overhead, high speed, fully deterministic), and save LLM compute for decisions that truly need semantic understanding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This isn't a technical detail. It's an architectural philosophy: &lt;strong&gt;not every problem deserves an AI solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Quant traders should feel this one deeply.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The Same Choice in Quantitative Trading
&lt;/h2&gt;

&lt;p&gt;Your strategy has two types of decisions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deterministic Decisions — Use "Rules"&lt;/strong&gt;&lt;br&gt;
Anyone who's written strategies on FMZ knows the core trading logic is often just a few lines of deterministic code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Moving Average Crossover Signal — FMZ JavaScript Example&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PERIOD_D1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;// Golden cross — go long&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Golden cross, opening long&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clear conditions, deterministic results, no need to "understand" semantics. Written with if-else, 100% reliable, millisecond execution.&lt;/p&gt;

&lt;p&gt;This follows a similar logic to Anthropic using regex for sentiment detection — &lt;strong&gt;deterministic problems call for deterministic tools.&lt;/strong&gt; Of course, regex is string matching and moving averages are mathematical calculations — different tools, but they play similar roles in their respective systems: deterministic judgments that don't need AI involvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop-loss with if-else is 100% reliable. Stop-loss with AI is "probably" reliable. Your account can't afford that "probably."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fuzzy Judgment Decisions — Use AI&lt;/strong&gt;&lt;br&gt;
But some decisions can't be written as if-else:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;News sentiment analysis:&lt;/strong&gt; The Fed statement just dropped — hawkish or dovish? "The committee decided to maintain the current rate level but will closely monitor incoming data" — is this bullish or bearish? Moving averages can't tell you, and neither can regex.&lt;br&gt;
&lt;strong&gt;Anomaly detection:&lt;/strong&gt; An altcoin's social media mentions surged 800% in 3 hours, but the price hasn't moved. Is this "smart money accumulating" or "someone manufacturing buzz before a pump"? This requires multi-dimensional fuzzy judgment.&lt;br&gt;
&lt;strong&gt;Strategy generation:&lt;/strong&gt; You have an intuition — "this pattern has been followed by a rally lately." But you can't articulate the exact entry/exit conditions. You can describe this intuition to an LLM and let it translate it into backtestable quantitative factors.&lt;br&gt;
&lt;strong&gt;These scenarios share a common trait:&lt;/strong&gt; the input is unstructured, the criteria are fuzzy, and it requires "understanding" rather than just "matching."&lt;/p&gt;
&lt;h2&gt;
  
  
  3. A Layering Approach Worth Borrowing
&lt;/h2&gt;

&lt;p&gt;Back to Claude Code's architecture. Community analysis revealed a clear layering:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk4u54lhnhlnypp180n0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk4u54lhnhlnypp180n0.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
Of course, Claude Code and quantitative trading are entirely different domains — this mapping isn't exact. But the resonance in design philosophy is real: &lt;strong&gt;choose the right tool at each layer, rather than hammering every nail with the same hammer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The open-source community is practicing similar ideas. TradingAgents is a notable multi-agent quant trading framework (built on LangGraph, with an accompanying academic paper). It simulates a real trading firm's team structure: technical analysts handle candlestick and indicator calculations, sentiment analysts interpret news, and traders with different risk profiles synthesize all inputs for final decisions. &lt;strong&gt;Not one omniscient AI doing everything, but different roles each handling their specialty.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's worth noting that TradingAgents is a research framework — it addresses "how AI makes trading decisions." But in live trading, you still need the other half: exchange connectivity, order management, risk execution, audit logs — engineering infrastructure work that platforms like FMZ have already built for you.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. How to Implement Vibe Trading: A Layered Architecture Example
&lt;/h2&gt;

&lt;p&gt;Back to Vibe Trading. The direction is right, but the layering must be clear.&lt;/p&gt;

&lt;p&gt;Suppose BTC's moving averages just formed a golden cross today, but the news is all about regulatory crackdowns. What do you do? Pure MA says go long; pure news says stay out. This is exactly the scenario that calls for layering.&lt;/p&gt;

&lt;p&gt;On FMZ, a simplified layered architecture can be implemented like this (note: simplified example — please add proper contract setup and risk controls for live trading):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
Strategy Parameters (add in the "Parameters" section of FMZ strategy editor):
  OPENROUTER_API_KEY : string, your OpenRouter API Key
  AI_MODEL           : string, default "google/gemini-2.5-flash", can be changed to other models
*/&lt;/span&gt;

&lt;span class="c1"&gt;// Semantic Decision Layer: call AI via OpenRouter for market sentiment&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAISentiment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Analyze current crypto market news, give a sentiment score (-1 to 1, -1 extreme fear, 1 extreme greed), return only a number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openrouter.ai/api/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AI_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;OPENROUTER_API_KEY&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15000&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Fall back to neutral when AI returns unexpected output — system reliability shouldn't depend on every AI call being correct&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI returned unexpected format, using default 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI sentiment score:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;lastSignalTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;// Track last signal bar time to prevent duplicate triggers on the same candle&lt;/span&gt;

    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PERIOD_D1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;curTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Time&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isBullCross&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isBearCross&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ma5&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;ma20&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;// Check position status&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetPosition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hasPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="c1"&gt;// Layer 1: Deterministic signal as "gate" — golden cross + no position + not yet processed on this bar, then ask AI&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isBullCross&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasPosition&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;curTime&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;lastSignalTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;lastSignalTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curTime&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sentiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAISentiment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;// Layer 2: AI sentiment as "reference" — affects sizing but doesn't independently trigger trades&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentiment&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Golden cross + AI bullish, full position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentiment&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Golden cross + AI neutral, half position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Golden cross but AI bearish, skipping signal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Death cross — close position: deterministic rule, no AI involved&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isBearCross&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;hasPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closebuy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Death cross, closing position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Layer 3: Stop-loss is "iron law" — no AI involvement&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;curPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;entryPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Price&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curPrice&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;entryPrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.97&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// 3% below entry price&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closebuy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stop-loss triggered, unconditional close, loss:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;curPrice&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;entryPrice&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw7jyws4hn2pwkl1x31r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw7jyws4hn2pwkl1x31r.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
The core logic of this code is worth unpacking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The golden cross is the "gate."&lt;/strong&gt; Only when the deterministic signal fires does the strategy call AI. It won't query the LLM on every candle — saving money (LLM APIs charge per token) and filtering out noise. This mirrors Anthropic's approach: regex filters first, heavier processing only triggers on a match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI sentiment is a "reference." **It affects position sizing and whether to skip a signal, but never independently triggers a trade. Notice the error handling on AI return values — if the LLM returns unparseable content, it falls back to a neutral value of 0. **System reliability should never depend on every AI call being correct.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stop-loss is "iron law."&lt;/strong&gt; Drop 3% below entry price and the position closes unconditionally — no asking AI for its opinion. AI might say "long-term bullish," but your account can't wait for the long term. This uses a hard price-percentage stop — no fuzzy judgment involved.&lt;/p&gt;

&lt;p&gt;This is the right way to do Vibe Trading: **use natural language to let AI help you "sense" market atmosphere, and use deterministic code to "execute" trading actions. **The boundary between the two must not be blurred.&lt;/p&gt;

&lt;p&gt;Practical tip: In FMZ's backtesting system, first run a pure MA strategy as a baseline, then add the AI sentiment layer and compare returns and drawdowns. If AI makes things worse — your layering is wrong, and AI may be interfering where it shouldn't. Log every AI response with Log() so you can review each decision after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. One Takeaway
&lt;/h2&gt;

&lt;p&gt;The most advanced AI company uses regex to detect sentiment — not because they can't build something better.&lt;/p&gt;

&lt;p&gt;It's because they know: choosing the right tool matters more than &lt;strong&gt;choosing the strongest tool.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moving average strategies aren't sexy. Regex isn't sophisticated. But in their respective domains, they're more reliable than any AI.&lt;/p&gt;

&lt;p&gt;Conversely, when you need to extract "is this report bullish or bearish on BTC" from a 5,000-word macro research note — moving averages can't help you, and neither can regex. That's when AI should step in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's not about "whether to use AI" — it's about "at which layer to use it."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That unassuming regex file in Claude Code's source answered a question we often overlook. And what FMZ gives you is a ready-made layered infrastructure — exchange connectivity, indicator calculations, live trading management, audit logs — all built for you. All you need to figure out is: which decisions go to TA.MA(), and which go to AI.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibetrading</category>
      <category>strategy</category>
      <category>claude</category>
    </item>
    <item>
      <title>Multi-Factor Strategies Aren't Exclusive to Big Firms: A Research Framework for Independent Quants</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Wed, 01 Apr 2026 02:13:55 +0000</pubDate>
      <link>https://forem.com/quant001/multi-factor-strategies-arent-exclusive-to-big-firms-a-research-framework-for-independent-quants-38ka</link>
      <guid>https://forem.com/quant001/multi-factor-strategies-arent-exclusive-to-big-firms-a-research-framework-for-independent-quants-38ka</guid>
      <description>&lt;h2&gt;
  
  
  I. Why Automated Factor Mining?
&lt;/h2&gt;

&lt;p&gt;If you've been around quantitative trading, you've surely heard the word "factor." What is a factor? Simply put, it's a market signal expressed through data — price momentum, volume anomalies, Bollinger Band positioning — used to predict whether a given coin will go up or down over a certain period.&lt;/p&gt;

&lt;p&gt;Sounds simple, but anyone who's actually done factor research knows how hard it is:&lt;/p&gt;

&lt;p&gt;Solid financial knowledge and a deep background in mathematical statistics&lt;br&gt;
Large volumes of clean historical data&lt;br&gt;
A rigorous backtesting framework&lt;br&gt;
And an unavoidable problem: factor decay&lt;br&gt;
A signal that works today might completely fail in a few days — because market participants learn, adapt, and arbitrage the pattern away. That's why factor mining is never a one-time job; it requires continuous iteration.&lt;/p&gt;

&lt;p&gt;What this article introduces is a system that automates this entire process: running a complete loop of factor mining → validation → elimination → signal synthesis → order execution at fixed intervals. Machine iteration replaces manual repetition, keeping the strategy in sync with the market's evolving rhythm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7k6c7a4j5xcj2ahl7hy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7k6c7a4j5xcj2ahl7hy.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  II. System Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The traditional factor mining workflow goes: researcher proposes hypothesis → writes code → runs backtest → filters → goes live → discovers it's broken a few months later → starts over. The entire cycle can take weeks or even months.&lt;/p&gt;

&lt;p&gt;This system compresses the entire loop into a single automated cycle executed at fixed intervals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivry0qnqke02tk9eqd11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivry0qnqke02tk9eqd11.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
The system is driven by two schedulers: a slow trigger that executes the full factor iteration pipeline at hourly intervals, and a fast trigger that polls position status at second-level intervals, handling take-profit/stop-loss and dashboard refresh.&lt;/p&gt;
&lt;h2&gt;
  
  
  III. Module Details &amp;amp; Core Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1 Get Symbol Pool&lt;/strong&gt;&lt;br&gt;
At the start of each round, the system pulls real-time quotes for all perpetual contracts from the exchange, sorted by trading volume to select the top N. Liquidity is a prerequisite for factor effectiveness — low-cap coins with sparse volume make any signal unreliable.&lt;/p&gt;

&lt;p&gt;Simultaneously, it detects the BTC 4-hour candlestick volatility historical percentile to determine the overall market state (normal / high_vol / low_vol / volatile). This assessment directly influences the directional bias of AI-generated factors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Filter high-liquidity symbols by trading volume&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetTickers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tickers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDT.swap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quoteVolume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Volume&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quoteVolume&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quoteVolume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_symbolPool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Detect BTC volatility percentile to determine market state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BTC_USDT.swap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PERIOD_H4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returns20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;returns20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avgVol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;returns20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;returns20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Compare against full-history volatility to determine percentile&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allVols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;allVols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;btcR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;allVols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;btcVolPercentile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allVols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;avgVol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;allVols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;marketState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btcVolPercentile&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;marketState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high_vol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btcVolPercentile&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;marketState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low_vol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_marketState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;marketState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_btcVolPct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;btcVolPercentile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2 Check Factor Pool Status&lt;/strong&gt;&lt;br&gt;
Before asking AI to generate new factors, the system first takes stock of the current factor pool's health: which factors have seen their recent IC consistently declining (decaying), and which dimensions haven't been covered yet. This information is passed directly to the AI as constraints, preventing redundant exploration of already-failed directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_factorPool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icHistory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_icHistory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icDecayWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icDecayWindow&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// Recent window length&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icDecayThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icDecayThreshold&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Decay threshold&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetFactorCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetFactorCount&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;degradedFactors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icArr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icHistory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;icArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;icArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;icDecayWindow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recentAvg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recentAvg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;icDecayThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;degradedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;recentIC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recentAvg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationale&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Dynamically determine how many new factors to explore this round&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;explorationBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;explorationBuffer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;explorationCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;explorationBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;targetFactorCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;validCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;explorationBuffer&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generate_initial&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iterate_factors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.3 Build the Prompt: Let AI Invent Factors&lt;/strong&gt;&lt;br&gt;
What the AI receives is not an open-ended task, but a constrained framework. The prompt includes: current market state, list of currently effective factors (no duplicates allowed), recently decayed factors (no fine-tuning allowed), already-covered dimensions, and dimensions not yet explored.&lt;/p&gt;

&lt;p&gt;This way, the candidate factors generated are genuine attempts in new directions, rather than re-running existing factors with tweaked parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Key snippet of the iteration-mode prompt&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usedDimensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationale&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;None&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icHistory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;: Historical IC=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; Recent IC=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; | Logic: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;None&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;degradedSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;degradedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;degradedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;: Recent IC=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentIC&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; | Original logic: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationale&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No decayed factors this round&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;【Currently Effective Factors (no variants needed)】&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;validSummary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;【Recently Decayed Factors (no fine-tuning on these dimensions)】&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;degradedSummary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;【Already Covered Dimensions (no duplicates)】&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;usedDimensions&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;【Unexplored Dimensions (prioritize from here)】&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;unusedSample&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generate &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;explorationCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; entirely new-direction factors:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1. Must be completely different from covered dimensions; no fine-tuning of failed factors&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2. Prioritize selection from unexplored dimensions&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3. Prioritize designing nonlinear combination factors&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4. Design for the current &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;marketState&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; market state&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI's System Prompt includes a complete specification of the FMZ platform's TA function library, code format constraints, crypto market prior knowledge, and a full list of explorable factor dimensions (see strategy source code for details). The output format is strictly pure JSON (no Markdown wrapping):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MomentumAcceleration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"rationale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Short-term momentum acceleration, capturing retail chase-rally inertia inflection points"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(records[n-1].Close - records[n-6].Close)/records[n-6].Close - (records[n-2].Close - records[n-7].Close)/(records[n-7].Close + 0.0001)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exploration"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.4 IC Validation: Let the Data Speak, Not Intuition&lt;/strong&gt;&lt;br&gt;
IC (Information Coefficient) measures how well the cross-sectional ranking derived from a factor correlates with the actual return ranking of the next candlestick. The higher the IC, the more accurate the factor's prediction.&lt;/p&gt;

&lt;p&gt;Validation uses historical walk-forward replay: taking several hundred past candlesticks, at each time point t, using the data from time t-1 to compute the factor value and predict the return of candlestick t. Time-series alignment is strict, eliminating any look-ahead bias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calcRankICFull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;symRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factorName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;syms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symRecords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allLengths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;symRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minSymLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;allLengths&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minSymLen&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;testLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fVals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;nRets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sym&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullRecords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;symRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="c1"&gt;// Use data up to t-1 to compute factor (slice(0, t) excludes candlestick t)&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fullRecords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;})();&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;fVals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="c1"&gt;// Predict the actual return of candlestick t&lt;/span&gt;
            &lt;span class="nx"&gt;nRets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;fullRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fullRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fVals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Compute Rank IC (Spearman correlation coefficient)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fRank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;rRank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;fVals&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fRank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;nRets&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rRank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fVals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fRank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rRank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;den&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
            &lt;span class="nx"&gt;rr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;den&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;den&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avgIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;avgIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;icList&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IC threshold is controlled by the variable $vars.icThreshold, defaulting to 0.02. This is a relatively lenient entry-level threshold, suitable for quickly filtering out obviously ineffective factors. For stricter statistical significance control, this value can be raised according to actual needs. Factors that fail to pass the threshold are eliminated regardless of how perfect their logic may be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.5 Correlation Filtering &amp;amp; Bottom Culling&lt;/strong&gt;&lt;br&gt;
Factors that pass IC validation still need to clear two more hurdles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First hurdle:&lt;/strong&gt; Correlation filtering. If two factors have highly similar cross-sectional scores (|corr| &amp;gt; threshold), keep the one with higher IC and discard the other. When two factors are highly correlated in their cross-sectional scores, they are essentially capturing nearly identical information — keeping one is sufficient; adding another doesn't add a new perspective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second hurdle:&lt;/strong&gt; Bottom culling. The factor pool has a capacity limit; excess factors are ranked by performance, and the worst ones are eliminated. Factors with recently declining IC are ranked using their recent IC rather than historical average IC, subjecting them to greater elimination pressure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Correlation filtering (keep highest IC, discard highly correlated redundant factors)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corrThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;corrThreshold&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;survivedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icAvg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icAvg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sort by IC descending first&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decorrelatedFactors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;survivedFactors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isRedundant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;decorrelatedFactors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calcCorrelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;factorScoresMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nx"&gt;factorScoresMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;corr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;corrThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Record absorbed correlated factor (for dashboard display)&lt;/span&gt;
            &lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;corrGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;corrGroup&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;corrGroup&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;isRedundant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isRedundant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;decorrelatedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;corrGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Bottom culling: decaying factors ranked by recent IC instead of historical average&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetFactorCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetFactorCount&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;decorrelatedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDecaying&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentIC&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icAvg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDecaying&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentIC&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icAvg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;scoreB&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;scoreA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decorrelatedFactors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetFactorCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afi_factorPool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalPool&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: The correlation calculation is based on factor scores from the current cross-section, which may produce occasional misjudgments at certain points in time. A more robust approach would be to average correlations across multiple historical cross-sections — this is a direction for future improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.6 Signal Synthesis &amp;amp; Rebalancing Execution&lt;/strong&gt;&lt;br&gt;
Once the factor pool is stable, the system computes a composite score for each symbol: Z-score standardize each factor's cross-sectional values, then combine them with weighted summation based on each factor's recent IC — better-performing factors get larger weight, and factors with negative recent IC have their weight set to zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Factor weights: recent IC weighted (negative IC factors get zero weight)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;totalW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icHistory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recentArr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recentIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;recentArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;recentArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;recentArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recentIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Negative IC → weight = 0&lt;/span&gt;
    &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;totalW&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalW&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="nx"&gt;totalW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Z-score standardization&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;zscore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validSyms&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawMatrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;std&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;std&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Composite scoring&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sym&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;validSyms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;factorPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;zscore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Threshold filtering: skip ambiguous signals, don't enter positions&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longShortN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longShortN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longThreshold&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shortThreshold&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;longThreshold&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longShortN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;shortThreshold&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longShortN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During rebalancing execution, old positions not in the current round's list are closed first, then new signals are entered proportionally based on account equity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;positionRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;positionRatio&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Total equity usage ratio&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxLeverage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxLeverage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;equity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Equity&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;perAmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;equity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;positionRatio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;shortList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Close old positions not in the target set&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;longList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;shortList&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sym&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentHoldings&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;targetSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentHoldings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;PD_LONG&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closebuy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closesell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="c1"&gt;// Clear take-profit tracking state&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;_USDT/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_maxpnl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_trail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Enter new signal positions (using market orders, -1 indicates market price)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openPos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLong&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetMarginLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLeverage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allMarkets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CtVal&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CtVal&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CtVal&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amtPrec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AmountPrecision&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AmountPrecision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minQty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MinQty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MinQty&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MinQty&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxQty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MaxQty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MaxQty&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MaxQty&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;_N&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;perAmt&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ctVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amtPrec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minQty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;maxQty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.7 Position Monitoring: Stop-Loss / Take-Profit / Dynamic Trailing Stop&lt;/strong&gt;&lt;br&gt;
The fast trigger runs at second-level intervals, monitoring the unrealized P&amp;amp;L of all positions in real time and executing three exit strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed stop-loss:&lt;/strong&gt; Auto-close when unrealized loss exceeds STOP_LOSS_PCT (default 5%)&lt;br&gt;
&lt;strong&gt;Fixed take-profit:&lt;/strong&gt; Auto-close when unrealized profit exceeds TAKE_PROFIT_PCT (default 10%)&lt;br&gt;
&lt;strong&gt;Dynamic trailing stop:&lt;/strong&gt; Activated once unrealized profit reaches TRAIL_TRIGGER (3%); the drawdown threshold adjusts dynamically with the peak unrealized profit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STOP_LOSS_PCT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stopLossPct&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TAKE_PROFIT_PCT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;takeProfitPct&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TRAIL_TRIGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Activate trailing stop after 3% unrealized profit&lt;/span&gt;

&lt;span class="c1"&gt;// Dynamic drawdown threshold: higher peak profit allows larger drawdown&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDynamicTrailDrawdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Peak profit ≥7%, allow 3% drawdown&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Peak profit ≥4%, allow 2% drawdown&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                   &lt;span class="c1"&gt;// Otherwise, 1.5% drawdown&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;monitorTPSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="k"&gt;of &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;positions&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;_USDT/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_USDT.swap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;PD_LONG&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Track peak unrealized profit&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_maxpnl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_maxpnl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxPnl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxPnl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_maxpnl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxPnl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Activate trailing stop&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_trail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRAIL_TRIGGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_trail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; trailing stop activated, unrealized profit: +&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trailDrawdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDynamicTrailDrawdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_trail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;trailDrawdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trailing stop (drawdown &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxPnl&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%, threshold &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;trailDrawdown&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;STOP_LOSS_PCT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stop-loss (&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;TAKE_PROFIT_PCT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Take-profit (&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;isLong&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closebuy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closesell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;triggered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_maxpnl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_trail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IV. Key Design Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;4.1 Why Rank IC (Instead of Pearson IC)&lt;/strong&gt;&lt;br&gt;
Rank IC computes correlation using ranks rather than raw values, making it naturally robust against extreme values (outliers). The crypto market has heavily fat-tailed price distributions. Pearson IC is easily distorted by a handful of extreme candlesticks, while Rank IC offers greater stability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 Strict Time-Series Alignment: Eliminating Look-Ahead Bias&lt;/strong&gt;&lt;br&gt;
Both IC validation and live signal computation uniformly use t-1 period factor values to predict t-period returns. During validation, records are passed as fullRecords.slice(0, t), physically truncating future data so that no matter how AI-generated factor code references records[n], it can only access history up to t-1. In live mode, the last candlestick is removed (slice(0, n-1)) before computing factor values to predict the next candlestick's movement. The logic is identical in both cases, preventing inflated IC from look-ahead data leakage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3 Recent IC Weighting: Weights Self-Adapt to the Market&lt;/strong&gt;&lt;br&gt;
Factor weights are not fixed but dynamically adjusted based on recent IC. When a factor starts failing (recent IC declining), its weight in signal synthesis automatically decreases toward zero. The system completes weight rebalancing without any manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.4 Dual-Trigger Architecture&lt;/strong&gt;&lt;br&gt;
Factor iteration is a computationally heavy task (candlestick fetching + IC backtesting + AI calls) — running it at hourly intervals is sufficient. Position protection is a time-sensitive task requiring second-level response. Splitting these into triggers with different frequencies prevents them from blocking each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  V. Live Observations: The Factor Iteration Process
&lt;/h2&gt;

&lt;p&gt;After two days of live operation, the following phenomena were observed:&lt;/p&gt;

&lt;p&gt;Factors that entered the pool early generally had historical ICs between 0.04 and 0.07, passing the basic threshold.&lt;br&gt;
As iterations progressed, nearly all factors saw their recent IC declining — some dropping from 0.06 to 0.008, others falling into negative territory. This indicates that the signals these factors captured are becoming ineffective in the current market environment.&lt;br&gt;
After the system detected decay, the next round prioritized exploring uncovered dimensions, searching for new candidate factors as replacements. The entire process required no manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nzxz5jntdidlm0z9lmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nzxz5jntdidlm0z9lmi.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
Two days is too short to sufficiently validate whether the system's self-adaptive capability is genuinely effective. This section merely documents that the system executed the iteration actions as designed. More meaningful conclusions require longer-term sustained observation. However, this process itself already demonstrates that the system's fundamental design logic is functioning: it doesn't stubbornly cling to failed signals, but continuously attempts new dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  VI. Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Building this system wasn't about proving that AI can beat the market. Rather, it's about showing that in the age of AI, many things that only top-tier institutions could previously accomplish are now within reach for ordinary individuals to attempt.&lt;/p&gt;

&lt;p&gt;Factor mining, strategy iteration, automated execution — these things that used to require a team, massive data infrastructure, and years of accumulation to build, can now be run as a single workflow.&lt;/p&gt;

&lt;p&gt;This doesn't mean it will produce consistent profits. The market is always more complex than any system. But it does mean that the barrier to entry is lowering, the tools are getting stronger, and the possibility for ordinary people to participate in this endeavor is growing.&lt;/p&gt;

&lt;p&gt;⚠️ Risk Disclaimer: All strategies carry the risk of losses. The content of this article is for technical learning purposes only and does not constitute investment advice. Please test thoroughly before going live.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>strategy</category>
      <category>quant</category>
      <category>workflow</category>
    </item>
    <item>
      <title>Binance Perpetual Futures New Listing Automated Workflow: Let AI Watch the Market for You</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Tue, 24 Mar 2026 03:07:58 +0000</pubDate>
      <link>https://forem.com/quant001/binance-perpetual-futures-new-listing-automated-workflow-let-ai-watch-the-market-for-you-20n5</link>
      <guid>https://forem.com/quant001/binance-perpetual-futures-new-listing-automated-workflow-let-ai-watch-the-market-for-you-20n5</guid>
      <description>&lt;p&gt;&lt;em&gt;The concept of "new listing trading" is familiar to anyone in the stock market — get in early, grab your allocation, and ride the price discovery when the ticker goes live. The core logic is the same in crypto, except the opportunities come far more frequently. Major centralized exchanges list new perpetual futures contracts every few days, and these opportunities are open to everyone equally.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the early hours after a contract goes live, the market hasn't fully priced in the asset. Price elasticity is extreme, and moves far larger than typical price action can unfold in a very short time. If you can call the direction in advance and enter at the moment of launch, that's exactly where the opportunity lies in perpetual futures new listings.&lt;/p&gt;

&lt;p&gt;But here's the problem — before a new contract goes live, we know almost nothing about the token. Team background, tokenomics, market sentiment, funding rates… all of this requires significant time to collect and analyze. Entering without sufficient understanding is no different from gambling. And trying to manually research every new listing? There's simply no way to keep up.&lt;/p&gt;

&lt;p&gt;That's why the workflow introduced today was built to solve exactly this problem — the system automatically begins collecting data and running continuous AI analysis the moment an announcement is published, building a solid knowledge base before the token goes live. The entire workflow runs 24/7 autonomously. No manual monitoring needed. Announcement detection, data collection, AI analysis, and market entry — all fully automated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvx01vfjo1qrewv0rbyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvx01vfjo1qrewv0rbyr.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall Architecture: Two Parallel Pipelines
&lt;/h2&gt;

&lt;p&gt;This strategy's architecture is split into two pipelines.&lt;/p&gt;

&lt;p&gt;The Analysis Pipeline runs in loops at longer intervals, continuously monitoring new listing announcements, collecting multi-dimensional data, calling AI for analysis, and accumulating each analysis conclusion into a historical record. The Execution Pipeline runs in rapid high-frequency loops, responsible for real-time detection of whether new tokens have gone live on the exchange. Once a listing is detected, it immediately reads the analysis results, decides whether to enter, and continuously monitors positions for take-profit and stop-loss.&lt;/p&gt;

&lt;p&gt;The two pipelines have clear separation of duties — the Analysis Pipeline provides direction, and the Execution Pipeline handles execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis Pipeline: Step 1 — Discover New Tokens, Build Tracking Queue
&lt;/h2&gt;

&lt;p&gt;When the Analysis Pipeline starts, it first initializes global state, recording initial capital, run count, and other baseline data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_initialized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_initialized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_trackingList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_STARTTIME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_initmoney&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initmoney&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;initAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Balance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It then fetches Binance's official announcements to identify upcoming perpetual futures listings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.binance.com/bapi/composite/v1/public/cms/article/list/query?type=1&amp;amp;pageNo=1&amp;amp;pageSize=10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clienttype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a new token is identified, the system pushes it into the tracking queue with a status tag: before launch it's PRE_LISTING (pre-tracking), on launch day it switches to LAUNCH_DAY, after entry it moves to TRADING, and upon completion it's marked DONE and automatically cleared from the queue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;trackingList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;launchDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isLaunchDay&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LAUNCH_DAY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PRE_LISTING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;discoveredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;hoursToLaunch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isLaunchDay&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Launching today&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hoursToLaunch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;analysisCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lastAnalyzedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way the system always focuses only on targets that are currently valuable, never wasting resources on expired opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis Pipeline: Step 2 — Multi-Dimensional Data Collection
&lt;/h2&gt;

&lt;p&gt;After discovering a new token, the system immediately begins collecting data from three dimensions.&lt;/p&gt;

&lt;p&gt;Fundamental data is fetched via the CoinMarketCap API to obtain the token's market cap, circulation ratio, CMC ranking, and more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchCMC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cmcApiKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;convert=USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-CMC_PRO_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max_supply&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_supply&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cmc_rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;circulatingSupply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;circulating_supply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;circulationRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;circulating_supply&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;spotPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;change1h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percent_change_1h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;change24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percent_change_24h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;change7d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percent_change_7d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;volume24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volume_24h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;marketCap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;market_cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fullyDilutedMarketCap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fully_diluted_market_cap&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sentiment data&lt;/strong&gt; is gathered via Brave Search, pulling recent news related to the token's funding background, team developments, and unlock schedules, using three different keyword combinations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchBraveNews&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;braveKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;" token funding team&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;" vesting unlock schedule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;" binance futures listing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="c1"&gt;// For each query, call the Brave Search API, filter results matching the coin name, deduplicate and return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Market data&lt;/strong&gt; is collected simultaneously from four exchanges — Bybit, OKX, Gate, and HTX — including funding rates, open interest, spreads, and other key indicators. Taking OKX as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchOKX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-USDT-SWAP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tkRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.okx.com/api/v5/market/ticker?instId=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;instId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tkRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tkRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fundingRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFundingRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fundingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFundingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.okx.com/api/v5/public/funding-rate?instId=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;instId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;fundingRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fundingRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;nextFundingRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextFundingRate&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextFundingRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;fundingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fundingTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;nextFundingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextFundingTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="nc"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;openInterest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oiRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.okx.com/api/v5/public/open-interest?instType=SWAP&amp;amp;instId=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;instId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oiData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oiRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oiRaw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oiData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;openInterest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oiData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oiUsd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sodUtc0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sodUtc0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;bid1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidPx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;bid1Size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidSz&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;ask1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;askPx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;ask1Size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;askSz&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;spread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;askPx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidPx&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="na"&gt;change24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sodUtc0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;sodUtc0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="na"&gt;high24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;high24h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;low24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;low24h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;open24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open24h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;volume24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vol24h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;volCcy24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volCcy24h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;openInterest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;fundingRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFundingRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fundingTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFundingTime&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OKX failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-exchange verification effectively avoids bias from any single data source, building a more complete picture of the new token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis Pipeline: Step 3 — AI Analysis with Historical Memory
&lt;/h2&gt;

&lt;p&gt;This is the most critical design element of the entire workflow.&lt;/p&gt;

&lt;p&gt;From announcement to actual launch, there are often several days. During this window, the system repeatedly collects data and calls AI for analysis. The key insight: &lt;strong&gt;every analysis passes all previous conclusions to the AI&lt;/strong&gt;, allowing it to make new assessments building on historical judgments.&lt;/p&gt;

&lt;p&gt;Reading history and injecting it into the prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;historyKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_history_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;historyKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildHistorySection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(This is the first analysis for this token, no history available)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aiConclusion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;### Analysis #&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Time: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; | Hours to launch: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hoursToLaunch&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; | Phase: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Conclusion: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, Confidence &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%, Risk &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;riskLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Trend: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trendConsistency&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Initial&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Entry timing: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entryTiming&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Summary: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;
        &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI analysis follows several core principles: when historical direction is consistent, confidence increases with each iteration; when a direction reversal appears, the AI must explicitly state whether it's a genuine signal or short-term noise; when historical judgments have been indecisive, confidence must remain conservative and not be inflated. The AI ultimately outputs a structured conclusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;AI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Long|Short|Hold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0-100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;integer&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trendConsistency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Initial|Strengthening|Maintaining|Weakening|Reversal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reversalType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Genuine signal|Short-term noise|null"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entryTiming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"immediate|drawdown_N"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;drawdown_&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;means&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pullback&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"priceRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"leverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stopLoss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stop-loss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;percentage&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"takeProfit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;take-profit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;percentage&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"riskLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"High|Medium|Low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"riskPoints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Risk point 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Risk point 2"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keyChanges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Most significant data changes compared to last analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Comprehensive judgment in 100 words or less"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After analysis is complete, the conclusion is stored in the history, and the token's final strategy is updated for the Execution Pipeline to read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hoursToLaunch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hoursToLaunch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currentData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;aiConclusion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aiResult&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_history_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nl_strategy_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;historyCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;aiResult&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When direction remains consistent, confidence increases with each analysis iteration; when direction reverses, the AI must provide clear reasoning. Judgments accumulated this way are far more reliable than any single independent analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution Pipeline: Step 4 — Listing Detection and Safe Entry
&lt;/h2&gt;

&lt;p&gt;The Execution Pipeline runs high-frequency loops, using exchange.GetMarkets() to detect whether a new token has appeared in the exchange's contract list. Once a listing is detected, it first runs through a safety check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;riskLevel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;High&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIN_CONFIDENCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hasPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;             &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TRADING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After passing all checks, it executes based on the AI's recommended entry timing. For immediate entry, it calculates contract quantity and places the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;leverage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leverage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_LEVERAGE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetMarginLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leverage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allocAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSITION_AMOUNT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Balance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calcContractAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allocAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the AI determines the opening premium is too high and recommends waiting for a pullback, the system records the target price and continuously polls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;openPrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;openPrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_nl_waitEntry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drawdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;waitStartTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;openPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;drawdownPct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TRADING_WAIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the target isn't triggered within 2 hours, the opportunity is considered expired and entry is automatically abandoned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution Pipeline: Step 5 — Position Monitoring and Take-Profit / Stop-Loss
&lt;/h2&gt;

&lt;p&gt;After entry, the Execution Pipeline continuously monitors position status. Take-profit and stop-loss use two mechanisms running in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TP_SL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;DEFAULT_SL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// Default stop-loss 10%&lt;/span&gt;
    &lt;span class="na"&gt;DEFAULT_TP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// Default fixed take-profit 25%&lt;/span&gt;
    &lt;span class="na"&gt;TRAILING_TRIGGER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Enable trailing TP when unrealized profit reaches 30%&lt;/span&gt;
    &lt;span class="na"&gt;TRAILING_DRAWDOWN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;       &lt;span class="c1"&gt;// Trailing TP triggers on 8% drawdown from peak&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Update peak unrealized profit&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxProfit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;maxProfit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxProfitKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxProfit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Automatically enable trailing TP when threshold is reached&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tpDrawdown&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;maxPnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;TP_SL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRAILING_TRIGGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tpDrawdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TP_SL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRAILING_DRAWDOWN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tpDrawKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tpDrawdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Check three exit conditions in order — whichever triggers first executes&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tpDrawdown&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;drawdown&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;tpDrawdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trailing TP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxSL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stop-loss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pnlPct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;takeProfit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;closeReason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fixed TP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeReason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLong&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closebuy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closesell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxProfitKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nc"&gt;_G&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tpDrawKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trailing take-profit lets winners run as far as possible, while fixed take-profit and stop-loss guard the floor. All three are active simultaneously — whichever triggers first gets executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Operation Dashboard
&lt;/h2&gt;

&lt;p&gt;After the strategy starts running, the dashboard displays four real-time status tables: Account Overview shows runtime, current equity, and total return; Tracking Queue shows each monitored token's status, launch countdown, and analysis count; AI Analysis Summary displays each token's latest full analysis conclusion; Live Position Monitor shows unrealized P&amp;amp;L, peak profit, trailing TP status, and supports manual position closing and stop-loss parameter adjustments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0xbagtssq5iwtt76d95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0xbagtssq5iwtt76d95.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
Taking the recently launched EWYUSDT as an example, the system discovered it from the announcement and has completed multiple consecutive analyses. Every time, the AI's direction has been Long, with the trend marked as "Strengthening" — this is precisely the historical memory mechanism at work. Multiple analyses pointing in the same direction indicate that the bullish signal is stable, rather than a misjudgment caused by a single data fluctuation. The latest comprehensive assessment: OKX and HTX funding rates are stable and positive, news related to the Korean ETF perpetual listing is generally positive, and the AI recommends going Long with 76% confidence. Suggested entry at the 128–130 price range at market open, 10x leverage, 5% stop-loss, 15% take-profit, medium risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy Boundaries and Risk Disclaimer
&lt;/h2&gt;

&lt;p&gt;The core problem this strategy solves is: before a new token goes live, systematically replacing manual research to build understanding of the token, and converting analytical conclusions into actionable entry signals. However, it's important to clearly recognize its limitations:&lt;/p&gt;

&lt;p&gt;New tokens are inherently extremely volatile — stop-losses may be triggered within the first minute of launch; AI judgment quality depends on data completeness, and with less data available pre-launch, assessments will be more conservative; leverage amplifies both gains and losses, and no strategy can guarantee consistent profitability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proper position sizing is the most important prerequisite for using this strategy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This system is currently more of a starting point. New listing trading is a direction with significant depth — the signal dimensions, entry timing judgments, and position management details all have substantial room for optimization. If you have your own experience and insights in this area, feel free to share in the comments. Collective wisdom will take us further. Every strategy carries the risk of loss — always refine the strategy logic based on your own circumstances before use.&lt;/p&gt;

</description>
      <category>futures</category>
      <category>workflow</category>
      <category>ai</category>
      <category>strategy</category>
    </item>
    <item>
      <title>Quantitative Trading with Liquidation Data: An AI-Automated Strategy Based on Liquidation Signals</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:24:30 +0000</pubDate>
      <link>https://forem.com/quant001/quantitative-trading-with-liquidation-data-an-ai-automated-strategy-based-on-liquidation-signals-k37</link>
      <guid>https://forem.com/quant001/quantitative-trading-with-liquidation-data-an-ai-automated-strategy-based-on-liquidation-signals-k37</guid>
      <description>&lt;p&gt;&lt;strong&gt;Preface&lt;/strong&gt;&lt;br&gt;
If you've traded perpetual contracts, you've probably been liquidated at some point. At best you lose part of your position; at worst your entire principal is gone. But have you ever thought about this — the moment you get liquidated, that data point is actually recorded?&lt;/p&gt;

&lt;p&gt;Exchanges push every forced liquidation event in real time: which token, which direction, how much notional value, and exactly when. This is liquidation data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-03-10 13:10:19 Liquidation: {"s":"DEXEUSDT","S":"BUY","o":"LIMIT","f":"IOC","q":"30.99","p":"5.427000","ap":"5.347646","X":"FILLED","l":"17.21","z":"30.99","T":1773119419184}
2026-03-10 13:10:18 Liquidation: {"s":"BEATUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"21","p":"0.3503000","ap":"0.3573000","X":"FILLED","l":"4","z":"21","T":1773119418458}
2026-03-10 13:10:18 Liquidation: {"s":"COAIUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"35","p":"0.2968000","ap":"0.3115000","X":"FILLED","l":"35","z":"35","T":1773119418118}
2026-03-10 13:10:18 Liquidation: {"s":"AIAUSDT","S":"BUY","o":"LIMIT","f":"IOC","q":"537","p":"0.0844900","ap":"0.0823800","X":"FILLED","l":"10","z":"537","T":1773119418118}
2026-03-10 13:10:09 Liquidation: {"s":"BABYUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"1965","p":"0.0161200","ap":"0.0162300","X":"FILLED","l":"1376","z":"1965","T":1773119409616}
2026-03-10 13:10:08 Liquidation: {"s":"MBOXUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"372","p":"0.0173800","ap":"0.0178100","X":"FILLED","l":"372","z":"372","T":1773119408667}
2026-03-10 13:10:07 Liquidation: {"s":"GALAUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"23717","p":"0.00337","ap":"0.00341","X":"FILLED","l":"23717","z":"23717","T":1773119407235}
2026-03-10 13:10:04 Liquidation: {"s":"RIVERUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"17.7","p":"10.945000","ap":"11.109943","X":"FILLED","l":"3.3","z":"17.7","T":1773119404767}
2026-03-10 13:10:04 Liquidation: {"s":"ROBOUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"3000","p":"0.0445100","ap":"0.0451800","X":"FILLED","l":"3000","z":"3000","T":1773119404308}
2026-03-10 13:09:47 Liquidation: {"s":"RIVERUSDT","S":"BUY","o":"LIMIT","f":"IOC","q":"48.1","p":"11.287000","ap":"11.122577","X":"FILLED","l":"17.7","z":"48.1","T":1773119387280}
2026-03-10 13:09:45 Liquidation: {"s":"DENTUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"827079","p":"0.000253","ap":"0.000257","X":"FILLED","l":"827079","z":"827079","T":1773119385320}
2026-03-10 13:09:44 Liquidation: {"s":"BULLAUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"526","p":"0.0203300","ap":"0.0213800","X":"FILLED","l":"526","z":"526","T":1773119384220}
2026-03-10 13:09:40 Liquidation: {"s":"DENTUSDT","S":"SELL","o":"LIMIT","f":"IOC","q":"554440","p":"0.000253","ap":"0.000257","X":"FILLED","l":"289977","z":"554440","T":1773119380709}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single liquidation event doesn't mean much on its own. But when you aggregate everyone's liquidations together, something very interesting emerges — you can see where capital is under pressure in the market, and which side is already cracking. Liquidation data is widely regarded as one of the most important components of order flow analysis. It represents blood-stained chips — the rawest reaction of capital that can't be faked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febh4lbbemgzcw24wcu17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febh4lbbemgzcw24wcu17.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;br&gt;
What this article sets out to do is simple: &lt;strong&gt;use liquidation data as the core signal, layer on candlestick verification and news judgment, let AI make the comprehensive decision, and run the whole thing through an automated workflow — 24/7, no manual chart-watching required.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy3ydeoqx2klybx3avsu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy3ydeoqx2klybx3avsu.png" alt=" " width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Strategy Logic
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.1 What Liquidation Data Can Tell Us&lt;/strong&gt;&lt;br&gt;
Positions are being liquidated every moment. Normal liquidations aren't signals. What we're looking for are statistical anomalies — a token experiencing liquidation volume far above its historical baseline in a short period, indicating that weak positions on one side are being systematically wiped out.&lt;/p&gt;

&lt;p&gt;After a liquidation cascade, the trend tends to continue. This is the core assumption of this strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2 Why Trend-Following, Not Mean-Reversion&lt;/strong&gt;&lt;br&gt;
When facing a wave of liquidations, there are generally two approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mean-reversion: Assume the worst is over, step in to catch the bounce&lt;/li&gt;
&lt;li&gt;Trend-following: Assume weak positions haven't finished clearing out, ride the direction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The risk of mean-reversion is that you don't know if the liquidation cascade is over. If longs are still being wiped out in droves and you step in to buy, it's like sticking your hand under a waterfall — what you catch isn't the bottom, it's more downside.&lt;/p&gt;

&lt;p&gt;So this strategy chooses trend-following — when longs get liquidated, go short; when shorts get liquidated, go long.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.3 Limitations of a Single Signal&lt;/strong&gt;&lt;br&gt;
Relying on liquidation data alone for decisions is too crude and easily thrown off by noise. So on top of the liquidation data, we add two more layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Candlestick verification: Is price action confirming the liquidation direction?&lt;/li&gt;
&lt;li&gt;News judgment: Is there a substantive event driving this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI then synthesizes all three dimensions to make the final decision.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Overall Architecture
&lt;/h2&gt;

&lt;p&gt;The workflow uses a linear structure, triggered at fixed intervals. The complete flow is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After the trigger fires, first check whether initialization has been completed&lt;/li&gt;
&lt;li&gt;If it's the first run, enter the initialization collection node to establish historical baseline data&lt;/li&gt;
&lt;li&gt;Once initialization is complete, each trigger executes in sequence: collect new data → scan for anomalous signals → determine whether entry conditions are met&lt;/li&gt;
&lt;li&gt;If an anomaly is found, continue downstream into data enrichment, AI judgment, and trade execution nodes&lt;/li&gt;
&lt;li&gt;If no anomaly is found, terminate immediately and wait for the next trigger interval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire flow halts when conditions aren't met and continues when they are — no manual intervention needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Key Node Breakdown
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1 Initialization Collection&lt;/strong&gt;&lt;br&gt;
On first startup, the system needs to establish historical baseline data. It connects to Binance Futures' forced liquidation push channel via WebSocket, continuously collecting liquidation data for a period and storing it in global variables.&lt;/p&gt;

&lt;p&gt;Each data point contains four fields: timestamp, token, liquidation direction, and liquidation amount. During collection, two types of data are filtered out: partially filled forced liquidation orders, and small-amount noise data. Once collection is complete, the data is written to persistent storage and initialization is marked as done. On subsequent triggers, this node is skipped entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excerpt from source code
var ws = Dial('wss://fstream.binance.com/ws/!forceOrder@arr')

while (Date.now() &amp;lt; endTs) {
    var msg = ws.read(1000)
    try {
        var obj = JSON.parse(msg)
        var orders = Array.isArray(obj) ? obj : [obj]
        for (var i = 0; i &amp;lt; orders.length; i++) {
            var item = orders[i]
            if (!item || !item.o) continue
            var o = item.o
            if (o.X !== 'FILLED') continue
            if (EXCLUDE[o.s]) continue
            if (!/USDT$/i.test(o.s)) continue       // ✅ Only process USDT contracts
            var price = parseFloat(o.ap || o.p)
            var qty = parseFloat(o.z)
            var value = price * qty
            if (value &amp;lt; MIN_VALUE) continue
            liquidationData.push({
                t: item.E || Date.now(),
                s: o.s,
                d: o.S,
                v: value
            })
            totalNew++
        }
    } catch(e) {}
}

_G('liquidationData', liquidationData)               // Persistent storage
_G('liqInitialized', true)                           // Mark initialization complete

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2 Strategy Execution: Collection + Z-Score Scanning&lt;/strong&gt;&lt;br&gt;
On each trigger, new liquidation data is first collected via WebSocket and appended to the historical window, then Z-Score anomaly detection is run per token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The core idea behind Z-Score:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Divide the baseline period's liquidation data into equal-length time segments, recording the total liquidation amount for each monitoring segment. Then take the latest monitoring segment's liquidation volume and compare it against the mean of all historical segments in the baseline period, calculating the degree of deviation. Only when the deviation exceeds the threshold is it considered anomalous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excerpt from source code
var mean = hist.reduce(function(s, v) { return s + v }, 0) / hist.length
var std = Math.sqrt(
    hist.reduce(function(s, v) { return s + Math.pow(v - mean, 2) }, 0) / hist.length
)
var z = std &amp;gt; 0 ? (rec - mean) / std : 0
if (z &amp;lt; ZSCORE_THRESH || rec &amp;lt;= 0) continue

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefit of this approach is self-adaptation: active tokens have active baselines, illiquid tokens have their own baselines — you won't get false positives just because a token naturally has high trading volume.&lt;/p&gt;

&lt;p&gt;Volume alone isn't enough — you also need to check whether the direction is one-sided:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excerpt from source code
var longRatio = total &amp;gt; 0 ? st.longV / total : 0.5
var direction = null
if (longRatio &amp;gt; DIR_THRESH) direction = 'SHORT'       // Longs getting wiped → trend-follow short
else if (longRatio &amp;lt; 1 - DIR_THRESH) direction = 'LONG' // Shorts getting wiped → trend-follow long
if (!direction) continue                                // Mixed liquidations → direction unclear, skip

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both conditions must be met simultaneously for a signal to trigger: Z-Score above threshold, AND one-sided liquidation (long or short) exceeding 75%. When both sides are getting wiped equally, direction is unclear — skip entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3 Data Enrichment: Candlesticks + News&lt;/strong&gt;&lt;br&gt;
After a signal triggers, the system doesn't open a position immediately. Instead, it enters a data enrichment node to prepare more complete context for the subsequent AI judgment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Candlestick component:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fetch the token's most recent 1-minute candlesticks, calculate price change and volatility, determine the current candlestick trend direction, and compare it with the liquidation signal direction to check for alignment. If the signal suggests shorting but the candlesticks are still trending up, credibility takes a hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;News component:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Via the Brave Search API, fetch the token's latest news for the day. Liquidations driven by concrete events have a much higher probability of trend continuation; if there's no news and it's purely technical clearing, the AI will be more conservative.&lt;/p&gt;

&lt;p&gt;Once both types of data are collected, they're sent to the AI together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.4 AI Comprehensive Judgment&lt;/strong&gt;&lt;br&gt;
The AI receives liquidation data, candlesticks, and news, then evaluates based on a fixed decision framework — all three dimensions are indispensable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision matrix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa73v5p4o34nmztpj0xm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa73v5p4o34nmztpj0xm.png" alt=" " width="588" height="216"&gt;&lt;/a&gt;&lt;br&gt;
The AI outputs a structured result containing direction, decision, confidence level, and reasoning. Only entry signals that reach the confidence threshold will actually trigger position opening downstream.&lt;/p&gt;

&lt;p&gt;The core of the prompt is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Judgment Steps

Step 1: Liquidation Intensity
- Z-Score exceeds high threshold AND direction purity is very high → Strong signal
- Z-Score meets base threshold AND direction purity meets requirements → Medium signal
- Otherwise → Don't enter

Step 2: Candlestick Trend
- Aligned with liquidation direction → High probability of trend continuation, positive factor
- Opposite to liquidation direction → Possibly just a temporary clearing rather than a trend, negative factor

Step 3: News Verification
- Substantive bearish/bullish news aligned with direction → Positive factor
- No news → Pure technical liquidation, reduce confidence
- News contradicts direction → Don't enter

## Output Format
Return strict JSON, no markdown wrapping:
{
    "symbol": "token",
    "direction": "LONG or SHORT",
    "action": "enter/hold/don't enter",
    "confidence": "high/medium/low",
    "liq_note": "One-sentence assessment of liquidation signal",
    "trend_note": "One-sentence assessment of candlestick trend",
    "news_note": "One-sentence assessment of news",
    "action_reason": "One-sentence comprehensive reasoning"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.5 Trade Execution and Stop-Loss Management&lt;/strong&gt;&lt;br&gt;
After the AI decides to enter, the trade execution node automatically opens the position while also managing stop-losses and updating the visualization dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Position opening flow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before opening, signals are sorted by confidence — high-confidence signals execute first. If the token already has an open position and the new signal has higher confidence, the old position is closed first and then a new one is opened; if the new signal's confidence doesn't exceed the existing position's, it's skipped. When the number of open positions reaches the cap, all subsequent signals are skipped.&lt;/p&gt;

&lt;p&gt;Position size, leverage multiplier, and maximum number of positions are all configured via external variables. Contract quantity is dynamically calculated based on the real-time price from current market depth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excerpt from source code
// Calculate position size
var rawQty = OPEN_MONEY * CONFIG.LEVERAGE / refPrice / mkt.ctVal
var qty = floorToStep(rawQty, mkt.amtSize, mkt.amtPrec)

// Market order entry
var side = direction === 'LONG' ? 'buy' : 'sell'
var oid = exchange.CreateOrder(swapSym, side, -1, qty)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop-loss mechanism:&lt;/p&gt;

&lt;p&gt;Stop-loss uses a dual-layer mechanism with both defenses active simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trailing stop: Tracks the price extreme; if the pullback from peak unrealized profit exceeds a set percentage, close the position. Let profits run, but don't give back everything already earned.&lt;/li&gt;
&lt;li&gt;Fallback stop: The last line of defense; if price moves against the entry by more than a set percentage, force close.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excerpt from source code
if (pos.direction === 'LONG') {
    var trailStop = pos.peak * (1 - TRAILING_PCT)           // Trailing stop price
    var fallStop = pos.entryPrice * (1 - FALLBACK_PCT)      // Fallback stop price
    effectiveStop = Math.min(trailStop, fallStop)
    if (cur &amp;lt;= effectiveStop) triggered = true
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visualization dashboard:&lt;/p&gt;

&lt;p&gt;After each execution, three tables are updated in real time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account overview: Balance, number of positions, cumulative P&amp;amp;L, stop-loss parameters&lt;/li&gt;
&lt;li&gt;Position monitor: Entry price, current price, P&amp;amp;L percentage, max profit, current drawdown, and stop-loss price for each position&lt;/li&gt;
&lt;li&gt;Latest AI signals: Liquidation signal, candlestick assessment, news assessment, and comprehensive decision for each scanned token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qeia8llk7nkjbz0boqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qeia8llk7nkjbz0boqz.png" alt=" " width="741" height="523"&gt;&lt;/a&gt;&lt;br&gt;
You can see what the strategy is doing at any time — no need to watch the charts.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Complete Flow Recap
&lt;/h2&gt;

&lt;p&gt;Here's the complete logic of this strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialization:&lt;/strong&gt; On first run, collect historical liquidation data to establish baselines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collection:&lt;/strong&gt; On each trigger, collect new liquidation data and update the historical window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scanning:&lt;/strong&gt; Use Z-Score to find tokens with statistically anomalous liquidation volumes; filter out signals with impure directionality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enrichment:&lt;/strong&gt; Fetch candlestick and news data for anomalous tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judgment:&lt;/strong&gt; AI synthesizes all three dimensions to make entry decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution:&lt;/strong&gt; Auto-open positions sorted by confidence, with dual-layer stop-loss and visualization dashboard throughout&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is an exploration of AI-enhanced trading built on traditional liquidation data. Liquidation data itself is the market's rawest reaction; layering on candlesticks and news effectively improves signal credibility; and the introduction of AI makes multi-dimensional decision-making automatable.&lt;/p&gt;

&lt;p&gt;If you're interested in strategies based on on-chain or market microstructure data like this, feel free to leave feedback — further strategy development on more signal types can follow.&lt;/p&gt;

</description>
      <category>workflow</category>
      <category>aitrading</category>
      <category>strategy</category>
      <category>code</category>
    </item>
    <item>
      <title>Don't Panic When Coins Pump! Workflow-Powered Shorting to Catch the Pullback</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Fri, 13 Mar 2026 01:41:23 +0000</pubDate>
      <link>https://forem.com/quant001/dont-panic-when-coins-pump-workflow-powered-shorting-to-catch-the-pullback-31g2</link>
      <guid>https://forem.com/quant001/dont-panic-when-coins-pump-workflow-powered-shorting-to-catch-the-pullback-31g2</guid>
      <description>&lt;p&gt;This strategy was open-sourced by user @Gianbin, adapted from our earlier dual-route workflow trading framework. Thanks for the generous contribution — here's a full breakdown of the strategy logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Core Concept: Contrarian Shorting of Pump Coins
&lt;/h2&gt;

&lt;p&gt;In crypto, "pump coins" appear frequently — tokens that surge 30–40% or even 100%+ in a single day. They look tempting, but the harder they pump, the harder they tend to dump.&lt;/p&gt;

&lt;p&gt;This strategy exploits exactly that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screen the top gainers from Binance's 24-hour leaderboard&lt;/li&gt;
&lt;li&gt;Use an AI multi-factor model to identify which ones have "topped out"&lt;/li&gt;
&lt;li&gt;Open short positions and wait for the pullback, profiting from overleveraged chasers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Capital management principle:&lt;/strong&gt; Fixed 50 USDT per entry, total position capped at 500 USDT — relatively controlled risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Workflow Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The strategy consists of two independent workflows, each with its own role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Main Trading Flow&lt;/strong&gt; (every 15 min): Screen top gainers → Collect data → AI analysis → Execute entry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk Control Flow&lt;/strong&gt; (every 5 sec): Real-time monitoring → TP/SL detection → Inverse pyramid scaling → Visual dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Strategy decisions can run at a relaxed pace, but risk control must react fast. That's why the trigger frequencies differ so dramatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodc1ta2aplx7lsh5r756.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodc1ta2aplx7lsh5r756.png" alt=" " width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Main Trading Flow — Step by Step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Screen Pump Coins from the Gainers List&lt;/strong&gt;&lt;br&gt;
A timer trigger fires every 15 minutes, pulling all USDT perpetual contract tickers from Binance. It filters for tokens with 24-hour gains exceeding 10% and takes the top 20. Tokens already in open positions are automatically excluded to prevent duplicate entries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Core screening logic (excerpt)
const minChange = $vars.minChange || 0.1; // Default gain threshold: 10%
const topN = $vars.topN;                  // Top N, default 20

// Filter USDT perpetuals, calculate 24h change
const change24h = open24h &amp;gt; 0 ? (price - open24h) / open24h : 0;
if (change24h &amp;lt; minChange) continue;      // Below threshold, skip

// Exclude tokens already in positions
if (excludeHolding &amp;amp;&amp;amp; holdingSymbols.indexOf(symbol) !== -1) continue;

// Sort by gain descending, take top N
usdtPairs.sort((a, b) =&amp;gt; b.change24h - a.change24h);
const topGainers = usdtPairs.slice(0, topN);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step is essentially an "audition" — rounding up the hottest pump coins for further analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Multi-Dimensional Data Collection&lt;/strong&gt;&lt;br&gt;
Price gains alone aren't enough. The system simultaneously collects the following data to feed the AI analysis:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwpu69a4anbtmnwpdpls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwpu69a4anbtmnwpdpls.png" alt=" " width="750" height="187"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;strong&gt;OI/MCap ratio&lt;/strong&gt; is the core metric — the higher this ratio, the more leveraged the market is, the greater the liquidation risk, and the better the odds for shorting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Fetching open interest via Binance API (excerpt)
const ret = exchange.IO("api", "GET", "/fapi/v1/openInterest", "symbol=" + symbol);
if (ret &amp;amp;&amp;amp; ret.openInterest) {
    openInterest = parseFloat(ret.openInterest) * coin.price; // Convert to USD
}

// Calculate OI/MCap ratio
const oiMcapRatio = marketCap &amp;gt; 0 ? openInterest / marketCap : 0;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Market cap data is fetched from the CoinMarketCap API with a 30-minute local cache to avoid excessive external API calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: AI Six-Factor Scoring System&lt;/strong&gt;&lt;br&gt;
This is the heart of the strategy. After collecting data, everything is packaged and sent to an AI model (x-ai/grok-4.1-fast in this case) for scoring on a scale of 10. Only targets scoring 8.0 or above make it to the candidate entry list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Six Factors and Weights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp8cwgngnznldxi7lq33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp8cwgngnznldxi7lq33.png" alt=" " width="606" height="252"&gt;&lt;/a&gt;&lt;br&gt;
OI/MCap ratio carries the highest weight (3.5) because it directly reflects market leverage — higher leverage means a reversal is more likely to trigger cascading liquidations, giving shorts better risk-reward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;K-line Pattern Scoring Rules&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long upper shadow breaking new high&lt;/strong&gt; (2.8 pts): High makes a new high, upper shadow &amp;gt; body × 2&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;Long upper shadow at highs *&lt;/em&gt;(2.4 pts): Upper shadow &amp;gt; body × 2, close near 24h high&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bearish candle on high volume&lt;/strong&gt; (2.0 pts): Closes red, volume &amp;gt; previous day × 1.5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doji at highs&lt;/strong&gt; (1.8 pts): Body &amp;lt; range × 0.1, price near the high&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two consecutive bearish candles&lt;/strong&gt; (1.5 pts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hard Filters (Applied Before Scoring)&lt;/strong&gt;&lt;br&gt;
Before AI scoring, the strategy applies several hard filters — failing any one means the token is skipped entirely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Already in position → skip (prevent overconcentration)&lt;/li&gt;
&lt;li&gt;Funding rate &amp;lt; -0.20% → skip (shorts already overcrowded)&lt;/li&gt;
&lt;li&gt;Open interest &amp;lt; $30M → skip (insufficient liquidity)&lt;/li&gt;
&lt;li&gt;Weekly drawdown &amp;gt; 5% → skip (too far from high, may have already pulled back)&lt;/li&gt;
&lt;li&gt;Intraday drawdown &amp;gt; 5% → skip (already dropped from today's high, no longer at peak)
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Weekly high calculation (excerpt)
const weeklyHigh = Math.max(...klines.slice(-7).map(k =&amp;gt; k.high));
const weeklyDrawdown = (weeklyHigh - price) / weeklyHigh;
if (weeklyDrawdown &amp;gt; 0.05) { // More than 5% from weekly high, skip
    filtered.weeklyDrawdown++;
    continue;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Final Decision Logic&lt;/strong&gt;&lt;br&gt;
When total score ≥ 8.0 and intraday drawdown ≤ 5%, the decision logic kicks in: if funding rate ≥ -0.15%, output "open short"; if funding rate is between -0.20% and -0.15%, output "cautious short." Targets scoring below 8.0 are not outputted and no positions are opened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Trade Execution&lt;/strong&gt;&lt;br&gt;
The AI outputs decisions in JSON format, and the trade execution node parses and places orders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Execute short (excerpt)
function executeShort(coin, signalInfo) {
    exchange.SetCurrency(coin + '_USDT');
    exchange.SetContractType("swap");
    exchange.SetMarginLevel(CONFIG.DEFAULT_LEVERAGE); // Set required leverage

    // Calculate contract size from fixed amount
    const contractAmount = calculateContractAmount(
        CONFIG.FIXED_AMOUNT_USD, // Fixed 50U
        currentPrice,
        market
    );

    exchange.SetDirection("sell");
    const orderId = exchange.Sell(-1, contractAmount); // Market short

    if (orderId) {
        _G(`${coin}_USDT.swap_maxprofit`, 0); // Initialize max profit tracker
        Log(`✅ ${coin}: Short opened, score ${signalInfo.score}`);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry uses a fixed 50 USDT — single-trade risk is well-contained.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Risk Control Flow — Detailed Breakdown
&lt;/h2&gt;

&lt;p&gt;Triggers every 5 seconds, continuously monitoring all open positions with two core functions: inverse pyramid scaling and take-profit / stop-loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inverse Pyramid Scaling&lt;/strong&gt;&lt;br&gt;
This is the most interesting design in the strategy. After opening a short, if the price rises instead of falling, most traders would stop out. But this strategy chooses to scale in against the trend — the higher it goes, the more it adds — because the higher the pump, the harder the eventual crash tends to be.&lt;/p&gt;

&lt;p&gt;Scaling rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial entry: 50U (entry price = P0)&lt;/li&gt;
&lt;li&gt;If price rises 50% from P0 → first add: 150U, record price as P1&lt;/li&gt;
&lt;li&gt;If price rises 70% from P1 → second add: 300U&lt;/li&gt;
&lt;li&gt;Maximum 2 additions, total position cap: 500U (50 + 150 + 300)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Pyramid add detection (excerpt)
function checkAndExecutePyramidAdd(coin, entryPrice, currentPrice, isShort) {
    const addCount = _G(addCountKey) || 0;
    if (addCount &amp;gt;= 2) return null; // Max 2 additions

    if (addCount === 0) {
        // First add: triggered at 50% above entry
        triggerPrice = storedEntryPrice * (1 + PYRAMID_CONFIG.ADD1_TRIGGER);
        addAmount = PYRAMID_CONFIG.ADD1_AMOUNT; // 150U
    } else if (addCount === 1) {
        // Second add: triggered at 70% above first add price
        triggerPrice = add1Price * (1 + PYRAMID_CONFIG.ADD2_TRIGGER);
        addAmount = PYRAMID_CONFIG.ADD2_AMOUNT; // 300U
    }

    if (currentPrice &amp;gt;= triggerPrice) {
        return {
            level: addCount + 1,
            amount: addAmount,
            triggerPrice,
            currentPrice
        };
    }
    return null;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The logic behind this design:&lt;/strong&gt; The more a pump coin surges, the more frenzied the speculation, and the bigger the bubble. When the reversal finally hits, high leverage triggers cascading liquidations that amplify the crash. Holding a larger position at higher prices means profits multiply when the reversal comes.&lt;/p&gt;

&lt;p&gt;**The risk is equally obvious: **If the token genuinely keeps rising without looking back (e.g., a real bull market or major fundamental catalyst), losses scale up too. Therefore, the account should hold sufficient funds to withstand volatility and avoid forced liquidation. The strategy explicitly caps total position at 500U to prevent unlimited scaling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take-Profit / Stop-Loss Mechanism&lt;/strong&gt;&lt;br&gt;
Take-profit uses a trailing drawdown model rather than a fixed target:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When max unrealized profit reaches 35%, drawdown monitoring activates automatically&lt;/li&gt;
&lt;li&gt;Once profit pulls back 5% from its peak, the position is closed immediately
This design "lets profits run" — it won't exit too early and miss bigger gains, but also won't ride the elevator back down and give it all back.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Auto take-profit trigger logic (excerpt)
if (enableAutoTpDrawdown &amp;amp;&amp;amp; isShort &amp;amp;&amp;amp; tpDrawdown === 0 
    &amp;amp;&amp;amp; maxPnlPercent &amp;gt;= autoTpTrigger) {
    tpDrawdown = autoTpDrawdownValue; // Set 5% trailing TP
    _G(tpDrawdownKey, tpDrawdown);
    Log(`🎯 ${coin} max P&amp;amp;L reached ${maxPnlPercent}%, enabling 5% trailing TP`);
}

// Trigger take-profit
if (tpDrawdown &amp;gt; 0 &amp;amp;&amp;amp; maxPnlPercent &amp;gt; 0 &amp;amp;&amp;amp; drawdown &amp;gt;= tpDrawdown) {
    autoCloseReason = `Trailing TP (drawdown ${drawdown}% ≥ ${tpDrawdown}%)`;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For stop-loss, the strategy supports manually setting a fixed percentage stop-loss as a hard floor of protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Visual Dashboard
&lt;/h2&gt;

&lt;p&gt;The strategy includes four built-in monitoring panels for real-time operational awareness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Account Overview:&lt;/strong&gt; Equity, cumulative P&amp;amp;L, profit rate, account leverage (with color-coded risk levels)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Signal Table:&lt;/strong&gt; Records every AI decision's score, conclusion, per-factor scores and reasoning — no guessing what the AI is thinking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Position Monitor:&lt;/strong&gt; Real-time display of each position's entry price, current price, unrealized P&amp;amp;L, max profit record, current drawdown, scaling trigger prices, and TP/SL status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grid Details:&lt;/strong&gt; Order status when positions are combined with grid trading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9anjqkymhpo2nxexp0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9anjqkymhpo2nxexp0e.png" alt=" " width="800" height="205"&gt;&lt;/a&gt;&lt;br&gt;
Refreshes every 5 seconds, rendered as interactive tables via FMZ's LogStatus, supporting direct actions like closing positions and modifying TP/SL parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Objective Pros &amp;amp; Cons Assessment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contrarian approach with natural counterparties.&lt;/strong&gt; Shorting while the market chases means ample liquidity and minimal slippage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI multi-factor screening — not blind shorting.&lt;/strong&gt; Six dimensions of comprehensive scoring filter out most low-quality signals. Only targets scoring 8+ get traded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inverse pyramid + trailing TP = solid risk-reward.&lt;/strong&gt; Adding more at higher prices means bigger profits on reversal; flexible TP lets profits fully develop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict position control.&lt;/strong&gt; 500U max per trade — even a total loss is within tolerance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Risks &amp;amp; Limitations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sustained losses in a one-sided bull market.&lt;/strong&gt; If the market enters a strong uptrend, shorting the top gainers can mean consecutive losses.&lt;/li&gt;
&lt;li&gt;**Inverse pyramid scaling is a double-edged sword. **Scaling against the trend amplifies winning trades, but equally amplifies losing ones. If a pump coin genuinely keeps rising (e.g., a project with a major fundamental catalyst), all three position layers take losses — maximum exposure is 500U.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best suited for&lt;/strong&gt;: Ranging or bearish environments. Not suitable for one-sided bull markets. Assess the broader market context before deploying.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The core philosophy of this strategy: replace subjective judgment with data and AI, replace wishful thinking with strict position control.&lt;/p&gt;

&lt;p&gt;Whether it's the design of the six-factor scoring system or the choice of OI/MCap ratio as the core metric, you can see the author has a solid understanding of market microstructure — knowing which indicators truly matter and which are just noise.&lt;/p&gt;

&lt;p&gt;That said, every strategy has its boundaries. Tools are static; markets are dynamic. We strongly recommend thorough backtesting before going live, adjusting parameters to your own risk tolerance, and never copying blindly.&lt;/p&gt;

&lt;p&gt;Special thanks to user @Gianbin for sharing this strategy so openly. It's this kind of open-source spirit that gives more people the opportunity to learn and explore the possibilities of quantitative trading. If you have great strategy ideas of your own, we'd love to hear about them!&lt;/p&gt;

</description>
      <category>workflow</category>
      <category>coin</category>
      <category>strategy</category>
      <category>quantitative</category>
    </item>
    <item>
      <title>Practice of Web3 Tron Encapsulated and Access to SunSwap DEX Based on FMZ Platform</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Mon, 09 Mar 2026 07:52:13 +0000</pubDate>
      <link>https://forem.com/quant001/practice-of-web3-tron-encapsulated-and-access-to-sunswap-dex-based-on-fmz-platform-34kl</link>
      <guid>https://forem.com/quant001/practice-of-web3-tron-encapsulated-and-access-to-sunswap-dex-based-on-fmz-platform-34kl</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;In the field of blockchain tradings, decentralized exchanges (DEX) provide a trustless way to exchange assets, and SunSwap, as a representative DEX in the Tron ecosystem, supports smart contract interactions based on Web3. We FMZ platform provides a powerful quantitative trading framework that enables developers to integrate multiple trading strategies.&lt;/p&gt;

&lt;p&gt;The FMZ platform already supports the encapsulation of Web3 Ethereum (ETH) exchange objects, and added support for Web3 TRON recently, encapsulating the exchange objects of the TRON chain. Just like accessing UniSwap DEX, access to SunSwap DEX is also possible.&lt;/p&gt;

&lt;p&gt;This article will explore how to encapsulate the Web3 interface based on the FMZ Quant platform and access SunSwap DEX for trading. We will introduce the Web3 interaction method of the Tron network, the SunSwap smart contract call, and show how to query liquidity, execute transactions, and obtain transaction results through code examples, so as to build an efficient quantitative trading strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;All codes and designs shared in this article have been tested and are intended for learning and communication. However, even after testing, these contents may still need to be adjusted according to actual needs. If you want to apply them to live trading, please be sure to evaluate, optimize and modify them yourself to ensure that they meet your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Configure the Web3 TRON exchange object of the FMZ platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the previous article FMZ Quant Web3 Expansion: Tron Support Added, Expand On-chain Tradings, we have introduced the common methods of Tron gRPC nodes and learned how to configure Web3 TRON exchange objects on FMZ. This article will not repeat these contents. As for the node address, we will use the node provided by TRON official directly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy the docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://youtu.be/xpfaimePdHc?si=wVASaGguaaxNwqkS" rel="noopener noreferrer"&gt;https://youtu.be/xpfaimePdHc?si=wVASaGguaaxNwqkS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike CEX exchanges, there is no wall to worry about. You can access the official node by deploying a docker locally.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SunSwap DEX
&lt;a href="https://sun.io/" rel="noopener noreferrer"&gt;https://sun.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open the front-end page of SunSwap in the browser, you can connect to the wallet and observe the data conveniently.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tronscan
&lt;a href="https://tronscan.org/" rel="noopener noreferrer"&gt;https://tronscan.org/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essential tools for viewing and analyzing data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;base58
The address on TRON is base58 encoded, which is different from Ethereum. Sometimes, in order to view and compare data, some conversion is required. On FMZ, you can use the base58 encoded address directly. Some smart contracts return data in HEX encoding, which requires some conversion. Several functions are simply implemented to convert the address format:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following are the main conversion functions. Due to limited space, I will not post all the codes. The complete code is in the "Tron SunSwap V3 Trading Library" template library disclosed at the end of the article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ethAddressToTron(ethAddress) {
    if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) {
        throw "Invalid Ethereum address"
    }

    const ethAddressBytes = new Uint8Array(20)
    for (let i = 0; i &amp;lt; 20; i++) {
        ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16)
    }

    const tronAddressBytes = new Uint8Array(21)
    tronAddressBytes[0] = 0x41
    tronAddressBytes.set(ethAddressBytes, 1)

    const hash1 = Encode("sha256", "hex", "hex", uint8ArrayToHex(tronAddressBytes))
    const hash2 = Encode("sha256", "hex", "hex", hash1)
    const checksum = hash2.slice(0, 8)

    const fullAddress = new Uint8Array(25)
    fullAddress.set(tronAddressBytes, 0)
    fullAddress.set(hexToUint8Array(checksum), 21)

    return base58Encode(fullAddress)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Package call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because sometimes you need to read data from multiple methods at once, calling them one by one is time-consuming and laborious. So a packaged calling function is encapsulated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function multicall(e, multicallContractAddress, data, outTypes) {
    let ret = e.IO("api", multicallContractAddress, "aggregate", data)
    if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) {
        Log("invalid ret:", ret)
        return null
    }

    let arrRet = ret["returnData"]
    if (outTypes.length != arrRet.length) {
        Log("Arrays have unequal lengths:", arrRet, outTypes)
        return null
    }

    let outData = []
    for (let i in arrRet) {
        outData.push(e.IO("decode", outTypes[i], arrRet[i]))
    }

    return outData
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tron SunSwap V3 Trading Library
&lt;/h2&gt;

&lt;p&gt;The design of SunSwap V3 trading library is modeled after the UniSwap trading library template of the FMZ platform. Since the SunSwap template code is long, I will not post it here, and will mainly explain the functions implemented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create SunSwap Trading Object&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let e = $.CreateSunSwapEx()                 // By default, the exchange exchanges[0] is used to initialize the SunSwap trading object.
// let e = $.CreateSunSwapEx(exchanges[1])  // Initialize the SunSwap trading object using the secondary exchange object.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template has only one interface function, namely: $.CreateSunSwapEx(), which is used to create a SunSwap trading object. After the object is created, you can call the method of this object to perform some function calls, such as: exchange transactions, query pool prices, obtain all V3 pool information, pledge, decompression, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GetMarkets&lt;/strong&gt;&lt;br&gt;
The test codes of "Tron SunSwap V3 Trading Library" are all recorded in the main() function of the template and will not be repeated here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let markets = e.GetMarkets()
    let idx = 0
    let tbl = {"type": "table", "title": "test GetMarkets", "cols": ["Index", "PoolAddress", "Symbol", "BaseAsset", "QuoteAsset", "BaseName", "QuoteName", "BaseDecimals", "QuoteDecimals", "BaseAddress", "QuoteAddress"], "rows": []}
    for (let currency in markets) {
        let arrMarket = markets[currency]
        for (let market of arrMarket) {
            tbl["rows"].push([idx, market["PoolAddress"], market["Symbol"], market["BaseAsset"], market["QuoteAsset"], market["BaseName"], market["QuoteName"], market["BaseDecimals"], market["QuoteDecimals"], market["BaseAddress"], market["QuoteAddress"]])    
            idx++
        }
    }
    LogStatus("`" + JSON.stringify(tbl) + "`")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function GetMarkets() is used to obtain the relevant information of all exchange pools in the SunSwap V3 pool, cached in the variables of the SunSwap trading object, and used to query accuracy, address and other information during other operations.&lt;/p&gt;

&lt;p&gt;After running the test code, the information of all V3 pools will be displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxayv936d40413boopsjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxayv936d40413boopsjd.png" alt=" " width="800" height="209"&gt;&lt;/a&gt;&lt;br&gt;
The code has removed some of the contract addresses currently seen, such as "phishing contracts" and "invalid contracts".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GetTicker&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let ticker1 = e.GetTicker("WTRX_USDT")
    Log("symbol:", "WTRX_USDT", ", ticker:", ticker1)
    let ticker2 = e.GetTicker("iCMX_USDT", "TLVDozYEBgeaJXH7oKBsougzEJKNrogFun")  // iCMX_USDT
    Log("symbol:", "iCMX_USDT", ", ticker:", ticker2)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the GetTicker() function to query the price of a certain exchange pool, and you can specify the address of a specific pool. Note that some pools have poor liquidity and the price is only for reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;multicall&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let data = []
    let walletAddress = exchange.IO("address")
    data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)])    
    data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)])    
    Log(data)
    let ret = multicall(exchange, "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", data, ["uint256", "uint256"])
    Log(ret, toAmount(ret[0], 6))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function can be used for subsequent function encapsulation. For example, in the example, two balanceOf method calls can be encoded and the data can be requested once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GetSwapOutAmount&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let retBuy = e.GetSwapOutAmount("WTRX_USDT", 1, "buy")     // 1 quote -&amp;gt; base
    Log("WTRX_USDT", "buy outAmount:", retBuy["outAmount"], ", ask:", (1 / retBuy["outAmount"]))
    let retSell = e.GetSwapOutAmount("WTRX_USDT", 1, "sell")   // 1 base -&amp;gt; quote
    Log("WTRX_USDT", "sell outAmount:", retSell["outAmount"], ", bid:", (retSell["outAmount"] / 1))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an important function, which encapsulates SunSwap's intelligent routing service. Given tokenInSymbol and tokenOutSymbol, it can return possible exchange paths, the number of output tokens and other information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GetAssets&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let assets = e.GetAssets()
    let tbl = {"type": "table", "title": "test GetAssets", "cols": ["Name", "Address", "Decimals", "Balance"], "rows": []}   
    for (let asset of assets) {
        tbl["rows"].push([asset.Currency, asset.Address, asset.Decimals, asset.Amount])
    }
    LogStatus("`" + JSON.stringify(tbl) + "`")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since there is no good mechanism to traverse TRC20 tokens, with the help of tronscan's function, this third-party interface is used to query account asset information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe290vpj9ik59nrktba5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe290vpj9ik59nrktba5s.png" alt=" " width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmmfubs69c06b3g5stu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmmfubs69c06b3g5stu4.png" alt=" " width="538" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Swap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // 6 USDT -&amp;gt; ? WTRX
    let ret = e.Swap("WTRX_USDT", 6, "buy")
    Log(ret)

    // 10 TRX -&amp;gt; ? WTRX
    // let ret = e.Swap("TRX_WTRX", 10, "sell")
    // Log(ret)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important function is this exchange. When executing the exchange, the exchange path will be formulated according to the smart routing first, and then the exchange method will be called to exchange the token.&lt;br&gt;
It should be noted that if it is a TRX package/unpackage exchange, it is only executed using the WTRX contract method.&lt;/p&gt;

&lt;p&gt;We perform the following tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let ret = e.Swap("TRX_USDT", 5, "sell")
Log(ret)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw6g8o1626fb7t8o21k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw6g8o1626fb7t8o21k2.png" alt=" " width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e5fj5qffm17as4f6rua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e5fj5qffm17as4f6rua.png" alt=" " width="800" height="240"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Other Encapsulations&lt;/strong&gt;&lt;br&gt;
There are some other functions such as "staking" and "unstaking". You can check them in the main() function in the template code, so I won't go into details.&lt;br&gt;
You can also expand and add functions such as adding liquidity and removing liquidity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template Code&lt;/strong&gt;&lt;br&gt;
This template does not have any interaction design and interface parameters. It only implements the basic function of accessing SunSwap DEX. More functions can be optimized and added later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isEmptyObject(obj) {
    return Object.keys(obj).length === 0
}

function computePoolPrice(decimals0, decimals1, sqrtPriceX96) {
    [decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt)
    const TWO = BigInt(2)
    const TEN = BigInt(10)
    const SIX_TENTH = BigInt(1000000)
    const Q192 = (TWO ** BigInt(96)) ** TWO
    return (Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) / Number(SIX_TENTH))
}

function multicall(e, multicallContractAddress, data, outTypes) {
    let ret = e.IO("api", multicallContractAddress, "aggregate", data)
    if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) {
        Log("invalid ret:", ret)
        return null
    }

    let arrRet = ret["returnData"]
    if (outTypes.length != arrRet.length) {
        Log("Arrays have unequal lengths:", arrRet, outTypes)
        return null
    }

    let outData = []
    for (let i in arrRet) {
        outData.push(e.IO("decode", outTypes[i], arrRet[i]))
    }

    return outData
}

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function hexToUint8Array(hex) {
    if (hex.length % 2 !== 0) {
        throw new Error("Invalid hex string length")
    }
    return Uint8Array.from(
        hex.match(/.{1,2}/g).map(byte =&amp;gt; parseInt(byte, 16))
    )
}

function uint8ArrayToHex(array) {
    return Array.from(array).map(byte =&amp;gt; byte.toString(16).padStart(2, "0")).join("")
}

function ethAddressToTron(ethAddress) {
    if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) {
        throw "Invalid Ethereum address"
    }

    const ethAddressBytes = new Uint8Array(20)
    for (let i = 0; i &amp;lt; 20; i++) {
        ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16)
    }

    const tronAddressBytes = new Uint8Array(21)
    tronAddressBytes[0] = 0x41
    tronAddressBytes.set(ethAddressBytes, 1)

    const hash1 = Encode("sha256", "hex", "hex", uint8ArrayToHex(tronAddressBytes))
    const hash2 = Encode("sha256", "hex", "hex", hash1)
    const checksum = hash2.slice(0, 8)

    const fullAddress = new Uint8Array(25)
    fullAddress.set(tronAddressBytes, 0)
    fullAddress.set(hexToUint8Array(checksum), 21)

    return base58Encode(fullAddress)
}

function base58Encode(buffer) {
    const base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
    let num = BigInt("0x" + Array.from(buffer).map(b =&amp;gt; b.toString(16).padStart(2, "0")).join(""))
    let encoded = ""
    while (num &amp;gt; 0) {
        let remainder = num % 58n
        num = num / 58n
        encoded = base58Alphabet[Number(remainder)] + encoded
    }
    for (let byte of buffer) {
        if (byte === 0) {
            encoded = base58Alphabet[0] + encoded
        } else {
            break
        }
    }

    return encoded
}

function base58ToHex(base58Str) {
    const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

    var num = BigInt(0)
    for (var char of base58Str) {
        var digit = BigInt(ALPHABET.indexOf(char))
        if (digit === BigInt(-1)) throw new Error("Invalid Base58 character: " + char)
        num = num * BigInt(58) + digit
    }

    var hex = num.toString(16)    
    if (hex.length % 2 !== 0) {
        hex = "0" + hex
    }

    return "0x" + hex
}

$.CreateSunSwapEx = function(e) {
    let sunSwapEx = {}

    sunSwapEx.registerABI = function(address, codeABI) {
        let e = sunSwapEx.e

        if (typeof(address) == "undefined" || typeof(codeABI) == "undefined") {
            throw "need address, codeABI"
        }

        return e.IO("abi", address, codeABI)
    }

    sunSwapEx.getCurrencyInfo = function(token0Address, token1Address) {
        let e = sunSwapEx.e

        let arrTokenAddress = [token0Address, token1Address]
        let tokenInfoCallData = []
        let tokenInfoRetType = []
        for (let tokenAddress of arrTokenAddress) {
            tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "symbol")])
            tokenInfoRetType.push("string")
            tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "name")])
            tokenInfoRetType.push("string")
            tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "decimals")])
            tokenInfoRetType.push("uint8")
        }
        let currencyInfo = _C(multicall, e, multicallContractAddress, tokenInfoCallData, tokenInfoRetType)
        if (currencyInfo.length != 6) {
            Log("invalid currency Info:", currencyInfo)
            return null
        }
        let ret = {
            "token0Symbol": currencyInfo[0],
            "token0Name": currencyInfo[1],
            "token0Decimals": currencyInfo[2],
            "token1Symbol": currencyInfo[3],
            "token1Name": currencyInfo[4],
            "token1Decimals": currencyInfo[5]
        }

        return ret
    }

    sunSwapEx.waitMined = function(tx) {
        let e = sunSwapEx.e        
        let i = 0
        let maxLoop = 10

        while (true) {
            Sleep(3000)

            let info = e.IO("api", "tron", "GetTransactionInfoByID", tx)
            if (info &amp;amp;&amp;amp; info.receipt &amp;amp;&amp;amp; typeof(info.receipt.result) == "number") {
                Log("GetTransactionInfoByID:", info)
                if (info.receipt.result == 1) {
                    return true
                } else {                
                    return false 
                }            
            }

            Log("Transaction not yet mined", tx)
            if (i &amp;gt; maxLoop) {
                break
            }
            i++
        }

        Log(`Transaction: ${tx} not found`)
        return false 
    }

    sunSwapEx.getTokenInfo = function(tokenSymbol) {
        let markets = sunSwapEx.markets
        if (!markets || isEmptyObject(markets)) {
            markets = sunSwapEx.GetMarkets()
        }

        if (tokenSymbol == "TRX") {
            return {"tokenSymbol": tokenSymbol, "tokenName": tokenSymbol, "tokenDecimals": 6, "tokenAddress": "--"}
        }

        for (let currency in markets) {
            for (let market of markets[currency]) {
                if (market["BaseAsset"] == tokenSymbol) {
                    return {"tokenSymbol": market["BaseAsset"], "tokenName": market["BaseName"], "tokenDecimals": market["BaseDecimals"], "tokenAddress": market["BaseAddress"]}
                } else if (market["QuoteAsset"] == tokenSymbol) {
                    return {"tokenSymbol": market["QuoteAsset"], "tokenName": market["QuoteName"], "tokenDecimals": market["QuoteDecimals"], "tokenAddress": market["QuoteAddress"]}
                }
            }
        }

        Log("not found token symbol:", tokenSymbol)
        return null
    }

    sunSwapEx.dealStakeTRX = function(methodName, resourceName, amount) {
        let e = sunSwapEx.e

        let walletAddress = e.IO("address")
        let balance = toInnerAmount(amount, 6)
        let resourceCode = -1

        if (resourceName == "BANDWIDTH") {
            resourceCode = 0
        } else if (resourceName == "ENERGY") {
            resourceCode = 1
        } else if (resourceName == "TRON_POWER") {
            resourceCode = 2
        } else {
            Log("not support resourceName:", resourceName)
            return null 
        }

        return e.IO("api", "tron", methodName, walletAddress, resourceCode, balance)
    }

    sunSwapEx.GetMarkets = function() {
        // sunswap v3 pool

        let e = sunSwapEx.e

        if (!sunSwapEx.markets) {
            sunSwapEx.markets = {}
        }

        let markets = sunSwapEx.markets
        if (!isEmptyObject(markets)) {
            return markets
        }

        let factoryV3Address = sunSwapEx.factoryV3Address

        // allPoolsLength
        let poolIdx = e.IO("api", factoryV3Address, "allPoolsLength")
        if (!poolIdx) {
            Log("invalid poolIdx:", poolIdx)
            return null 
        }
        Log("allPoolsLength:", poolIdx)

        // multicallContractAddress
        let multicallContractAddress = sunSwapEx.multicallContractAddress

        // get All pool address
        let retPools = []
        let getPoolsData = []
        let retPoolsType = []
        for (let i = 0; i &amp;lt; poolIdx; i++) {
            getPoolsData.push([factoryV3Address, "0x" + e.IO("encode", factoryV3Address, "allPools", String(i))])
            retPoolsType.push("address")

            if (getPoolsData.length &amp;gt; 30 || i == poolIdx - 1) {
                let arr = _C(multicall, e, multicallContractAddress, getPoolsData, retPoolsType)

                retPools.push(...arr)
                getPoolsData = []
                retPoolsType = []
            } 
        }

        // allPools
        let poolABI = sunSwapEx.poolABI
        for (let i in retPools) {
            // poolAddress
            let poolAddress = ethAddressToTron(retPools[i])

            // register pool ABI
            sunSwapEx.registerABI(poolAddress, poolABI)

            // get token address of the pool
            let tokenCallData = [[poolAddress, "0x" + e.IO("encode", poolAddress, "token0")], [poolAddress, "0x" + e.IO("encode", poolAddress, "token1")]]
            let tokenRetType = ["address", "address"]
            let arrTokenAddress = _C(multicall, e, multicallContractAddress, tokenCallData, tokenRetType)

            let token0Address = ethAddressToTron(arrTokenAddress[0])
            let token1Address = ethAddressToTron(arrTokenAddress[1])

            // symbol , name , decimals
            let currencyInfo = sunSwapEx.getCurrencyInfo(token0Address, token1Address)
            if (!currencyInfo) {
                return null
            }

            let token0Symbol = currencyInfo["token0Symbol"]
            let token0Name = currencyInfo["token0Name"]
            let token0Decimals = currencyInfo["token0Decimals"]
            let token1Symbol = currencyInfo["token1Symbol"]
            let token1Name = currencyInfo["token1Name"]
            let token1Decimals = currencyInfo["token1Decimals"]

            // Alias
            let mapAlias = {
                "TJWm3jWaJeCdyRckEXfopsJBvZi6wXVK2p": "PAPA",
                "TPYmHEhy5n8TCEfYGqW2rPxsghSfzghPDn": "USDDOLD",
                "TTiwtPv4MFHMoCpGDBEF85qQjD2v4e99ck": "HOUSDOLD",
                "TRFe3hT5oYhjSZ6f3ji5FJ7YCfrkWnHRvh": "ETHB"
            }
            if (typeof(mapAlias[token0Address]) != "undefined") {
                token0Symbol = mapAlias[token0Address]
            }
            if (typeof(mapAlias[token1Address]) != "undefined") {
                token1Symbol = mapAlias[token1Address]
            }

            // bad
            let mapBad = {
                "TMEmRKPob42vhkqEGopCnfBPixN5XTkdUc": "U S D T",      // Attention: Fishing contract
                "TXioUZK8ju3R54KvzSqPc6Ufq5wkxFqpq9": "U S D T",      // Attention: Fishing contract
                "TU41FWQQT8hZ6t72gVEjtSGn1Dshbevi7g": "CNY Coin",     // Attention: Invalid
                "TYB7oXNq4VyuiH5BHb4os8rTh7Ca8pxqSx": "BULL",         // Attention: Fishing contract
                "TYJxozCVMUiHg3TbQp9PKeuXdTsX9ugJz9": "SHISHA",       // Attention: Not circulated
                "TF9io9LGyjuK3uTpr73pAaQ5m9scxd9xvr": "TestToken18",  // Attention: Test trading pairs
                "TK8E3sFhBt3EB6gTT6d6co8RMB6DFUnNwE": "TestToken6"    // Attention: Test trading pairs
            }
            if (typeof(mapBad[token0Address]) != "undefined" || typeof(mapBad[token1Address]) != "undefined") {
                continue
            }

            // market
            let currency = token0Symbol + "_" + token1Symbol
            let market = {
                "Symbol": currency,
                "BaseAsset": token0Symbol,
                "QuoteAsset": token1Symbol,
                "BaseName": token0Name,
                "QuoteName": token1Name,
                "BaseDecimals": token0Decimals,
                "QuoteDecimals": token1Decimals,
                "BaseAddress": token0Address,
                "QuoteAddress": token1Address,
                "PoolAddress": poolAddress,
                "PoolIndex": i
            }

            if (!Array.isArray(markets[currency])) {
                markets[currency] = []
            }
            markets[currency].push(market)
            LogStatus(_D(), "get markets, length:", Object.keys(markets).length)

            Sleep(200)
        }

        _G("sunswap_markets", markets)
        sunSwapEx.markets = markets

        return markets
    }

    sunSwapEx.GetTicker = function(symbol, targetPoolAddress) {
        let e = sunSwapEx.e

        let markets = sunSwapEx.markets
        if (!markets || isEmptyObject(markets)) {
            markets = sunSwapEx.GetMarkets()
        }

        let arrPoolAddress = []
        let arrToken0Decimals = []
        let arrToken1Decimals = []
        for (let currency in markets) {        
            if (!Array.isArray(markets[currency]) || markets[currency].length == 0) {
                continue 
            }

            if (currency == symbol) {
                for (let ele of markets[currency]) {
                    if (typeof(targetPoolAddress) != "undefined" &amp;amp;&amp;amp; ele["PoolAddress"] != targetPoolAddress) {
                        continue 
                    }
                    arrPoolAddress.push(ele["PoolAddress"])
                    arrToken0Decimals.push(ele["BaseDecimals"])
                    arrToken1Decimals.push(ele["QuoteDecimals"])
                }
            }
        }

        if (arrPoolAddress.length == 0) {
            Log(`${symbol} and ${targetPoolAddress} not found`)
            sunSwapEx.markets = {}
            return null
        }

        let arrPrice = []
        let slot0CallData = []
        let slot0RetType = []
        for (let i in arrPoolAddress) {
            let poolAddress = arrPoolAddress[i]
            let poolABI = sunSwapEx.poolABI
            e.IO("abi", poolAddress, poolABI)
            slot0CallData.push([poolAddress, "0x" + e.IO("encode", poolAddress, "slot0")])
            slot0RetType.push("(uint160,int24,uint16,uint16,uint16,uint8,bool)")
        }
        let multicallContractAddress = sunSwapEx.multicallContractAddress
        let arrSlot0 = _C(multicall, e, multicallContractAddress, slot0CallData, slot0RetType)

        for (let i in arrSlot0) {
            let slot0 = arrSlot0[i]
            let token0Decimals = arrToken0Decimals[i]
            let token1Decimals = arrToken1Decimals[i]
            let sqrtPriceX96 = slot0["Field1"]
            let poolAddress = arrPoolAddress[i]
            let price = computePoolPrice(token0Decimals, token1Decimals, sqrtPriceX96)

            arrPrice.push({"price": price, "poolAddress": poolAddress})
        }

        return arrPrice
    }

    sunSwapEx.GetSwapOutAmount = function(symbol, amountIn, type) {
        let arrCurrency = symbol.split("_")
        if (arrCurrency.length != 2) {
            Log("invalid symbol:", symbol)
            return null
        }

        let baseCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[0])
        let quoteCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[1])
        if (!baseCurrencyInfo || !quoteCurrencyInfo) {
            return null 
        }

        let baseSymbol = baseCurrencyInfo["tokenSymbol"]
        let baseAddress = baseCurrencyInfo["tokenAddress"]
        let baseDecimals = baseCurrencyInfo["tokenDecimals"]
        let quoteSymbol = quoteCurrencyInfo["tokenSymbol"]
        let quoteAddress = quoteCurrencyInfo["tokenAddress"]
        let quoteDecimals = quoteCurrencyInfo["tokenDecimals"]

        // black hole address
        if (baseSymbol == "TRX") {
            baseAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"
        }
        if (quoteSymbol == "TRX") {
            quoteAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"
        }

        let query = null
        if (type == "buy") {
            let amount = toInnerAmount(amountIn, quoteDecimals)
            query = `?fromToken=${quoteAddress}&amp;amp;toToken=${baseAddress}&amp;amp;amountIn=${amount}&amp;amp;typeList=WTRX,SUNSWAP_V1,SUNSWAP_V2,SUNSWAP_V3`
        } else if (type == "sell") {
            let amount = toInnerAmount(amountIn, baseDecimals)
            query = `?fromToken=${baseAddress}&amp;amp;toToken=${quoteAddress}&amp;amp;amountIn=${amount}&amp;amp;typeList=WTRX,SUNSWAP_V1,SUNSWAP_V2,SUNSWAP_V3`
        } else {
            Log("not support type:", type)
            return null 
        }

        try {
            let ret = JSON.parse(HttpQuery("https://rot.endjgfsv.link/swap/router" + query))
            Log("https://rot.endjgfsv.link/swap/router" + query, "GetSwapOutAmount ret:", ret)

            if (!ret || ret["message"] != "SUCCESS") {
                Log("invalid data:", ret)
                return null
            }

            let outAmount = null
            let best = null 
            let info = ret["data"]
            for (let ele of info) {
                if (!outAmount) {
                    outAmount = parseFloat(ele["amountOut"])
                    best = ele
                } else {
                    let amount = parseFloat(ele["amountOut"])
                    if (amount &amp;gt; outAmount) {
                        outAmount = amount
                        best = ele
                    }
                }
            }

            if (!outAmount || !best) {
                Log("info:", info)
                return null 
            }

            return {"info": info, "outAmount": outAmount, "best": best, "baseCurrencyInfo": baseCurrencyInfo, "quoteCurrencyInfo": quoteCurrencyInfo}
        } catch(e) {
            Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
            return null
        }
    }

    sunSwapEx.GetAccount = function() {
        let e = sunSwapEx.e

        let walletAddress = e.IO("address")
        let account = e.IO("api", "tron", "GetAccount", walletAddress)
        if (!account) {
            Log("get account failed, account:", account)
            return null 
        }

        let trxBalance = toAmount(account["balance"], 6)

        let resource = e.IO("api", "tron", "GetAccountResource", walletAddress)
        if (!resource) {
            Log("get resource failed, resource:", resource)
            return null 
        }

        let energyLimit = resource["EnergyLimit"] ? resource["EnergyLimit"] : 0
        let freeNetLimit = resource["freeNetLimit"] ? resource["freeNetLimit"] : 0

        return {"Info": {"GetAccount": account, "GetAccountResource": resource}, "TRX": trxBalance, "Bandwidth": freeNetLimit, "Energy": energyLimit}
    }

    sunSwapEx.GetAssets = function() {
        let e = sunSwapEx.e
        let walletAddress = e.IO("address")

        try {
            let ret = JSON.parse(HttpQuery("https://apilist.tronscanapi.com/api/account/token_asset_overview?address=" + walletAddress))

            let assets = []
            for (let info of ret["data"]) {
                let decimals = parseInt(info["tokenDecimal"])
                let asset = {
                    "Currency": info["tokenAbbr"] == "trx" ? "TRX" : info["tokenAbbr"],
                    "Decimals": decimals,
                    "Amount": toAmount(info["balance"], decimals),
                    "Address": info["tokenId"],
                }

                assets.push(asset)
            }

            return assets
        } catch(e) {
            Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
            return null
        }
    }

    sunSwapEx.StakeTRX = function(resourceName, frozenBalance) {
        return sunSwapEx.dealStakeTRX("FreezeBalanceV2", resourceName, frozenBalance)
    }

    sunSwapEx.UnStakeTRX = function(resourceName, unfreezeBalance) {
        return sunSwapEx.dealStakeTRX("UnfreezeBalanceV2", resourceName, unfreezeBalance)
    }

    sunSwapEx.WrappedTRX = function(actionType, amountIn) {
        let e = sunSwapEx.e

        let tx = null
        if (actionType == "deposit") {
            let amount = toInnerAmount(amountIn, 6)
            tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "deposit", amount, {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
        } else if (actionType == "withdraw") {
            tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "withdraw(uint256)", toInnerAmount(amountIn, 6), {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
        } else {
            Log("not support actionType:", actionType)
            return false 
        }

        if (tx) {
            Log("tx: ", tx)
            let txRet = sunSwapEx.waitMined(tx)
            if (!txRet) {
                Log(actionType, "failed")
                return false
            } else {
                Log(actionType, "success")
                return true
            }
        } else {
            Log("trans error")
            return false
        }
    }

    sunSwapEx.Swap = function(symbol, amountIn, type) {
        let e = sunSwapEx.e
        let smartRouterAddress = sunSwapEx.smartRouterAddress

        if (symbol == "TRX_WTRX" || symbol == "WTRX_TRX") {
            let actionType = null 
            if (type == "buy") {
                actionType = symbol == "TRX_WTRX" ? "withdraw" : "deposit"
            } else if (type == "sell") {
                actionType = symbol == "TRX_WTRX" ? "deposit" : "withdraw"
            } else {
                Log("invalid type:", type)
                return false 
            }

            return sunSwapEx.WrappedTRX(actionType, amountIn)
        }

        let swapInfo = sunSwapEx.GetSwapOutAmount(symbol, amountIn, type)
        if (!swapInfo || !swapInfo["best"]) {
            Log("invalid swapInfo:", swapInfo)
            return false
        }

        let outAmount = swapInfo["outAmount"]
        let best = swapInfo["best"]

        // path
        let path = best["tokens"]
        if (!path || path.length &amp;lt; 2) {
            Log("invalid path:", path)
            return false 
        }

        // poolVersions and versionsLen
        let poolVersions = [] 
        let versionsLen = []
        for (var v of best["poolVersions"]) {
            if (poolVersions.length == 0) {
                poolVersions.push(v)
                versionsLen.push(2)
            } else {
                if (poolVersions[poolVersions.length - 1] == v) {
                    versionsLen[versionsLen.length - 1] += 1
                } else {
                    poolVersions.push(v)
                    versionsLen.push(1)
                }
            }
        }

        // fees 
        let poolFees = best["poolFees"]

        // get decimals , token name 
        let token0Decimals = swapInfo["baseCurrencyInfo"]["tokenDecimals"]
        let token1Decimals = swapInfo["quoteCurrencyInfo"]["tokenDecimals"]

        let tokenInName = type == "buy" ? swapInfo["quoteCurrencyInfo"]["tokenSymbol"] : swapInfo["baseCurrencyInfo"]["tokenSymbol"]
        let tokenOutName = type == "buy" ? swapInfo["baseCurrencyInfo"]["tokenSymbol"] : swapInfo["quoteCurrencyInfo"]["tokenSymbol"]
        let tokenInAddress = type == "buy" ? swapInfo["quoteCurrencyInfo"]["tokenAddress"] : swapInfo["baseCurrencyInfo"]["tokenAddress"]
        let tokenOutAddress = type == "buy" ? swapInfo["baseCurrencyInfo"]["tokenAddress"] : swapInfo["quoteCurrencyInfo"]["tokenAddress"]
        let tokenInDecimals = type == "buy" ? token1Decimals : token0Decimals

        // calc amount
        let amount = null 
        let minAmount = null 
        if (type == "buy") {
            amount = toInnerAmount(amountIn, token1Decimals)
            minAmount = toInnerAmount(outAmount * 0.99, token0Decimals)
        } else if (type == "sell") {
            amount = toInnerAmount(amountIn, token0Decimals)
            minAmount = toInnerAmount(outAmount * 0.99, token1Decimals)
        } else {
            Log("invalid type:", type)
            return false 
        }

        // wallet address 
        let walletAddress = e.IO("address")

        // expired timestamp
        let expiredTS = parseInt((new Date().getTime() + 1000 * 60 * 5) / 1000)

        // [amount of the token to be swapped, minimum acceptable amount of the token obtained from the swap, address to receive the token obtained from the swap, deadline]
        let data = [String(amount), String(minAmount), walletAddress, expiredTS]

        // allowance
        if (tokenInName != "TRX" &amp;amp;&amp;amp; !(tokenInName == "WTRX" &amp;amp;&amp;amp; tokenOutName == "TRX")) {
            let allowanceAmount = e.IO("api", tokenInAddress, "allowance", walletAddress, smartRouterAddress)
            let realAmount = toAmount(allowanceAmount, tokenInDecimals)
            if (realAmount &amp;lt; amountIn) {
                // approve
                Log("realAmount is", realAmount, "too small, try to approve large amount")
                let txApprove = e.IO("api", tokenInAddress, "approve(address,uint256)", smartRouterAddress, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
                if (!txApprove) {
                    throw "approve error"
                }

                let txRet = sunSwapEx.waitMined(txApprove)
                if (!txRet) {
                    Log("approve failed")
                    return false 
                } else {
                    Log("approve success")
                }
            } else {
                Log("allowance", realAmount, "no need to approve")
            }
        }

        // swap
        Log(`path:${path}, poolVersions:${poolVersions}, versionsLen:${versionsLen}, poolFees:${poolFees}, data:${data}`)
        Log("best swap:", best)
        let tx = e.IO("api", smartRouterAddress, "swapExactInput(address[],string[],uint256[],uint24[],(uint256,uint256,address,uint256))", tokenInName == "TRX" ? toInnerAmount(amountIn, 6) : 0, path, poolVersions, versionsLen, poolFees, data, {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
        if (tx) {
            Log("tx: ", tx)
            let txRet = sunSwapEx.waitMined(tx)
            if (!txRet) {
                Log("swap", tokenInName, "to", tokenOutName, "failed")
                return false
            } else {
                Log("swap", tokenInName, "to", tokenOutName, "success")
                return true
            }            
        } else {
            Log("trans error")
            return false
        }
    }

    sunSwapEx.SendTRX = function(to, amount) {
        let e = sunSwapEx.e
        return e.IO("api", "tron", "send", to, toInnerAmount(amount, 6))
    }

    sunSwapEx.SetFeeLimit = function(feeLimit) {
        sunSwapEx.feeLimit = feeLimit
        Log("SetFeeLimit, feeLimit:", sunSwapEx.feeLimit, ", toInnerAmount:", toInnerAmount(sunSwapEx.feeLimit, 6))        
    }

    // init
    if (typeof(e) == "undefined") {
        e = exchange
        Log("By default, the exchange configuration is used")
    }
    sunSwapEx.e = e

    // tron
    let ret = e.IO("api", "tron", "GetNodeInfo")
    if (!ret) {
        throw "The current Web3 tron ​​exchange object may be misconfigured"
    } else {
        Log("node information:", ret)
    }

    // feeLimit
    sunSwapEx.feeLimit = 50

    // register abi
    let factoryV3Address = "TThJt8zaJzJMhCEScH7zWKnp5buVZqys9x"
    sunSwapEx.factoryV3Address = factoryV3Address

    let poolABI = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"}],"name":"Burn","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":false,"name":"recipient","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount0","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount1","internalType":"uint128","type":"uint128"}],"name":"Collect","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount1","internalType":"uint128","type":"uint128"}],"name":"CollectProtocol","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"paid0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"paid1","internalType":"uint256","type":"uint256"}],"name":"Flash","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"observationCardinalityNextOld","internalType":"uint16","type":"uint16"},{"indexed":false,"name":"observationCardinalityNextNew","internalType":"uint16","type":"uint16"}],"name":"IncreaseObservationCardinalityNext","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"indexed":false,"name":"tick","internalType":"int24","type":"int24"}],"name":"Initialize","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"}],"name":"Mint","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"feeProtocol0Old","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol1Old","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol0New","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol1New","internalType":"uint8","type":"uint8"}],"name":"SetFeeProtocol","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"int256","type":"int256"},{"indexed":false,"name":"amount1","internalType":"int256","type":"int256"},{"indexed":false,"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"indexed":false,"name":"liquidity","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"tick","internalType":"int24","type":"int24"}],"name":"Swap","anonymous":false,"type":"event"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"factory","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint24","type":"uint24"}],"inputs":[],"name":"fee","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[],"name":"feeGrowthGlobal0X128","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[],"name":"feeGrowthGlobal1X128","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"liquidity","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"maxLiquidityPerTick","stateMutability":"view","type":"function"},{"outputs":[{"name":"blockTimestamp","internalType":"uint32","type":"uint32"},{"name":"tickCumulative","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityCumulativeX128","internalType":"uint160","type":"uint160"},{"name":"initialized","internalType":"bool","type":"bool"}],"inputs":[{"name":"","internalType":"uint256","type":"uint256"}],"name":"observations","stateMutability":"view","type":"function"},{"outputs":[{"name":"liquidity","internalType":"uint128","type":"uint128"},{"name":"feeGrowthInside0LastX128","internalType":"uint256","type":"uint256"},{"name":"feeGrowthInside1LastX128","internalType":"uint256","type":"uint256"},{"name":"tokensOwed0","internalType":"uint128","type":"uint128"},{"name":"tokensOwed1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"","internalType":"bytes32","type":"bytes32"}],"name":"positions","stateMutability":"view","type":"function"},{"outputs":[{"name":"token0","internalType":"uint128","type":"uint128"},{"name":"token1","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"protocolFees","stateMutability":"view","type":"function"},{"outputs":[{"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"name":"tick","internalType":"int24","type":"int24"},{"name":"observationIndex","internalType":"uint16","type":"uint16"},{"name":"observationCardinality","internalType":"uint16","type":"uint16"},{"name":"observationCardinalityNext","internalType":"uint16","type":"uint16"},{"name":"feeProtocol","internalType":"uint8","type":"uint8"},{"name":"unlocked","internalType":"bool","type":"bool"}],"inputs":[],"name":"slot0","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"","internalType":"int16","type":"int16"}],"name":"tickBitmap","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"int24","type":"int24"}],"inputs":[],"name":"tickSpacing","stateMutability":"view","type":"function"},{"outputs":[{"name":"liquidityGross","internalType":"uint128","type":"uint128"},{"name":"liquidityNet","internalType":"int128","type":"int128"},{"name":"feeGrowthOutside0X128","internalType":"uint256","type":"uint256"},{"name":"feeGrowthOutside1X128","internalType":"uint256","type":"uint256"},{"name":"tickCumulativeOutside","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityOutsideX128","internalType":"uint160","type":"uint160"},{"name":"secondsOutside","internalType":"uint32","type":"uint32"},{"name":"initialized","internalType":"bool","type":"bool"}],"inputs":[{"name":"","internalType":"int24","type":"int24"}],"name":"ticks","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"token0","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"token1","stateMutability":"view","type":"function"},{"outputs":[{"name":"tickCumulativeInside","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityInsideX128","internalType":"uint160","type":"uint160"},{"name":"secondsInside","internalType":"uint32","type":"uint32"}],"inputs":[{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"}],"name":"snapshotCumulativesInside","stateMutability":"view","type":"function"},{"outputs":[{"name":"tickCumulatives","internalType":"int56[]","type":"int56[]"},{"name":"secondsPerLiquidityCumulativeX128s","internalType":"uint160[]","type":"uint160[]"}],"inputs":[{"name":"secondsAgos","internalType":"uint32[]","type":"uint32[]"}],"name":"observe","stateMutability":"view","type":"function"},{"outputs":[],"inputs":[{"name":"observationCardinalityNext","internalType":"uint16","type":"uint16"}],"name":"increaseObservationCardinalityNext","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"}],"name":"initialize","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount","internalType":"uint128","type":"uint128"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"mint","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint128","type":"uint128"},{"name":"amount1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount0Requested","internalType":"uint128","type":"uint128"},{"name":"amount1Requested","internalType":"uint128","type":"uint128"}],"name":"collect","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount","internalType":"uint128","type":"uint128"}],"name":"burn","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"int256","type":"int256"},{"name":"amount1","internalType":"int256","type":"int256"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"zeroForOne","internalType":"bool","type":"bool"},{"name":"amountSpecified","internalType":"int256","type":"int256"},{"name":"sqrtPriceLimitX96","internalType":"uint160","type":"uint160"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"swap","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"flash","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"feeProtocol0","internalType":"uint8","type":"uint8"},{"name":"feeProtocol1","internalType":"uint8","type":"uint8"}],"name":"setFeeProtocol","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint128","type":"uint128"},{"name":"amount1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"amount0Requested","internalType":"uint128","type":"uint128"},{"name":"amount1Requested","internalType":"uint128","type":"uint128"}],"name":"collectProtocol","stateMutability":"nonpayable","type":"function"}]`
    sunSwapEx.poolABI = poolABI

    let multicallContractAddress = "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98"
    sunSwapEx.multicallContractAddress = multicallContractAddress
    let multicallContractABI = `[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct TronMulticall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBasefee","outputs":[{"internalType":"uint256","name":"basefee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"trcToken","name":"id","type":"trcToken"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isContract","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"view","type":"function"}]`
    sunSwapEx.registerABI(multicallContractAddress, multicallContractABI)

    let smartRouterAddress = "TCFNp179Lg46D16zKoumd4Poa2WFFdtqYj"
    sunSwapEx.smartRouterAddress = smartRouterAddress
    let smartRouterABI = `[{"outputs":[{"name":"amountsOut","type":"uint256[]"}],"inputs":[{"name":"path","type":"address[]"},{"name":"poolVersion","type":"string[]"},{"name":"versionLen","type":"uint256[]"},{"name":"fees","type":"uint24[]"},{"name":"data","type":"tuple"}],"name":"swapExactInput","stateMutability":"payable","type":"function","payable":true}]`
    sunSwapEx.registerABI(smartRouterAddress, smartRouterABI)

    // get sunswap_markets
    sunSwapEx.markets = _G("sunswap_markets")

    return sunSwapEx
}

// for test
function main() {
    // reset log
    LogReset(1)
    // reset _G()
    // _G(null)

    // Create SunswapEx 
    let e = $.CreateSunSwapEx()
    // let e = $.CreateSunSwapEx(exchanges[1])

    // /* GetMarkets
    let markets = e.GetMarkets()
    let idx = 0
    let tbl = {"type": "table", "title": "test GetMarkets", "cols": ["Index", "PoolAddress", "Symbol", "BaseAsset", "QuoteAsset", "BaseName", "QuoteName", "BaseDecimals", "QuoteDecimals", "BaseAddress", "QuoteAddress"], "rows": []}
    for (let currency in markets) {
        let arrMarket = markets[currency]
        for (let market of arrMarket) {
            tbl["rows"].push([idx, market["PoolAddress"], market["Symbol"], market["BaseAsset"], market["QuoteAsset"], market["BaseName"], market["QuoteName"], market["BaseDecimals"], market["QuoteDecimals"], market["BaseAddress"], market["QuoteAddress"]])    
            idx++
        }
    }
    LogStatus("`" + JSON.stringify(tbl) + "`")
    // */

    /* GetTicker    
    let ticker1 = e.GetTicker("WTRX_USDT")
    Log("symbol:", "WTRX_USDT", ", ticker:", ticker1)
    let ticker2 = e.GetTicker("iCMX_USDT", "TLVDozYEBgeaJXH7oKBsougzEJKNrogFun")  // iCMX_USDT
    Log("symbol:", "iCMX_USDT", ", ticker:", ticker2)
    */

    /* multicall  
    let data = []
    let walletAddress = exchange.IO("address")
    data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)])    
    data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)])    
    Log(data)
    let ret = multicall(exchange, "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", data, ["uint256", "uint256"])
    Log(ret, toAmount(ret[0], 6))
    */

    /* GetSwapOutAmount
    let retBuy = e.GetSwapOutAmount("WTRX_USDT", 1, "buy")     // 1 quote -&amp;gt; base
    Log("WTRX_USDT", "buy outAmount:", retBuy["outAmount"], ", ask:", (1 / retBuy["outAmount"]))
    let retSell = e.GetSwapOutAmount("WTRX_USDT", 1, "sell")   // 1 base -&amp;gt; quote
    Log("WTRX_USDT", "sell outAmount:", retSell["outAmount"], ", bid:", (retSell["outAmount"] / 1))
    */

    /* GetAssets
    let assets = e.GetAssets()
    let tbl = {"type": "table", "title": "test GetAssets", "cols": ["Name", "Address", "Decimals", "Balance"], "rows": []}   
    for (let asset of assets) {
        tbl["rows"].push([asset.Currency, asset.Address, asset.Decimals, asset.Amount])
    }
    LogStatus("`" + JSON.stringify(tbl) + "`")
    */

    /* Swap
    // let ret = e.Swap("WTRX_USDT", 6, "buy")
    // let ret = e.Swap("WTRX_TUSD", 6, "buy")
    // let ret = e.Swap("WTRX_TUSD", 10, "sell")
    // let ret = e.Swap("TRX_WTRX", 10, "sell")
    // let ret = e.Swap("TRX_WTRX", 10, "buy")
    // let ret = e.Swap("TUSD_TRX", 0.3, "sell")
    // let ret = e.Swap("WTRX_USDT", 20, "sell")
    // let ret = e.Swap("USDT_TRX", 6, "sell")
    Log(ret)
    */

    /* check token
    let mapFilter = {}
    let checkMarkets = e.GetMarkets()
    for (let currency in checkMarkets) {
        for (let market of checkMarkets[currency]) {
            let base = market["BaseAsset"]
            let quote = market["QuoteAsset"]
            if (typeof(mapFilter[base]) == "undefined") {
                mapFilter[base] = market["BaseAddress"]
            }
            if (typeof(mapFilter[quote]) == "undefined") {
                mapFilter[quote] = market["QuoteAddress"]
            }

            if (market["BaseAddress"] != mapFilter[base]) {
                Log(market["BaseAsset"], market["BaseAddress"], " --- ", base, mapFilter[base])
            }
            if (market["QuoteAddress"] != mapFilter[quote]) {
                Log(market["QuoteAsset"], market["QuoteAddress"], " --- ", quote, mapFilter[quote])
            }
        }
    }
    */

    /* GetAccount
    let account = e.GetAccount()
    Log(account)
    */

    /* StakeTRX
    // BANDWIDTH, ENERGY, TRON_POWER
    let retStakeTRX = e.StakeTRX("ENERGY", 10)         // Stake TRX
    Log("retStakeTRX:", retStakeTRX)
    // let retUnStakeTRX = e.UnStakeTRX("ENERGY", 100) // UnStake TRX
    // Log("retUnStakeTRX:", retUnStakeTRX)
    */

    /* send
    // let ret = e.SendTRX("...", 180)
    // Log("SendTRX ret:", ret)
    */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  END
&lt;/h2&gt;

&lt;p&gt;Through this article, we have realized the Web3 Tron package based on the FMZ Quant platform, and connected to SunSwap DEX successfully, completing the complete process from querying the trading pool to executing the exchange and parsing the transaction results. This not only provides an efficient automation solution for DEX transactions in the Tron ecosystem, but also demonstrates the powerful scalability of the FMZ Quant platform.&lt;/p&gt;

&lt;p&gt;I hope that the practical experience in this article can provide valuable reference for your quantitative trading system. Next, I will study the DEX on SOL.&lt;br&gt;
Thank you for reading~&lt;/p&gt;

</description>
      <category>dex</category>
      <category>web3</category>
      <category>quant</category>
      <category>code</category>
    </item>
    <item>
      <title>Polymarket Binary Hedging Arbitrage: From Concept to Live Execution</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Wed, 04 Mar 2026 05:17:32 +0000</pubDate>
      <link>https://forem.com/quant001/polymarket-binary-hedging-arbitrage-from-concept-to-live-execution-2do6</link>
      <guid>https://forem.com/quant001/polymarket-binary-hedging-arbitrage-from-concept-to-live-execution-2do6</guid>
      <description>&lt;h2&gt;
  
  
  I. The Binary Market Structure of Polymarket
&lt;/h2&gt;

&lt;p&gt;Polymarket is a prediction market where each question has only two possible outcomes — Yes or No, Up or Down, Win or Lose.&lt;/p&gt;

&lt;p&gt;Take the BTC 15-minute price contract as an example. In each round, there are two contracts: Up and Down. The bet is whether BTC's closing price after 15 minutes will be higher or lower than the opening price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpny8ul4c1e1hu2b2hms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpny8ul4c1e1hu2b2hms.png" alt=" " width="418" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your side is correct → it pays $1&lt;/li&gt;
&lt;li&gt;If your side is wrong → it pays $0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This market has an extremely special property:&lt;/p&gt;

&lt;p&gt;The probabilities of the two outcomes must sum to 100%. Therefore, the price of Up plus the price of Down should theoretically equal exactly 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueyabpts0aihe3l2lvw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueyabpts0aihe3l2lvw8.png" alt=" " width="530" height="64"&gt;&lt;/a&gt;&lt;br&gt;
This property does not exist in ordinary futures markets. It provides us with a natural deterministic anchor.&lt;/p&gt;
&lt;h2&gt;
  
  
  II. Core Arbitrage Logic: Finding Certainty in Uncertainty
&lt;/h2&gt;

&lt;p&gt;Quantitative trading essentially does one thing: find relatively certain opportunities in an uncertain market.&lt;/p&gt;

&lt;p&gt;The binary structure of Polymarket provides such an anchor:&lt;/p&gt;

&lt;p&gt;No matter whether BTC goes up or down during these 15 minutes, either Up or Down must settle at $1.&lt;/p&gt;

&lt;p&gt;Using this anchor, we do not need to predict direction. We only need to find a moment when the combined cost of buying Up and Down is less than 1. Once that condition is met, the profit is locked in at the moment of purchase.&lt;/p&gt;

&lt;p&gt;However, most of the time the market keeps the sum tightly around 1. Direct arbitrage opportunities are rare.&lt;/p&gt;

&lt;p&gt;So where do the opportunities come from?&lt;/p&gt;
&lt;h2&gt;
  
  
  III. The Opportunity Window: Price Imbalance During Market Overreaction
&lt;/h2&gt;

&lt;p&gt;Opportunities arise from price imbalance during market overreaction.&lt;/p&gt;

&lt;p&gt;Imagine this scenario:&lt;/p&gt;

&lt;p&gt;During a 15-minute round, BTC suddenly drops sharply. Panic spreads. The Up contract is heavily sold, and its price falls from 0.50 to 0.35.&lt;/p&gt;

&lt;p&gt;In theory, if Up drops by 0.15, Down should rise by 0.15 so that their sum remains 1. But markets do not react symmetrically in real time. Up may have already crashed to 0.35, while Down is still moving upward from 0.50 toward 0.65 and hasn't fully caught up.&lt;/p&gt;

&lt;p&gt;In that transitional moment, the sum may temporarily be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnrspx5sjkl1qy95nwve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnrspx5sjkl1qy95nwve.png" alt=" " width="344" height="43"&gt;&lt;/a&gt;&lt;br&gt;
This is the arbitrage window.&lt;/p&gt;

&lt;p&gt;You spend 0.93 to buy a combination that must pay out 1 dollar.&lt;br&gt;
The profit is already determined at the moment of purchase.&lt;/p&gt;
&lt;h2&gt;
  
  
  IV. The Original Idea: Four-Parameter Serial Version
&lt;/h2&gt;

&lt;p&gt;The original idea came from Twitter blogger @the_smart_ape.&lt;br&gt;
It is elegant and minimal, using only four parameters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vwkr5wep1krip13h4th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vwkr5wep1krip13h4th.png" alt=" " width="362" height="183"&gt;&lt;/a&gt;&lt;br&gt;
Execution logic:&lt;/p&gt;

&lt;p&gt;During the monitoring window at the start of each round, continuously track price.&lt;/p&gt;

&lt;p&gt;If one side drops more than the trigger threshold → buy it as Leg 1.&lt;/p&gt;

&lt;p&gt;Wait for the opposite side's price to fall back.&lt;/p&gt;

&lt;p&gt;When both legs' total price falls below the target → buy Leg 2.&lt;/p&gt;

&lt;p&gt;Hold until settlement and collect payout.&lt;/p&gt;

&lt;p&gt;The idea is clean and efficient. But in live trading, several problems appear.&lt;/p&gt;
&lt;h2&gt;
  
  
  V. Our Improvement: Proactively Locking Arbitrage Space
&lt;/h2&gt;

&lt;p&gt;The original idea is passive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buy Leg 1&lt;/li&gt;
&lt;li&gt;Wait for Leg 2 to become cheap enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our improvement is different:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At the moment of the dump, there is a high probability that Up and Down move at different speeds. We act simultaneously during this dislocation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Execution logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leg 1: Place a limit buy at dump price plus slippage.&lt;/li&gt;
&lt;li&gt;Leg 2: Reverse-calculate price using SUM_TARGET - leg1Price&lt;/li&gt;
&lt;li&gt;Submit both orders concurrently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the moment the orders are placed, the arbitrage space is already locked.&lt;br&gt;
We are no longer waiting for the market to meet our condition — we let the market come to us.&lt;/p&gt;

&lt;p&gt;Core implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function executeBothLegs(symbols, dumpSide, dumpAsk) {
    var leg1Symbol = (dumpSide === "Up") ? symbols.up   : symbols.down
    var leg2Symbol = (dumpSide === "Up") ? symbols.down : symbols.up

    leg1Price = _N(dumpAsk + SLIPPAGE, 4)
    leg2Price = _N(SUM_TARGET - leg1Price, 4)

    var goLeg1 = exchange.Go("CreateOrder", leg1Symbol, "buy", leg1Price, SHARES)
    var goLeg2 = exchange.Go("CreateOrder", leg2Symbol, "buy", leg2Price, SHARES)

    var id1 = goLeg1.wait()
    var id2 = goLeg2.wait()

    leg1OrderId = id1
    leg2OrderId = id2
    state = STATE.BOTH_PENDING
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VI. Problem One: Leg 1 Fills but Price Never Recovers
&lt;/h2&gt;

&lt;p&gt;Reality often looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up crashes&lt;/li&gt;
&lt;li&gt;You buy Leg 1&lt;/li&gt;
&lt;li&gt;Down never pulls back&lt;/li&gt;
&lt;li&gt;Market sentiment remains bearish
You are stuck holding a single-sided position that may go to zero at settlement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A stop-loss is mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Patch 1: Floor Price + Early Take Profit&lt;/strong&gt;&lt;br&gt;
We introduce two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FLOOR_PRICE: If price drops to a deep absolute level (e.g., 0.05), exit immediately.&lt;/li&gt;
&lt;li&gt;EARLY_TAKE_PROFIT: If Leg 1 rises enough before Leg 2 fills, sell and take profit early.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function handleLeg1OnlyRisk(symbols, upBid, downBid, isLastMin) {
    var holdBid    = (leg1Side === "Up") ? upBid : downBid
    var profitLine = leg1EntryAsk * (1 + EARLY_TAKE_PROFIT)
    var stopLine   = leg1EntryAsk * (1 - LAST_MIN_STOP_LOSS)

    var needClose = false
    var reason    = ""

    if (holdBid &amp;lt;= FLOOR_PRICE) {
        needClose = true; reason = "FLOOR_PRICE"
    } else if (!isLastMin &amp;amp;&amp;amp; holdBid &amp;gt;= profitLine) {
        needClose = true; reason = "EARLY_TAKE_PROFIT"
    } else if (isLastMin &amp;amp;&amp;amp; holdBid &amp;lt;= stopLine) {
        needClose = true; reason = "STOP_LOSS"
    }

    if (needClose) {
        cancelAndConfirmUntilClear(leg2OrderId)
        closePosition(holdSymbol, holdBid, reason)
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This caps downside and locks upside.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VII. Problem Two: Special Handling Near Settlement
Near settlement, market behavior changes dramatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There may not be enough time for mean reversion. Volatility often increases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Patch 2: Last-Minute Logic&lt;/strong&gt;&lt;br&gt;
We introduce LAST_MIN_S:&lt;/p&gt;

&lt;p&gt;When entering the final phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop attempting Leg 2&lt;/li&gt;
&lt;li&gt;Apply LAST_MIN_STOP_LOSS&lt;/li&gt;
&lt;li&gt;If not stopped out, hold to settlement
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var isLastMin = (remaining &amp;lt;= LAST_MIN_S)

if (isLastMin) {
    Log("⏰ In the last minute, cancel the pending orders that have not been executed")
    cancelAllPending("the last minute")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Early phase = wait for recovery&lt;br&gt;
Late phase = avoid going to zero&lt;/p&gt;
&lt;h2&gt;
  
  
  VIII. Problem Three: On-Chain Latency
&lt;/h2&gt;

&lt;p&gt;Polymarket is on-chain.&lt;/p&gt;

&lt;p&gt;Order confirmation and position updates may take several seconds.&lt;/p&gt;

&lt;p&gt;We add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order timeout detection&lt;/li&gt;
&lt;li&gt;Cancellation and retry mechanism&lt;/li&gt;
&lt;li&gt;Slippage parameter to increase fill probability
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function placeOrderAndConfirm(symbol, side, price, amount) {
    var orderId = exchange.CreateOrder(symbol, side, orderPrice, amount)

    var deadline = Date.now() + ORDER_TIMEOUT_S * 1000
    while (Date.now() &amp;lt; deadline) {
        var order = exchange.GetOrder(orderId)
        if (order &amp;amp;&amp;amp; order.Status === 1) {
            return { orderId: orderId, avgPrice: order.AvgPrice }  
        }
        if (order &amp;amp;&amp;amp; (order.Status === 2 || order.Status === 4)) {
            return { orderId: orderId, avgPrice: null }  
        }
        Sleep(1000)
    }

    exchange.CancelOrder(orderId)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This ensures execution reliability in a blockchain environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  IX. Redeem Mechanism
&lt;/h2&gt;

&lt;p&gt;Polymarket has a special feature:&lt;/p&gt;

&lt;p&gt;After settlement, funds are not automatically returned to balance.&lt;/p&gt;

&lt;p&gt;You must manually call the Redeem function to unlock capital.&lt;/p&gt;

&lt;p&gt;If forgotten, capital remains stuck.&lt;/p&gt;

&lt;p&gt;The strategy automatically calls Redeem each round to ensure capital recycling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function doRedeem() {
    var positions = exchange.GetPositions()
    for (var i = 0; i &amp;lt; positions.length; i++) {
        var pos = positions[i]
        if (pos.Info &amp;amp;&amp;amp; pos.Info.redeemable) {
            var result = exchange.IO("redeem", pos.Symbol, true)
            Log("Redeem :", result)
        }
    }
}

if (!redeemDone &amp;amp;&amp;amp; elapsed &amp;gt;= 840) {
    doRedeem()
    redeemDone = true
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  X. Real-Time Monitoring Dashboard
&lt;/h2&gt;

&lt;p&gt;The strategy includes a real-time monitoring panel displaying:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0zma41v0lup4g9h2oty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0zma41v0lup4g9h2oty.png" alt=" " width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account balance&lt;/li&gt;
&lt;li&gt;Strategy state&lt;/li&gt;
&lt;li&gt;Price monitoring&lt;/li&gt;
&lt;li&gt;Position details and floating P/L&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows full visibility during live trading.&lt;/p&gt;

&lt;h2&gt;
  
  
  XI. Live Trading Example &amp;amp; Strategy Limitations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3s4xf4g137wo08qpfcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3s4xf4g137wo08qpfcd.png" alt=" " width="800" height="344"&gt;&lt;/a&gt;&lt;br&gt;
Example:&lt;br&gt;
A new round begins. Down drops 18.6% from 0.43 to 0.35.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both legs are placed simultaneously:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Leg 1 Down at 0.37&lt;br&gt;
   Leg 2 Up at 0.60&lt;br&gt;
   Total = 0.97 (&amp;lt; target)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actual fill:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Leg 1 = 0.34&lt;br&gt;
   Leg 2 = 0.60&lt;br&gt;
   Total cost = 0.94&lt;/p&gt;

&lt;p&gt;Profit exceeded expectation.&lt;/p&gt;

&lt;p&gt;Three Honest Limitations&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No opportunity in calm markets. Arbitrage depends on overreaction. In stable markets, the strategy idles.&lt;/li&gt;
&lt;li&gt;Single-leg exposure risk. If Leg 2 never fills, Leg 1 may hit stop loss. Stop parameters are a trade-off.&lt;/li&gt;
&lt;li&gt;SUM_TARGET threshold trade-off. High threshold → frequent but small profits. Low threshold → rare but large profits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This defines the strategy's style.&lt;/p&gt;

&lt;h2&gt;
  
  
  XII. Future Optimization Directions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Integrate External BTC Price Feed. Pause entries during strong trending markets.&lt;/li&gt;
&lt;li&gt;Mathematical Modeling. Up and Down are essentially binary options. Option pricing theory could model:

&lt;ul&gt;
&lt;li&gt;Probability of imbalance&lt;/li&gt;
&lt;li&gt;Optimal entry timing&lt;/li&gt;
&lt;li&gt;Expected value more rigorously&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dynamic Parameter Adjustment. Current parameters are static. Volatility changes constantly. Adaptive parameters would significantly improve robustness.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The core idea — exploiting temporary mispricing in binary markets — is not limited to Polymarket BTC contracts.&lt;br&gt;
Any binary-structured market with short-term pricing inefficiencies can apply similar logic.&lt;br&gt;
What we built is merely a framework.&lt;br&gt;
The truly interesting work begins from here.&lt;/p&gt;

&lt;p&gt;Strategy Source Code:&lt;br&gt;
Polymarket BTC 15-Minute Dual-Leg Hedged Arbitrage Bot (Two-Sided Hedge Version)&lt;/p&gt;

</description>
      <category>aitrading</category>
      <category>hedging</category>
      <category>arbitrage</category>
      <category>strategy</category>
    </item>
    <item>
      <title>Funding Rate Arbitrage Strategy: Automated Implementation with AI and Workflows</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Wed, 25 Feb 2026 03:06:51 +0000</pubDate>
      <link>https://forem.com/quant001/funding-rate-arbitrage-strategy-automated-implementation-with-ai-and-workflows-3ha3</link>
      <guid>https://forem.com/quant001/funding-rate-arbitrage-strategy-automated-implementation-with-ai-and-workflows-3ha3</guid>
      <description>&lt;h2&gt;
  
  
  1. Strategy Background &amp;amp; Requirements
&lt;/h2&gt;

&lt;p&gt;Recently, some friends asked: could we build a funding rate arbitrage strategy? Between CEX and DEX platforms, funding rates are often inconsistent, creating opportunities to collect arbitrage signals and use AI to determine whether it's worth opening a position. However, there has always been a pain point: no good tool for detecting arbitrage opportunities.&lt;/p&gt;

&lt;p&gt;Fortunately, someone recommended the VarFunding platform. This platform collects real-time funding rate data from multiple exchanges, including Binance, Hyperliquid, Lighter, Bybit, and more — all of which are already supported on the FMZ Quant platform. More importantly, it compares funding rate data in real time and provides optimal arbitrage opportunity combinations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgg0ojwpsbxr9w878hpv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgg0ojwpsbxr9w878hpv0.png" alt=" " width="800" height="507"&gt;&lt;/a&gt;&lt;br&gt;
Talk about perfect timing! Today, let's walk through the design of this funding rate arbitrage strategy step by step.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. What Is Funding Rate Arbitrage?
&lt;/h2&gt;

&lt;p&gt;Before diving into the strategy, let's clarify: what exactly is funding rate arbitrage?&lt;/p&gt;

&lt;p&gt;Perpetual contracts have a mechanism called the funding rate. Settlement cycles vary by exchange — some settle every 8 hours, some every 4 hours, and some even every hour.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the rate is positive: longs pay shorts&lt;/li&gt;
&lt;li&gt;When the rate is negative: shorts pay longs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different exchanges have different user structures and liquidity profiles, resulting in different long/short dynamics. This means the funding rate for the same token often differs across exchanges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Arbitrage Principle&lt;/strong&gt;&lt;br&gt;
For example, Exchange A has a BTC funding rate of +0.1%, while Exchange B has +0.05%.&lt;/p&gt;

&lt;p&gt;In this case, we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go short on Exchange A (collect the 0.1% rate)&lt;/li&gt;
&lt;li&gt;Go long on Exchange B (pay the 0.05% rate)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The positions hedge each other — price movements cancel out — but the rate differential (0.05%) goes straight into your pocket.&lt;/p&gt;

&lt;p&gt;This is the core logic of funding rate arbitrage: you earn the rate spread, not price movement.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Why Use a Workflow?
&lt;/h2&gt;

&lt;p&gt;Now that we understand the principle, the next question is: how do we implement it?&lt;/p&gt;

&lt;p&gt;The traditional approach is to write a complete program, but this time we chose to implement it using a workflow. Workflows have several unique advantages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Modular Design&lt;/strong&gt;&lt;br&gt;
Each node handles an independent function — data collection, filtering, AI evaluation, execution — each with a clear role, making debugging and modification straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Visual Representation&lt;/strong&gt;&lt;br&gt;
The entire strategy's execution flow is visible at a glance, making it easy to quickly locate any issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Flexible Extensibility&lt;/strong&gt;&lt;br&gt;
Want to swap in new filtering criteria or optimize trading logic? Just modify the corresponding node without touching other parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Easy AI Integration&lt;/strong&gt;&lt;br&gt;
Workflows natively support calling large language models, making it simple to incorporate AI into the decision-making process.&lt;/p&gt;

&lt;p&gt;For an arbitrage strategy that requires multiple data sources and multi-step processing, workflows are an excellent implementation approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Overall Strategy Architecture
&lt;/h2&gt;

&lt;p&gt;This strategy has two main pipelines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline 1: Trade Execution (Hourly Interval)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppe6zgcu6xr4fgh0u23k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppe6zgcu6xr4fgh0u23k.png" alt=" " width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Exchange Initialization → Build exchange mappings&lt;/li&gt;
&lt;li&gt;Rate Data Analysis → Analyze historical data stability&lt;/li&gt;
&lt;li&gt;Close Position Detection → Check if existing positions need to be closed&lt;/li&gt;
&lt;li&gt;Rate Data Verification → Verify spread stability and liquidity&lt;/li&gt;
&lt;li&gt;AI Smart Evaluation → Comprehensive assessment of opening opportunities&lt;/li&gt;
&lt;li&gt;Open Position Execution → Execute arbitrage trades&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pipeline 2: Rate Collection (Minute Interval)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezuwsgyqjrv01v6xtbyo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezuwsgyqjrv01v6xtbyo.png" alt=" " width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch Arbitrage Opportunities → Pull data from VarFunding&lt;/li&gt;
&lt;li&gt;Data Filtering → Filter opportunities that meet criteria&lt;/li&gt;
&lt;li&gt;Data Storage → Save historical snapshots&lt;/li&gt;
&lt;li&gt;Visualization → Display account and position status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The two pipelines each serve their purpose: the execution pipeline handles decisions and trades, while the collection pipeline handles data gathering and display. This design allows the strategy to execute efficiently while maintaining real-time awareness of its operational state.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Configuration Parameters
&lt;/h2&gt;

&lt;p&gt;The strategy has several key parameters to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exchange: Exchange list — determines which exchanges the strategy searches for arbitrage opportunities between, e.g., ['binance', 'hyperliquid', 'lighter']&lt;/li&gt;
&lt;li&gt;Confidence: Confidence filter — determines which confidence levels qualify for the verification process, options: ['high', 'medium', 'low']&lt;/li&gt;
&lt;li&gt;Amount: Per-trade position size — AI will adjust the actual amount based on risk level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These parameters can be flexibly configured based on your needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Data Collection &amp;amp; Filtering
&lt;/h2&gt;

&lt;p&gt;The first step is fetching arbitrage opportunity data. The strategy sends an HTTP request to VarFunding's API endpoint with the configured exchange list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const url = `https://varfunding.xyz/api/funding?exchanges=${exchangeList.join(',')}`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The platform returns funding rate data for all tokens across all exchanges, along with pre-calculated optimal arbitrage combinations. However, the raw data is too extensive to use entirely. The strategy filters it, keeping only opportunities where both the long and short exchanges are in our configured list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const filteredMarkets = data.data.markets.filter(market =&amp;gt; {
    const bestExchange = market.bestRate?.exchange;
    const worstExchange = market.worstRate?.exchange;
    return exchangeList.includes(bestExchange) &amp;amp;&amp;amp; exchangeList.includes(worstExchange);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The filtered data is extracted into a concise format, retaining only key information: trading pair, long exchange, short exchange, rate spread, estimated annualized return, and confidence level. This data is saved for subsequent arbitrage opportunity analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Rate Stability Verification
&lt;/h2&gt;

&lt;p&gt;A single snapshot of rate data isn't sufficient, since rates may fluctuate temporarily. The strategy continuously collects data snapshots, saving current rate data every minute and retaining only the most recent N hours of historical records.&lt;/p&gt;

&lt;p&gt;Once enough samples accumulate, the strategy performs statistical analysis, calculating the frequency of each token appearing at different confidence levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const stats = {};
for (const snapshot of savedData) {
    for (const [baseAsset, info] of Object.entries(snapshot.data)) {
        if (!stats[baseAsset]) {
            stats[baseAsset] = { high: 0, medium: 0, low: 0, total: 0 };
        }
        stats[baseAsset].total++;
        if (info.confidence === 'high') stats[baseAsset].high++;
        // ... tally other confidence levels
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tokens are then categorized by their most frequent confidence level, and a weighted score is calculated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;score = (highCount × 3 + mediumCount × 2 + lowCount × 1) / totalCount

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This score reflects the stability of the arbitrage opportunity. A higher score indicates more consistent performance in historical data, making the opportunity more trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Close Position Detection
&lt;/h2&gt;

&lt;p&gt;After rate analysis, the strategy checks existing positions against the latest arbitrage opportunity list. The core logic involves scanning all exchanges for current positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function scanAllPositions() {
    const allPositions = {};
    for (const [exName, exIndex] of Object.entries(EXCHANGE_MAP)) {
        const positions = exchanges[exIndex].GetPositions();
        for (const pos of positions) {
            const baseAsset = pos.Symbol.match(/^([A-Z0-9]+)_/)[1];
            const isLong = pos.Amount &amp;gt; 0;
            // Record long/short position info
        }
    }
    return allPositions;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it determines which positions need to be closed. There are two scenarios:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1:&lt;/strong&gt; The arbitrage opportunity has disappeared. If a held position's token is no longer found in the latest opportunity list, it means the rate spread has converged and the position should be closed to lock in profits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2:&lt;/strong&gt; The direction has reversed. For example, if the original position was long on Exchange A and short on Exchange B, but now the optimal direction is reversed, the old positions need to be closed first.&lt;/p&gt;

&lt;p&gt;When closing positions, both sides are closed simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Close long position
exchanges[longExIndex].CreateOrder(symbol, "closebuy", -1, amount);
// Close short position
exchanges[shortExIndex].CreateOrder(symbol, "closesell", -1, amount);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Position records are then cleared, ensuring that any expired positions are handled before searching for new opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. In-Depth Rate Data Verification
&lt;/h2&gt;

&lt;p&gt;After close position detection, the strategy performs live verification on the filtered arbitrage opportunities. Verification covers two aspects:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Spread Stability Verification&lt;/strong&gt;&lt;br&gt;
By fetching K-line data from both exchanges, historical spreads are calculated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Fetch 240 candlesticks
const recordsLong = EX_Long.GetRecords(symbol, PERIOD_M1, 240);
const recordsShort = EX_Short.GetRecords(symbol, PERIOD_M1, 240);

// Calculate spreads
const spreads = [];
for (const time of commonTimes) {
    const midPrice = (priceMapLong[time] + priceMapShort[time]) / 2;
    const spread = (priceMapLong[time] - priceMapShort[time]) / midPrice;
    spreads.push(spread);
}

// Statistical metrics
const avgSpread = mean(spreads);
const range = max(spreads) - min(spreads);
const stdDev = standardDeviation(spreads);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These metrics reflect the consistency of price movements between the two exchanges. If spread volatility is too high, the risk is elevated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Liquidity Detection&lt;/strong&gt;&lt;br&gt;
Real-time market data is fetched to calculate trading costs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const tickerLong = EX_Long.GetTicker(symbol);
const tickerShort = EX_Short.GetTicker(symbol);

// Bid-ask spread (reflects depth)
const longSpread = (tickerLong.Sell - tickerLong.Buy) / tickerLong.Last;
const shortSpread = (tickerShort.Sell - tickerShort.Buy) / tickerShort.Last;

// Opening cost (buy on Long exchange, sell on Short exchange)
const openCost = (tickerLong.Sell - tickerShort.Buy) / midPrice;

// Closing cost (buy on Short exchange, sell on Long exchange)
const closeCost = (tickerShort.Sell - tickerLong.Buy) / midPrice;

// Round-trip cost
const roundTripCost = openCost + closeCost;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the round-trip cost is too high, it will erode rate profits, making the opportunity not worth pursuing.&lt;/p&gt;

&lt;p&gt;After verification, if there are no viable arbitrage opportunities (i.e., the data object is empty), the process ends. If there are new opening opportunities, they proceed to the AI evaluation stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. AI Smart Evaluation
&lt;/h2&gt;

&lt;p&gt;Verified opportunities are handed to AI for comprehensive evaluation. We use a carefully crafted prompt that instructs the AI to think like a professional arbitrage analyst.&lt;/p&gt;

&lt;p&gt;The AI analyzes several dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Risk-Reward Ratio&lt;/strong&gt;&lt;br&gt;
Whether rate income can cover spread volatility risk. The core judgment logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If rateSpread (per 8-hour income) &amp;gt; range × 0.3
This means even in extreme spread scenarios, 1-2 settlement cycles can cover the risk

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI scores each opportunity (0–100) and assigns a risk level (low/medium/high). Only opportunities meeting the score threshold are executed. Additionally, the AI recommends position sizes based on risk level:&lt;/p&gt;

&lt;p&gt;Score ≥ 80 and low risk: Suggested amount × 100%&lt;br&gt;
Score 70–79 or medium risk: Suggested amount × 80%&lt;br&gt;
Score 60–69: Suggested amount × 60%&lt;br&gt;
Score &amp;lt; 60: Not recommended&lt;br&gt;
The AI outputs a JSON array where each element contains: whether to open, score, suggested amount, risk level, and decision rationale.&lt;/p&gt;
&lt;h2&gt;
  
  
  11. Position Opening Execution Flow
&lt;/h2&gt;

&lt;p&gt;After AI approval, the strategy executes the opening. The execution flow is rigorous:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Calculate Contract Quantity&lt;/strong&gt;&lt;br&gt;
Based on the suggested amount and current price, calculate how many contracts to open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const currentPrice = getCurrentPrice(ex, symbol);
const contractAmount = amountUSD / currentPrice / ctVal;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where ctVal is the contract face value, which may differ across exchanges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Set Leverage&lt;/strong&gt;&lt;br&gt;
Attempt to set the target leverage (default 10x), falling back to lower levels if unsupported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const leveragesToTry = [10, 5, 3, 2, 1];
for (let lev of leveragesToTry) {
    const result = ex.SetMarginLevel(symbol, lev);
    if (result === true) {
        return lev; // Successfully set
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Place Order &amp;amp; Confirm&lt;/strong&gt;&lt;br&gt;
After placing the order, don't assume success — wait for fill confirmation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const orderId = ex.CreateOrder(symbol, 'buy', -1, contractAmount);

// Poll order status
for (let i = 0; i &amp;lt; 20; i++) {
    const order = ex.GetOrder(orderId);
    if (order.Status === ORDER_STATE_CLOSED) {
        // Fully filled
        break;
    }
    Sleep(500);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Verify Position&lt;/strong&gt;&lt;br&gt;
Even if the order shows as filled, verify the actual position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const position = getPosition(ex, symbol);
if (!position || Math.abs(position.Amount) === 0) {
    // Order filled but no position — opening failed
    return { success: false };
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Exception Handling&lt;/strong&gt;&lt;br&gt;
If only one side opens successfully (e.g., long succeeds but short fails), the strategy automatically closes the opened position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!results.short.success &amp;amp;&amp;amp; results.long.success) {
    // Attempt to close the long position
    const pos = getPosition(longEx, longSymbol);
    executeClose(longEx, longSymbol, true, pos.Amount, longExchange);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents unhedged exposure, ensuring the arbitrage's hedged nature is maintained.&lt;/p&gt;

&lt;p&gt;After successful opening, the strategy saves arbitrage position information — including open time, price, quantity, etc. — for subsequent close detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Visual Monitoring Dashboard
&lt;/h2&gt;

&lt;p&gt;During operation, all information is displayed in real time on a monitoring dashboard with five tables: the Account Overview shows each exchange's balance, frozen funds, total equity, and P&amp;amp;L summary; the Arbitrage Signals table presents currently detected arbitrage opportunities, execution status, and failure reasons; the Opening Decision Details log the AI's score, risk level, and rationale for each opportunity; the Live Arbitrage Positions display current arbitrage combinations, long/short P&amp;amp;L, and holding duration; and the Arbitrage Profit Statistics summarize today's, this week's, this month's, and cumulative arbitrage profits and returns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuz248d29knc547fyb265.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuz248d29knc547fyb265.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
Through these dashboards, you can monitor the strategy's operational status in real time, including account health, current opportunities, position P&amp;amp;L, and all other key information.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Risks &amp;amp; Considerations
&lt;/h2&gt;

&lt;p&gt;Finally, let's discuss risks and considerations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Spread Volatility Risk&lt;/strong&gt;&lt;br&gt;
Although funding rate arbitrage is a hedged strategy, it is not risk-free. The spread between two exchanges can fluctuate, and if the fluctuation exceeds the rate income, losses will occur. This is why the strategy verifies spread stability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Liquidity Risk&lt;/strong&gt;&lt;br&gt;
If an exchange lacks sufficient liquidity, slippage during opening and closing can be significant. For example, if order book depth is insufficient, your market orders may fill at poor prices, resulting in actual opening costs far exceeding expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Exchange Risk&lt;/strong&gt;&lt;br&gt;
This is especially relevant for newer DEXs, which may have smart contract vulnerabilities or liquidity dry-up issues. There have been cases where DEX users lost funds due to smart contract bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Capital Efficiency&lt;/strong&gt;&lt;br&gt;
This strategy requires funds on multiple exchanges. For example, if arbitraging across 3 exchanges, capital must be split three ways, resulting in relatively low capital efficiency.&lt;/p&gt;

&lt;p&gt;It's recommended to start with a small amount, familiarize yourself with the strategy's logic, and then gradually increase your commitment. Start by arbitraging between two major exchanges, then expand to more after gaining experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Summary &amp;amp; Outlook
&lt;/h2&gt;

&lt;p&gt;Today we introduced this funding rate arbitrage strategy based on the VarFunding platform. It implements a complete pipeline from data collection, stability verification, AI evaluation, to automated execution, with clear visual monitoring.&lt;/p&gt;

&lt;p&gt;The core philosophy of the strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data-driven opportunity discovery: Decisions are based on real-time rate data and historical statistics, not guesswork&lt;/li&gt;
&lt;li&gt;AI-assisted decision making: Large language models evaluate risk and reward, avoiding subjective human judgment&lt;/li&gt;
&lt;li&gt;Rigorous execution flow for risk control: From order confirmation to position verification, every step includes checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, this is just a foundational framework with plenty of room for optimization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add more exchanges: Currently supported exchanges are limited; expand to more platforms&lt;/li&gt;
&lt;li&gt;Optimize statistical logic: Introduce more sophisticated time series analysis to predict rate trends&lt;/li&gt;
&lt;li&gt;Improve AI evaluation: Let AI learn from historical arbitrage successes and failures to continuously optimize decisions&lt;/li&gt;
&lt;li&gt;Dynamic parameter adjustment: Automatically adjust position sizes and risk thresholds based on market volatility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete code is publicly available on the FMZ Quant platform: Workflow Funding Rate Arbitrage Strategy. Feel free to use and improve it.&lt;/p&gt;

</description>
      <category>aitrading</category>
      <category>workflow</category>
      <category>strategy</category>
      <category>arbitrage</category>
    </item>
    <item>
      <title>Building an AI-Powered Automated Trading System from Scratch: Making ClawdBot(OpenClaw) Your Trading Brain</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Thu, 05 Feb 2026 06:00:02 +0000</pubDate>
      <link>https://forem.com/quant001/building-an-ai-powered-automated-trading-system-from-scratch-making-clawdbotopenclaw-your-117c</link>
      <guid>https://forem.com/quant001/building-an-ai-powered-automated-trading-system-from-scratch-making-clawdbotopenclaw-your-117c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In our previous video, we built a dual moving average strategy using ClawdBot/OpenClaw. Many viewers wanted more and asked in the comments: Can we let AI monitor the market, make decisions, and execute trades on its own? Today, we're going to tackle this ultimate goal and build a fully automated AI trading system.&lt;/p&gt;

&lt;p&gt;Some of you are concerned that letting AI trade directly is too risky. That's why we've designed a safer architecture: ClawdBot serves as the brain, responsible for collecting data, analyzing news, and generating trading signals; FMZ Quant Platform serves as the hands, responsible for receiving signals, executing trades, and managing risk control. This approach leverages AI's analytical capabilities while maintaining the safety guarantees of a professional platform, plus real-time monitoring and review capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc76i7dtp0rqr20f9uzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc76i7dtp0rqr20f9uzg.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. System Architecture Breakdown
&lt;/h2&gt;

&lt;p&gt;Before we start coding, let's break down the entire system. The complete system consists of two core components—the Brain and the Executor—connected via HTTP requests to form a complete AI quantitative trading loop:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhol5o5n6rtcgmmyzdgd9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhol5o5n6rtcgmmyzdgd9.png" alt=" " width="800" height="466"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;1.1 ClawdBot (OpenClaw - Brain)&lt;/strong&gt;&lt;br&gt;
Collect Real-time Data: Through browser automation, obtain the target cryptocurrency's real-time price, trading volume, and news updates.&lt;br&gt;
Analyze News Sentiment: AI integrates news content and determines market sentiment as bullish, bearish, or neutral.&lt;br&gt;
Generate Trading Decision JSON: Combining data and sentiment analysis, generate buy, sell, or hold decisions, packaged as JSON format for standardized cross-platform communication.&lt;br&gt;
HTTP POST to FMZ: Push the JSON-formatted decision signals to the FMZ Quant Platform executor via HTTP POST requests.&lt;br&gt;
&lt;strong&gt;1.2 FMZ Strategy (Executor)&lt;/strong&gt;&lt;br&gt;
Listen to Channel for Signals: Continuously monitor the designated channel to capture HTTP requests and trading signals from the brain.&lt;br&gt;
Parse JSON and Execute Trades: Parse the received JSON data, extract core trading instructions, and trigger the platform's live trading bot to automatically complete order placement.&lt;br&gt;
Risk Management and Stop-Loss Protection: Monitor market prices in real-time during trade execution, implementing stop-loss and FOMO protection according to preset rules to reduce trading risks.&lt;br&gt;
Visual Dashboard Display: Display trading status, execution results, risk control data, and account equity on the platform dashboard in real-time for full-process visual monitoring.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Writing Instructions: Teaching AI What to Do
&lt;/h2&gt;

&lt;p&gt;The core of ClawdBot is the instruction set—essentially a detailed operating manual for the AI, specifying its operating steps, judgment criteria, and execution actions. Let's look at the structure of these instructions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1 Data Collection Section&lt;/strong&gt;&lt;br&gt;
First, tell the AI to open the target cryptocurrency's page and specify which key data to read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Operation Instructions]
1. Automatically open a new browser tab
2. Navigate to the target cryptocurrency page: https://www.coingecko.com/en/coins/bitcoin
3. Wait for the page to fully load

[Data to Read]
- BTC current price
- 24-hour trading volume

[News to Read]
- Scroll the page to find the "News" section
- Read the latest 3-5 news headlines and summaries
- Consolidate all news into a single summary
- Determine overall sentiment (bullish/bearish/neutral)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.2 Decision Logic Section&lt;/strong&gt;&lt;br&gt;
Next, define clear trading judgment criteria, specifying when to buy and when to sell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Buy Conditions]
□ Overall positive news sentiment (bullish news dominant)
□ Increased trading volume indicating active market
□ Comprehensive judgment of upward market trend

[Sell Conditions]
□ Overall negative news sentiment (bearish news dominant)
□ Clear risk signals in the market

[Hold Conditions]
□ Neutral or mixed news sentiment
□ Unclear market direction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.3 Output Format Definition&lt;/strong&gt;&lt;br&gt;
Require AI to generate standard JSON format data—the core communication protocol between ClawdBot and FMZ platform, ensuring unambiguous data parsing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "coin": "BTC",
    "timestamp": "2024-01-15T14:00:00.000Z",
    "current_price": 42500.00,
    "volume_24h": "25,000,000,000",
    "news_summary": "Continuous ETF fund inflows, strong institutional accumulation interest",
    "news_sentiment": "positive",
    "trade_decision": "buy",
    "decision_reason": "Positive news sentiment, increased trading volume, suitable for position building"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.4 Execution Actions&lt;/strong&gt;&lt;br&gt;
Finally, tell the AI to push the JSON signal to the FMZ platform and maintain local operation records for future review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Send Signal]
Execute HTTP POST request:
URL: https://www.fmz.com/api/v1?method=pub&amp;amp;robot={LiveTradingID}&amp;amp;channel={UUID}
- Live Trading ID: The unique numeric identifier of the corresponding bot on FMZ platform, used to precisely specify the target trading bot receiving signals.
- UUID: Universal Unique Identifier, serving as the custom channel's unique identifier and the communication key between ClawdBot and FMZ. Do not disclose it to avoid malicious operations.

[Record Log]
Append each operation's time, cryptocurrency, decision, and signal sending status to the local tradediary.csv file for subsequent strategy review and optimization.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this instruction set was refined through multiple tests. After testing passes, we can package it as a Skill and set up scheduled tasks for automatic execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4n9uhi2ztbmnl5wy99m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4n9uhi2ztbmnl5wy99m.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. FMZ Platform: Signal Receiving Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwti46e9ydxycjq3a0oqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwti46e9ydxycjq3a0oqo.png" alt=" " width="800" height="311"&gt;&lt;/a&gt;&lt;br&gt;
With AI decisions ready, we now need to write corresponding code on the FMZ platform to implement signal reception, trade execution, and risk management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 Configuration Parameters&lt;/strong&gt;&lt;br&gt;
First, define core configuration items to centrally manage trading, monitoring, and risk control parameters. The UUID must match the one in ClawdBot's instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CONFIG = {
    TRADE_AMOUNT: 0.01,           // Fixed trading amount per transaction
    CHECK_INTERVAL: 3000,         // Signal monitoring and risk check interval (milliseconds)
    UUID: "CustomUUID",           // Channel identifier matching ClawdBot, serves as communication key
    STOP_LOSS_PERCENT: 5,         // Stop-loss/FOMO protection percentage (%)
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2 Signal Reception and Deduplication&lt;/strong&gt;&lt;br&gt;
Use FMZ platform's built-in GetChannelData function to monitor the specified UUID channel and retrieve AI-sent JSON signals. An important detail: use timestamps to determine if a signal has been processed, preventing duplicate execution due to network or platform issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function receiveSignal() {
    try {
        const res = GetChannelData(CONFIG.UUID);

        if (res === null || res === "") {
            return null;
        }

        const signal = typeof res === 'string' ? JSON.parse(res) : res;

        // Validate signal format, ensure core fields are complete
        if (!signal.coin || !signal.trade_decision) {
            Log("❌ Invalid signal format, missing core fields");
            return null;
        }

        // Check if timestamp has been processed (core deduplication logic)
        if (isSignalProcessed(signal.timestamp)) {
            return null;
        }

        Log(`📥 New signal received: ${signal.coin} - ${signal.trade_decision}`);
        return signal;

    } catch (e) {
        Log(`❌ Failed to get signal: ${e.message}`);
        return null;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.3 Trade Execution Logic&lt;/strong&gt;&lt;br&gt;
Parse the core content from signals and execute corresponding buy/sell operations based on the trade_decision field. Before buying, check if USDT balance is sufficient; before selling, check if target cryptocurrency balance is sufficient to avoid trade failures due to insufficient balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function executeBuy(signal, reason) {
    const symbol = signal.coin + "_USDT"; // Construct trading pair, e.g., BTC_USDT
    exchange.SetCurrency(symbol); // Set current trading pair

    const account = _C(exchange.GetAccount); // Get real-time account assets
    const ticker = _C(exchange.GetTicker); // Get real-time market data
    const currentPrice = ticker.Last; // Get latest transaction price

    // Check sufficient funds
    const requiredBalance = currentPrice * CONFIG.TRADE_AMOUNT;
    if (account.Balance &amp;lt; requiredBalance) {
        Log(`❌ Insufficient balance, need USDT $${requiredBalance.toFixed(2)}, current $${account.Balance.toFixed(2)}`);
        return false;
    }

    // Execute market buy (-1 means market order, transact at current market price)
    const orderId = exchange.Buy(-1, CONFIG.TRADE_AMOUNT * currentPrice);

    if (orderId) {
        addTradeRecord({
            coin: signal.coin,
            action: "Buy",
            price: currentPrice,
            amount: CONFIG.TRADE_AMOUNT,
            reason: reason
        });
        Log(`✅ ${signal.coin} market buy successful, transaction price: $${currentPrice.toFixed(2)}`);
        return true;
    }
    Log(`❌ ${signal.coin} buy failed, no order ID generated`);
    return false;
}

function executeSell(signal, reason) {
    try {
        const symbol = signal.coin + "_USDT";
        exchange.SetCurrency(symbol);

        const account = _C(exchange.GetAccount);
        const ticker = _C(exchange.GetTicker);
        const currentPrice = ticker.Last;

        // Check sufficient target cryptocurrency
        if (account.Stocks &amp;lt; CONFIG.TRADE_AMOUNT) {
            Log(`❌ ${signal.coin} insufficient balance, need ${CONFIG.TRADE_AMOUNT}, current ${account.Stocks.toFixed(6)}`);
            return false;
        }

        // Execute market sell
        const orderId = exchange.Sell(-1, CONFIG.TRADE_AMOUNT);

        if (orderId) {
            // Add trade record to platform
            addTradeRecord({
                coin: signal.coin,
                action: "Sell",
                price: currentPrice,
                amount: CONFIG.TRADE_AMOUNT,
                reason: reason || signal.decision_reason,
                timestamp: new Date().toISOString()
            });

            Log(`✅ ${signal.coin} market sell successful - Price: $${currentPrice.toFixed(2)}, Amount: ${CONFIG.TRADE_AMOUNT}`);
            return true;
        } else {
            Log(`❌ ${signal.coin} sell failed, no order ID generated`);
            return false;
        }

    } catch (e) {
        Log(`❌ Sell exception: ${e.message}`);
        return false;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.4 Stop-Loss Risk Control Mechanism&lt;/strong&gt;&lt;br&gt;
The code sets a fixed stop-loss percentage. When market price hits the preset stop-loss line, it automatically executes reverse trading operations. This covers two core scenarios—loss stop-loss and FOMO stop-loss—for comprehensive risk control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function checkStopLoss() {
    const latestTrade = getLatestTradeRecord(); // Get latest trade record

    if (!latestTrade) return { triggered: false }; // Skip risk check if no trade records

    const ticker = _C(exchange.GetTicker);
    const currentPrice = ticker.Last;
    const tradePrice = latestTrade.price; // Latest trade's transaction price
    const priceChangePercent = ((currentPrice - tradePrice) / tradePrice) * 100; // Price change percentage

    // Scenario 1: Price drops after buying, trigger loss stop-loss to limit maximum loss
    if (latestTrade.action === "Buy") {
        const stopLossPrice = tradePrice * (1 - CONFIG.STOP_LOSS_PERCENT / 100); // Calculate stop-loss price

        if (currentPrice &amp;lt;= stopLossPrice) {
            Log(`🔴 Loss stop-loss triggered! Current drop: ${priceChangePercent.toFixed(2)}%, hit ${CONFIG.STOP_LOSS_PERCENT}% stop-loss line`);
            return {
                triggered: true,
                action: "Sell",
                reason: `Stop-loss sell: Drop triggered ${CONFIG.STOP_LOSS_PERCENT}% stop-loss line, current drop ${priceChangePercent.toFixed(2)}%`
            };
        }
    }

    // Scenario 2: Price rises after selling, trigger FOMO stop-loss to avoid missing major rallies
    if (latestTrade.action === "Sell") {
        const stopLossPrice = tradePrice * (1 + CONFIG.STOP_LOSS_PERCENT / 100); // Calculate FOMO protection price

        if (currentPrice &amp;gt;= stopLossPrice) {
            Log(`🟢 FOMO stop-loss triggered! Current rise: ${priceChangePercent.toFixed(2)}%, hit ${CONFIG.STOP_LOSS_PERCENT}% protection line`);
            return {
                triggered: true,
                action: "Buy",
                reason: `FOMO stop-loss: Rise triggered ${CONFIG.STOP_LOSS_PERCENT}% protection line, current rise ${priceChangePercent.toFixed(2)}%`
            };
        }
    }

    return { triggered: false };
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.5 Main Loop Logic&lt;br&gt;
The strategy's core is an infinite loop that processes stop-loss checks, AI signal handling, and dashboard updates in sequence following a risk-first principle, ensuring risk control always has the highest priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function main() {
    initialize(); // Initialize: create trade records, dashboard, etc.
    Log("🚀 Spot auto-trading bot started, listening for AI signals...");

    while (true) { // Infinite loop, continuous operation
        // 1. Check stop-loss (highest priority), execute immediately if triggered
        const stopLossInfo = checkStopLoss();
        if (stopLossInfo.triggered) {
            executeStopLoss(stopLossInfo);
        }

        // 2. Get and process AI signals, skip if no signal
        const signal = receiveSignal();
        if (signal) {
            processSignal(signal);
        }

        // 3. Update visual dashboard in real-time
        displayDashboard();

        // 4. Wait for preset interval, enter next check cycle
        Sleep(CONFIG.CHECK_INTERVAL);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.6 Visual Display&lt;/strong&gt;&lt;br&gt;
Running trades alone isn't intuitive enough. We've also created a visual dashboard with four core data tables, presenting account, risk control, signals, and trade records in one place for clear system status visibility:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf5ajur2rpqjvnkzj4fs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf5ajur2rpqjvnkzj4fs.png" alt=" " width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38jhs6zpfeulfoidpsak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38jhs6zpfeulfoidpsak.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Automated Execution
&lt;/h2&gt;

&lt;p&gt;Once ClawdBot instructions are debugged and FMZ platform code is tested successfully, the system can run fully automatically: Package the written instructions as a Skill (reusable AI operation template) in ClawdBot's backend, and set up scheduled tasks (e.g., execute every 10 minutes). The system will automatically collect data, analyze decisions, and send signals at the set frequency, while FMZ platform listens in real-time and executes trades—all without manual monitoring.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwx8zzzazjn0f6nc2pkd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwx8zzzazjn0f6nc2pkd.png" alt=" " width="600" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Precautions and Risk Warnings
&lt;/h2&gt;

&lt;p&gt;Here's a special reminder: Quantitative trading always carries market risks, and AI decisions are not absolutely accurate. Please note the following before operating:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI decisions are for reference only&lt;/strong&gt;—its analysis is based on public data and preset logic, and you cannot completely rely on it for investment decisions;&lt;/li&gt;
&lt;li&gt;Although stop-loss mechanisms are in place, the strategy may fail under extreme market conditions (such as flash crashes), unable to completely avoid losses;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's recommended to test with paper trading or small amounts first&lt;/strong&gt;, running at least 1-2 trading cycles to verify system stability before gradually increasing investment;&lt;/li&gt;
&lt;li&gt;Pay attention to risk isolation—it's recommended to use separate trading accounts and devices for operations to avoid exposing account information and UUID communication keys;&lt;/li&gt;
&lt;li&gt;Regularly check strategy operation status, review trading logs and platform error messages to ensure all ClawdBot and FMZ platform modules are working properly;&lt;/li&gt;
&lt;li&gt;Trading logs can serve as feedback data to optimize ClawdBot instruction decision logic and FMZ platform risk control parameters for continuous system iteration.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>openclaw</category>
      <category>aitrading</category>
      <category>ai</category>
      <category>strategy</category>
    </item>
    <item>
      <title>ClawdBot Hands-On Experience: The Era of AI-Written Trading Strategies Has Arrived</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Thu, 05 Feb 2026 05:30:56 +0000</pubDate>
      <link>https://forem.com/quant001/clawdbot-hands-on-experience-the-era-of-ai-written-trading-strategies-has-arrived-1mak</link>
      <guid>https://forem.com/quant001/clawdbot-hands-on-experience-the-era-of-ai-written-trading-strategies-has-arrived-1mak</guid>
      <description>&lt;p&gt;Recently, something called ClawdBot (OpenClaw) has completely taken off in AI circles—it's not just an ordinary chatbot, but a "personal assistant" that can directly operate your computer. As a quantitative trading enthusiast, the first thought that popped into my head was: Can this thing help me with quant trading?&lt;/p&gt;

&lt;p&gt;Today, let's run a real test by combining ClawdBot with FMZ Quant Platform to see how much work this "AI personal assistant" can actually handle in strategy development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Installation and First Meeting
&lt;/h2&gt;

&lt;p&gt;The installation process was simpler than expected. I tried it on macOS—basically one command line and following the official guide got it done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sy5z70p6a1275ql3d0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sy5z70p6a1275ql3d0m.png" alt=" " width="574" height="460"&gt;&lt;/a&gt;&lt;br&gt;
After launching, a clean Dashboard interface appeared—this is the main battlefield for communicating with AI. It supports multiple channels including Lark, Slack, and WhatsApp, with both text and voice options, quite flexible. Looking at AI's first reply, my quantitative personal assistant was officially on duty.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4t534r2eb5sx178wqa9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4t534r2eb5sx178wqa9.png" alt=" " width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge Begins: Let's Have AI Write a Strategy
&lt;/h2&gt;

&lt;p&gt;Without hesitation, I went straight to the challenge. I gave ClawdBot its first task: "Write a dual moving average quantitative strategy." I chose this because the logic is clean: buy when the short-term moving average crosses above the long-term one (golden cross), sell when it crosses below (death cross). Classic, clear, perfect for testing AI's coding ability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg85zy2t1xpvp1gxcxfb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg85zy2t1xpvp1gxcxfb1.png" alt=" " width="700" height="120"&gt;&lt;/a&gt;&lt;br&gt;
Once the instruction was sent, this "digital colleague" got to work. I watched it automatically open a browser, head straight to FMZ Quant Platform, find the strategy editor, and then—start writing code.&lt;/p&gt;

&lt;p&gt;The whole process was surprisingly smooth. It clearly understood FMZ's API, with clean code structure: first set parameters (5-day short-term, 20-day long-term, 0.01 per trade), then write utility functions for getting moving averages, initialize charts in the main function, use loops to continuously fetch K-lines, calculate indicators, execute trades, and even added visualization as a bonus. After finishing, it even included a detailed logic explanation—definitely beginner-friendly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5777kom36r6a7q6bn5m5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5777kom36r6a7q6bn5m5.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b8hga67opgvvrdujbit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b8hga67opgvvrdujbit.png" alt=" " width="700" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Backtesting and First Debugging
&lt;/h2&gt;

&lt;p&gt;Writing code isn't enough—you have to put it to the test. I told it: "Use Binance exchange, BTC/USDT trading pair, daily data, run a backtest."&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7zoyjp9oszm59wx1zlq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7zoyjp9oszm59wx1zlq.png" alt=" " width="700" height="280"&gt;&lt;/a&gt;&lt;br&gt;
AI switched to the backtest page, configured parameters, clicked run—and got an error! Just as I was thinking "guess I'll have to do this myself," it located the error on its own, proactively fixed a syntax issue, and resubmitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iyddbjv974ykvn7kmj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iyddbjv974ykvn7kmj8.png" alt=" " width="700" height="200"&gt;&lt;/a&gt;&lt;br&gt;
This time it ran successfully, but the results were... a bit dismal. However, it didn't stop there—it immediately generated a backtest report: return rate, maximum drawdown, win rate, analysis of typical losing trades—data at a glance. The report also initially identified various areas for optimization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdu8eyuzodpvxtcirj4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdu8eyuzodpvxtcirj4q.png" alt=" " width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy Optimization: Multi-Step Coordinated Operations
&lt;/h2&gt;

&lt;p&gt;The backtest results weren't ideal, so let's have AI optimize it. I proposed a complex requirement: change the exchange and trading pair, while adjusting and optimizing moving average period parameters to see if performance could improve. This task involves coordinated operations across multiple steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12lizuy5ulgnv06kk1t8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12lizuy5ulgnv06kk1t8.png" alt=" " width="700" height="100"&gt;&lt;/a&gt;&lt;br&gt;
After AI started executing, it first proposed specific optimization ideas based on the previous backtest results, then returned to the strategy editing page for modifications: changed the exchange to OKX, trading pair to ETH/USDT, and moving average periods from 5 and 20 to 10 and 50. What surprised me more was that it proactively added ATR volatility filtering and dynamic stop-loss logic—things I hadn't explicitly requested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlixorvdicttfbsw1m0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlixorvdicttfbsw1m0g.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
Then it returned to the editor and implemented the changes step by step. After modifications, it automatically re-ran the backtest—this time the return curve was much smoother, achieving positive returns. Finally, it generated a comparison analysis between old and new versions and provided suggestions for further optimization, such as considering increased position sizing, parameter and logic optimization, multi-asset portfolios, etc. These suggestions were all quite pertinent, showing that AI truly understands the basic logic of quantitative trading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbb311tmzc2441m6okyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbb311tmzc2441m6okyb.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuv5algj8qlnsuan3z6ux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuv5algj8qlnsuan3z6ux.png" alt=" " width="750" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up: Let AI Organize Development Notes
&lt;/h2&gt;

&lt;p&gt;Finally, a practical task: "Organize today's process from strategy writing to optimization debugging into reusable development documentation." It quickly organized a clearly structured note including: strategy version iteration records, reasoning for each modification, backtest data comparison, and potential next optimization directions—directly a qualified technical log.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mi3eyrcc7eznav4pfuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mi3eyrcc7eznav4pfuw.png" alt=" " width="750" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-Use Impressions
&lt;/h2&gt;

&lt;p&gt;After the complete test, the role ClawdBot plays in the quantitative workflow is that of a qualified quantitative trading assistant. While it can't replace your experience and judgment, it can efficiently execute many standardized operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Its advantages include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quick idea implementation:&lt;/strong&gt; From strategy description to runnable code in just a few minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High debugging efficiency:&lt;/strong&gt; Automatically locates errors, compares version differences, generates data reports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge base support:&lt;/strong&gt; Familiar with common strategy patterns, risk control methods, and platform APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tireless:&lt;/strong&gt; Can simultaneously backtest multiple parameter combinations or monitor multiple strategy statuses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But there are also clear limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Depends on clear instructions:&lt;/strong&gt; Vague requirements lead to output deviations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Superficial market understanding:&lt;/strong&gt; It understands code logic but not market sentiment and macro changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires human review:&lt;/strong&gt; After each important modification, logic reasonableness still needs human verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In actual use, the smoothest mode is: you grasp direction and risk control, it handles execution and debugging. For example, when you think "maybe adding volatility filtering would be better," it can immediately implement this idea and verify the effect; when you find the strategy loses money during sideways markets, it can quickly test multiple parameter combinations to find more robust configurations.&lt;/p&gt;

&lt;p&gt;This collaboration model is particularly suitable for high-repetition tasks like strategy prototyping, quick backtest verification, and parameter tuning. It lets quantitative developers focus more on core logic and market insights, rather than getting bogged down in code debugging and data organization.&lt;/p&gt;

</description>
      <category>clawdbot</category>
      <category>aitrading</category>
      <category>strategy</category>
      <category>workflow</category>
    </item>
    <item>
      <title>Building A Multi-Account Walkthrough System That Supports MyLanguage and Pine Strategy Language Based on FMZ</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Mon, 02 Feb 2026 07:13:47 +0000</pubDate>
      <link>https://forem.com/quant001/building-a-multi-account-walkthrough-system-that-supports-mylanguage-and-pine-strategy-language-59de</link>
      <guid>https://forem.com/quant001/building-a-multi-account-walkthrough-system-that-supports-mylanguage-and-pine-strategy-language-59de</guid>
      <description>&lt;h2&gt;
  
  
  Demand Scenarios of Walkthrough System
&lt;/h2&gt;

&lt;p&gt;I was often asked this question in the FMZ platform community and in private communication with users:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why can strategies written in MyLanguage or Pine scripts always only control one account and one product?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The essence of this problem lies in the design positioning of the language itself. MyLanguage and Pine language are highly encapsulated scripting languages, and their underlying implementation is based on JavaScript. In order to allow users to quickly get started and focus on strategy logic, both have done a lot of encapsulation and abstraction at the language level, but this has also sacrificed a certain degree of flexibility: by default, only single-account, single-product strategy execution models are supported.&lt;/p&gt;

&lt;p&gt;When users want to run multiple accounts in live trading, they can only do so by running multiple Pine or MyLanguage live trading instances. This approach is acceptable when the number of accounts is small, but if multiple instances are deployed on the same docker, a large number of API requests will be generated, and the exchange may even restrict access due to excessive request frequency, bringing unnecessary live trading risks.&lt;/p&gt;

&lt;p&gt;So, is there a more elegant way to copy the trading behavior of other accounts automatically by just running a Pine or MyLanguage script?&lt;/p&gt;

&lt;p&gt;The answer is: Yes.&lt;/p&gt;

&lt;p&gt;This article will guide you through building a cross-account, cross-product walkthrough system from scratch, which is compatible with My and Pine language strategies. Through the Leader-Subscriber architecture, it will implement an efficient, stable, and scalable multi-account synchronous trading framework to solve the various disadvantages you encounter in live trading deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy Design
&lt;/h2&gt;

&lt;p&gt;The program is designed and written in JavaScript, and the program architecture uses the Leader-Subscriber model.&lt;/p&gt;

&lt;p&gt;Strategy source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*backtest
start: 2024-05-21 00:00:00
end: 2025-05-20 00:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"},{"eid":"Futures_Binance","currency":"ETH_USDT"},{"eid":"Futures_Binance","currency":"ETH_USDT","balance":10000}]
args: [["isBacktest",true]]
*/

class Leader {
    constructor(leaderCfg = { type: "exchange", apiClient: exchanges[0] }) {
        // Account managed with trading signals configuration
        this.leaderCfg = leaderCfg
        // Cache the last position information for comparison
        this.lastPos = null
        // Record current position information
        this.currentPos = null
        // Record subscribers
        this.subscribers = []

        // According to the leaderCfg configuration, determine which monitoring solution to use: 1. Monitor the account managed with trading signals directly. 2. Monitor the data of the live trading strategy of the account managed with trading signals through the FMZ extended API. 3. Walkthrough through the message push mechanism. The default solution is 1.

        // initialization
        let ex = this.leaderCfg.apiClient
        let currency = ex.GetCurrency()
        let arrCurrency = currency.split("_")
        if (arrCurrency.length !== 2) {
            throw new Error("The account managed with trading signals configuration is wrong, it must be two currency pairs")
        }
        this.baseCurrency = arrCurrency[0]
        this.quoteCurrency = arrCurrency[1]
        // Get the total equity of the account managed with trading signals at initialization
        this.initEquity = _C(ex.GetAccount).Equity
        this.currentPos = _C(ex.GetPositions)
    }

    // Monitoring leader logic
    poll() {
        // Get the exchange object
        let ex = this.leaderCfg.apiClient

        // Get the leader's position and account asset data
        let pos = ex.GetPositions()
        if (!pos) {
            return
        }
        this.currentPos = pos

        // Call the judgment method to determine the position changes
        let { hasChanged, diff } = this._hasChanged(pos)
        if (hasChanged) {
            Log("Leader position changes, current position:", pos)
            Log("Position changes:", diff)
            // Notify Subscribers
            this.subscribers.forEach(subscriber =&amp;gt; {
                subscriber.applyPosChanges(diff)
            })
        }

        // Synchronous positions
        this.subscribers.forEach(subscriber =&amp;gt; {
            subscriber.syncPositions(pos)
        })
    }

    // Determine whether the position has changed
    _hasChanged(pos) {
        if (this.lastPos) {
            // Used to store the results of position differences
            let diff = {
                added: [],    // Newly added positions
                removed: [],  // Removed positions
                updated: []   // Updated positions (amount or price changes)
            }

            // Convert the last position and the current position into a Map, with the key being `symbol + direction` and the value being the position object
            let lastPosMap = new Map(this.lastPos.map(p =&amp;gt; [`${p.Symbol}|${p.Type}`, p]))
            let currentPosMap = new Map(pos.map(p =&amp;gt; [`${p.Symbol}|${p.Type}`, p]))

            // Traverse the current positions and find new and updated positions
            for (let [key, current] of currentPosMap) {
                if (!lastPosMap.has(key)) {
                    // If the key does not exist in the last position, it is a new position.
                    diff.added.push({ symbol: current.Symbol, type: current.Type, deltaAmount: current.Amount })
                } else {
                    // If it exists, check if the amount or price has changed
                    let last = lastPosMap.get(key)
                    if (current.Amount !== last.Amount) {
                        diff.updated.push({ symbol: current.Symbol, type: current.Type, deltaAmount: current.Amount - last.Amount })
                    }
                    // Remove from lastPosMap, and what remains is the removed position
                    lastPosMap.delete(key)
                }
            }

            // The remaining keys in lastPosMap are the removed positions.
            for (let [key, last] of lastPosMap) {
                diff.removed.push({ symbol: last.Symbol, type: last.Type, deltaAmount: -last.Amount })
            }

            // Determine if there is a change
            let hasChanged = diff.added.length &amp;gt; 0 || diff.removed.length &amp;gt; 0 || diff.updated.length &amp;gt; 0

            // If there is a change, update lastPos
            if (hasChanged) {
                this.lastPos = pos
            }

            return { hasChanged: hasChanged, diff: diff }
        } else {
            // If there is no last position record, update the record and do not synchronize positions
            this.lastPos = pos
            return { hasChanged: false, diff: { added: [], removed: [], updated: [] } }

            /* Another solution: synchronize positions
            if (pos.length &amp;gt; 0) {
                let diff = {
                    added: pos.map(p =&amp;gt; ({symbol: p.Symbol, type: p.Type, deltaAmount: p.Amount})),
                    removed: [],
                    updated: []
                }
                return {hasChanged: true, diff: diff}
            } else {
                return {hasChanged: false, diff: {added: [], removed: [], updated: []}}
            }
            */
        }
    }

    // Subscriber registration
    subscribe(subscriber) {
        if (this.subscribers.indexOf(subscriber) === -1) {
            if (this.quoteCurrency !== subscriber.quoteCurrency) {
                throw new Error("Subscriber currency pair does not match, current leader currency pair: " + this.quoteCurrency + ", subscriber currency pair:" + subscriber.quoteCurrency)
            }
            if (subscriber.followStrategy.followMode === "equity_ratio") {
                // Set the ratio of account managed with trading signals
                let ex = this.leaderCfg.apiClient
                let equity = _C(ex.GetAccount).Equity
                subscriber.setEquityRatio(equity)
            }
            this.subscribers.push(subscriber)
            Log("Subscriber registration is successful, subscription configuration:", subscriber.getApiClientInfo())
        }
    }

    // Unsubscribe
    unsubscribe(subscriber) {
        const index = this.subscribers.indexOf(subscriber)
        if (index !== -1) {
            this.subscribers.splice(index, 1)
            Log("Subscriber unregistration successful, subscription configuration:", subscriber.getApiClientInfo())
        } else {
            Log("Subscriber unregistration failed, subscription configuration:", subscriber.getApiClientInfo())
        }
    }

    // Get UI information
    fetchLeaderUI() {
        // Information of the trade order issuer
        let tblLeaderInfo = { "type": "table", "title": "Leader Info", "cols": ["order trade plan", "denominated currency", "number of walkthrough followers", "initial total equity"], "rows": [] }
        tblLeaderInfo.rows.push([this.leaderCfg.type, this.quoteCurrency, this.subscribers.length, this.initEquity])

        // Construct the display information of the trade order issuer: position information
        let tblLeaderPos = { "type": "table", "title": "Leader pos", "cols": ["trading product", "direction", "amount", "price"], "rows": [] }
        this.currentPos.forEach(pos =&amp;gt; {
            let row = [pos.Symbol, pos.Type == PD_LONG ? "long" : "short", pos.Amount, pos.Price]
            tblLeaderPos.rows.push(row)
        })

        // Construct the display information of the subscriber
        let strFollowerMsg = ""
        this.subscribers.forEach(subscriber =&amp;gt; {
            let arrTbl = subscriber.fetchFollowerUI()
            strFollowerMsg += "`" + JSON.stringify(arrTbl) + "`\n"
        })

        return "`" + JSON.stringify([tblLeaderInfo, tblLeaderPos]) + "`\n" + strFollowerMsg
    }

    // Expand functions such as pausing walkthrough trading and removing subscriptions
}

class Subscriber {
    constructor(subscriberCfg, followStrategy = { followMode: "position_ratio", ratio: 1, maxReTries: 3 }) {
        this.subscriberCfg = subscriberCfg
        this.followStrategy = followStrategy

        // initialization
        let ex = this.subscriberCfg.apiClient
        let currency = ex.GetCurrency()
        let arrCurrency = currency.split("_")
        if (arrCurrency.length !== 2) {
            throw new Error("Subscriber configuration error, must be two currency pairs")
        }
        this.baseCurrency = arrCurrency[0]
        this.quoteCurrency = arrCurrency[1]

        // Initial acquisition of position data
        this.currentPos = _C(ex.GetPositions)
    }

    setEquityRatio(leaderEquity) {
        // {followMode: "equity_ratio"} Automatically follow orders based on account equity ratio
        if (this.followStrategy.followMode === "equity_ratio") {
            let ex = this.subscriberCfg.apiClient
            let equity = _C(ex.GetAccount).Equity
            let ratio = equity / leaderEquity
            this.followStrategy.ratio = ratio
            Log("Rights and interests of the trade order issuer:", leaderEquity, "Subscriber benefits:", equity)
            Log("Automatic setting, subscriber equity ratio:", ratio)
        }
    }

    // Get the API client information bound to the subscriber
    getApiClientInfo() {
        let ex = this.subscriberCfg.apiClient
        let idx = this.subscriberCfg.clientIdx
        if (ex) {
            return { exName: ex.GetName(), exLabel: ex.GetLabel(), exIdx: idx, followStrategy: this.followStrategy }
        } else {
            throw new Error("The subscriber is not bound to the API client")
        }
    }

    // Returns the transaction direction parameters according to the position type and position changes
    getTradeSide(type, deltaAmount) {
        if (type == PD_LONG &amp;amp;&amp;amp; deltaAmount &amp;gt; 0) {
            return "buy"
        } else if (type == PD_LONG &amp;amp;&amp;amp; deltaAmount &amp;lt; 0) {
            return "closebuy"
        } else if (type == PD_SHORT &amp;amp;&amp;amp; deltaAmount &amp;gt; 0) {
            return "sell"
        } else if (type == PD_SHORT &amp;amp;&amp;amp; deltaAmount &amp;lt; 0) {
            return "closesell"
        }

        return null
    }

    getSymbolPosAmount(symbol, type) {
        let ex = this.subscriberCfg.apiClient
        if (ex) {
            let pos = _C(ex.GetPositions, symbol)
            if (pos.length &amp;gt; 0) {
                // Traverse the positions and find the corresponding symbol and type
                for (let i = 0; i &amp;lt; pos.length; i++) {
                    if (pos[i].Symbol === symbol &amp;amp;&amp;amp; pos[i].Type === type) {
                        return pos[i].Amount
                    }
                }
            }
            return 0
        } else {
            throw new Error("The subscriber is not bound to the API client")
        }
    }

    // Retry order
    tryCreateOrder(ex, symbol, side, price, amount, label, maxReTries) {
        for (let i = 0; i &amp;lt; Math.max(maxReTries, 1); i++) {
            let orderId = ex.CreateOrder(symbol, side, price, amount, label)
            if (orderId) {
                return orderId
            }
            Sleep(1000)
        }
        return null
    }

    // Synchronous position changes
    applyPosChanges(diff) {
        let ex = this.subscriberCfg.apiClient
        if (ex) {
            ["added", "removed", "updated"].forEach(key =&amp;gt; {
                diff[key].forEach(item =&amp;gt; {
                    let side = this.getTradeSide(item.type, item.deltaAmount)
                    if (side) {
                        // Calculate the walkthrough trading ratio
                        let ratio = this.followStrategy.ratio
                        let tradeAmount = Math.abs(item.deltaAmount) * ratio
                        if (side == "closebuy" || side == "closesell") {
                            // Get the number of positions to check
                            let posAmount = this.getSymbolPosAmount(item.symbol, item.type)
                            tradeAmount = Math.min(posAmount, tradeAmount)
                        }
                        // Order Id
                        // let orderId = ex.CreateOrder(item.symbol, side, -1, tradeAmount, ex.GetLabel())
                        let orderId = this.tryCreateOrder(ex, item.symbol, side, -1, tradeAmount, ex.GetLabel(), this.followStrategy.maxReTries)
                        // Check the Order Id
                        if (orderId) {
                            Log("The subscriber successfully placed an order, order ID:", orderId, ", Order direction:", side, ", Order amount:", Math.abs(item.deltaAmount), ", walkthrough order ratio (times):", ratio)
                        } else {
                            Log("Subscriber order failed, order ID: ", orderId, ", order direction: ", side, ", order amount: ", Math.abs(item.deltaAmount), ", walkthrough order ratio (times): ", ratio)
                        }
                    }
                })
            })

            // Update current position
            this.currentPos = _C(ex.GetPositions)
        } else {
            throw new Error("The subscriber is not bound to the API client")
        }
    }

    // Synchronous positions
    syncPositions(leaderPos) {
        let ex = this.subscriberCfg.apiClient
        this.currentPos = _C(ex.GetPositions)

        // Used to store the results of position differences
        let diff = {
            added: [],    // Newly added positions
            removed: [],  // Removed positions
            updated: []   // Updated positions (amount or price changes)
        }
        let leaderPosMap = new Map(leaderPos.map(p =&amp;gt; [`${p.Symbol}|${p.Type}`, p]))
        let currentPosMap = new Map(this.currentPos.map(p =&amp;gt; [`${p.Symbol}|${p.Type}`, p]))
        // Traverse the current positions and find new and updated positions
        for (let [key, leader] of leaderPosMap) {
            if (!currentPosMap.has(key)) {
                diff.added.push({ symbol: leader.Symbol, type: leader.Type, deltaAmount: leader.Amount })
            } else {
                let current = currentPosMap.get(key)
                if (leader.Amount !== current.Amount) {
                    diff.updated.push({ symbol: leader.Symbol, type: leader.Type, deltaAmount: leader.Amount - current.Amount * this.followStrategy.ratio })
                }
                currentPosMap.delete(key)
            }
        }
        for (let [key, current] of currentPosMap) {
            diff.removed.push({ symbol: current.Symbol, type: current.Type, deltaAmount: -current.Amount * this.followStrategy.ratio })
        }
        // Determine if there is a change
        let hasChanged = diff.added.length &amp;gt; 0 || diff.removed.length &amp;gt; 0 || diff.updated.length &amp;gt; 0
        if (hasChanged) {
            // synchronous
            this.applyPosChanges(diff)
        }
    }

    // Get subscriber UI information
    fetchFollowerUI() {
        // Subscriber information
        let ex = this.subscriberCfg.apiClient
        let equity = _C(ex.GetAccount).Equity
        let exLabel = ex.GetLabel()
        let tblFollowerInfo = { "type": "table", "title": "Follower Info", "cols": ["exchange object index", "exchange object tag", "denominated currency", "walkthrough order mode", "walkthrough order ratio (times)", "maximum retry times", "total equity"], "rows": [] }
        tblFollowerInfo.rows.push([this.subscriberCfg.clientIdx, exLabel, this.quoteCurrency, this.followStrategy.followMode, this.followStrategy.ratio, this.followStrategy.maxReTries, equity])

        // Subscriber position information
        let tblFollowerPos = { "type": "table", "title": "Follower pos", "cols": ["trading product", "direction", "amount", "price"], "rows": [] }
        let pos = this.currentPos
        pos.forEach(p =&amp;gt; {
            let row = [p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Amount, p.Price]
            tblFollowerPos.rows.push(row)
        })

        return [tblFollowerInfo, tblFollowerPos]
    }
}

// Test function, simulate random opening, simulate leader position change
function randomTrade(symbol, amount) {
    let randomNum = Math.random()
    if (randomNum &amp;lt; 0.0001) {
        Log("Simulate order managed with trading signals trading", "#FF0000")
        let ex = exchanges[0]
        let pos = _C(ex.GetPositions)

        if (pos.length &amp;gt; 0) {
            // Random close positions
            let randomPos = pos[Math.floor(Math.random() * pos.length)]
            let tradeAmount = Math.random() &amp;gt; 0.7 ? Math.abs(randomPos.Amount * 0.5) : Math.abs(randomPos.Amount)
            ex.CreateOrder(randomPos.Symbol, randomPos.Type === PD_LONG ? "closebuy" : "closesell", -1, tradeAmount, ex.GetLabel())
        } else {
            let tradeAmount = Math.random() * amount
            let side = Math.random() &amp;gt; 0.5 ? "buy" : "sell"
            if (side === "buy") {
                ex.CreateOrder(symbol, side, -1, tradeAmount, ex.GetLabel())
            } else {
                ex.CreateOrder(symbol, side, -1, tradeAmount, ex.GetLabel())
            }
        }
    }
}

// Strategy main loop
function main() {
    let leader = new Leader()

    let followStrategyArr = JSON.parse(strFollowStrategyArr)
    if (followStrategyArr.length &amp;gt; 0 &amp;amp;&amp;amp; followStrategyArr.length !== exchanges.length - 1) {
        throw new Error("Walkthrough trading strategy configuration error, walkthrough trading strategy amount and exchange amount do not match")
    }

    for (let i = 1; i &amp;lt; exchanges.length; i++) {
        let subscriber = null
        if (followStrategyArr.length == 0) {
            subscriber = new Subscriber({ apiClient: exchanges[i], clientIdx: i })
        } else {
            let followStrategy = followStrategyArr[i - 1]
            subscriber = new Subscriber({ apiClient: exchanges[i], clientIdx: i }, followStrategy)
        }
        leader.subscribe(subscriber)
    }

    // Start monitoring
    while (true) {
        leader.poll()
        Sleep(1000 * pollInterval)

        // Simulate random transactions
        if (IsVirtual() &amp;amp;&amp;amp; isBacktest) {
            randomTrade("BTC_USDT.swap", 0.001)
            randomTrade("ETH_USDT.swap", 0.02)
            randomTrade("SOL_USDT.swap", 0.1)
        }

        LogStatus(_D(), "\n", leader.fetchLeaderUI())
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Design pattern
Previously, we have designed several walkthrough trading strategies on the platform, using process-oriented design. This article is a new design attempt, using object-oriented style and observer design pattern.&lt;/li&gt;
&lt;li&gt;Monitoring plan
The essence of walkthrough trading is a monitoring behavior, monitoring the target's actions and replicating them when new actions are found.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, only one solution is implemented: configure the exchange object through API KEY, and monitor the position of the target account. In fact, there are two other solutions that can be used, which may be more complicated in design:&lt;/p&gt;

&lt;p&gt;Extended API through FMZ&lt;br&gt;
Monitor the log and status bar information of the target live trading, and operate and walkthrough orders according to the changes. The advantage of using this solution is that it can reduce API requests effectively.&lt;/p&gt;

&lt;p&gt;Rely on the message push of the target live trading&lt;br&gt;
You can turn on the message push of the target live trading on FMZ, and a message will be pushed when the target live trading has an order operation. The walkthrough trading strategy receives these messages and takes action. The advantages of using this solution are: reducing API requests and changing from a request polling mechanism to an event-driven mechanism.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walkthrough trading strategy
There may be multiple requirements for walkthrough trading strategies, and the strategy framework is designed to be as easy to expand as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Position replication:&lt;br&gt;
Positions can be replicated 1:1, or they can be scaled according to specified scaling parameters.&lt;/p&gt;

&lt;p&gt;Equity ratio&lt;br&gt;
The equity ratio of the account managed with trading signals and the walkthrough account can be automatically used as a scaling parameter for walkthrough trading.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Position synchronization
In actual use, there may be various reasons that cause the positions of the trade order issuer and the walkthrough trading follower to differ. You can design a system to detect the difference between the walkthrough trading account and the trade order issuer account when walkthrough orders, and synchronize the positions automatically.&lt;/li&gt;
&lt;li&gt;Order retry
You can specify the specific number of failed order retries in the walkthrough trading strategy.&lt;/li&gt;
&lt;li&gt;Backtest random test
The function randomTrade(symbol, amount) function in the code is used for random position opening test during backtesting to detect the walkthrough trading effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Strategy Backtesting and Verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttkz8pku420b7driiv4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttkz8pku420b7driiv4i.png" alt=" " width="800" height="283"&gt;&lt;/a&gt;&lt;br&gt;
According to the first exchange object added (trade order issuer), the subsequent exchange objects added walkthrough order (walkthrough order follower).&lt;/p&gt;

&lt;p&gt;In the test, three products are used to place orders randomly to verify the demand for multi-product order following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;randomTrade("BTC_USDT.swap", 0.001)
randomTrade("ETH_USDT.swap", 0.02)
randomTrade("SOL_USDT.swap", 0.1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Extension and Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expand the monitoring scheme for data such as positions and account information.&lt;/li&gt;
&lt;li&gt;Increase control over subscribers, and add functions such as pausing and unsubscribing of walkthrough trading accounts.&lt;/li&gt;
&lt;li&gt;Dynamically update walkthrough trading strategy parameters.&lt;/li&gt;
&lt;li&gt;Increase and expand richer walkthrough trading data and information display.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  END
&lt;/h2&gt;

&lt;p&gt;This article is just a starting point. It uses object-oriented style and observer mode to design a preliminary walkthrough trading strategy framework. I hope it can provide reference and inspiration for readers. Thank you for your reading and support.&lt;/p&gt;

</description>
      <category>strategy</category>
      <category>pinelanguage</category>
      <category>code</category>
      <category>backtest</category>
    </item>
  </channel>
</rss>
