<?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>DEV Community: Dream</title>
    <description>The latest articles on DEV Community by Dream (@quant001).</description>
    <link>https://dev.to/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>DEV Community: Dream</title>
      <link>https://dev.to/quant001</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quant001"/>
    <language>en</language>
    <item>
      <title>SpaceX Is About to IPO — How Can Ordinary Retail Investors Get In? The Crypto World Already Left You a Ticket</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Tue, 02 Jun 2026 01:18:14 +0000</pubDate>
      <link>https://dev.to/quant001/spacex-is-about-to-ipo-how-can-ordinary-retail-investors-get-in-the-crypto-world-already-left-3om9</link>
      <guid>https://dev.to/quant001/spacex-is-about-to-ipo-how-can-ordinary-retail-investors-get-in-the-crypto-world-already-left-3om9</guid>
      <description>&lt;p&gt;&lt;em&gt;Posted in the Submissions section · Created 2026-05-21 · Updated 2026-05-27 · FMZ Quant&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Biggest IPO Wave in History — Are Retail Investors Just Left Watching?
&lt;/h2&gt;

&lt;p&gt;In 2026, the tech capital markets are riding an unprecedented wave of AI giants going public. Three companies, three narratives, with a combined valuation approaching $4 trillion.&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%2Ftaiei2f0hl8m6iso7xs9.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%2Ftaiei2f0hl8m6iso7xs9.png" alt=" " width="800" height="334"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;SpaceX:&lt;/strong&gt;&lt;br&gt;
On April 2, 2026, SpaceX confidentially filed its IPO application with the SEC, targeting a $1.75 trillion valuation and aiming to raise up to $75 billion — potentially the largest IPO in human history, surpassing Saudi Aramco's prior record. The roadshow is set to begin June 5, ticker symbol SPCX, with trading expected to start June 12. Having merged with Musk's AI company xAI, SpaceX is now a "space + AI" dual-engine super-giant. Starlink's full-year 2025 revenue topped $11.3 billion, growing nearly 50% year-over-year, with over 10.3 million subscribers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI:&lt;/strong&gt;&lt;br&gt;
Just today (May 21, 2026), it was confirmed that OpenAI will file its draft IPO prospectus within the week at the earliest, targeting a September 2026 listing. Its current valuation has reached $852 billion, and this funding round of $122 billion sets a new global record for a single startup funding round, co-led by SoftBank, Amazon, and NVIDIA. ChatGPT became the fastest product ever to break 100 million users, and AI infrastructure is becoming the "water and electricity" of a new era.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anthropic:&lt;/strong&gt;&lt;br&gt;
The most underrated of the bunch. In February 2026, Anthropic closed its Series G, raising $30 billion at a $380 billion valuation, led by Singapore's sovereign fund GIC and Coatue. Yet in the on-chain Pre-IPO market, investors' implied valuation has soared to $1.2 trillion — a 3x premium over the primary market. Driving this frenzy is the explosive spread of Claude Code among developers: Anthropic's annualized revenue jumped from $9 billion at the end of 2025 to $30 billion by May 2026 — tripling in three months. The market currently expects its IPO to land as early as October 2026.&lt;/p&gt;

&lt;p&gt;Three companies — one is the gateway to AI compute, one is humanity's interstellar exit, and one is the research institution closest to the AGI safety frontier. Standing in 2026, "AI is the primary productive force" is already global consensus, and these three targets represent almost the entirety of "the future" itself.&lt;/p&gt;

&lt;p&gt;But here's the problem:&lt;/p&gt;

&lt;p&gt;Opening a U.S. brokerage account is a tedious process — passport, bank statements, overseas accounts…&lt;br&gt;
The vast majority of IPO subscription allocations are locked up by institutions and VIP investors&lt;br&gt;
Even if you can buy in, SpaceX is priced from $420 per share (at current secondary-market prices)&lt;br&gt;
Most importantly — you simply can't grab any first-day shares&lt;br&gt;
So is there any way for retail investors to participate?&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Crypto Got There First: Pre-IPO Contracts Are Already Live
&lt;/h2&gt;

&lt;p&gt;The answer is: of course there is.&lt;/p&gt;

&lt;p&gt;Crypto markets have always had a keen nose. Within a week of SpaceX filing its IPO application, the four major exchanges — Binance, OKX, Bitget, and BingX — successively launched SpaceX Pre-IPO product lines, putting targets that used to be reachable only by institutions and VIPs directly in front of every ordinary user.&lt;/p&gt;

&lt;p&gt;There are currently three main ways to participate:&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%2Fjt6r183hbmn8q6nl7xbf.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%2Fjt6r183hbmn8q6nl7xbf.png" alt=" " width="800" height="136"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Important note:&lt;/strong&gt; None of the above products are real equity — they carry no voting rights, no dividend rights, and no shareholder status. OKX's perpetual contract, for example, is priced at "one-billionth of SpaceX's total valuation," provisionally assuming 1 billion shares as the estimated total share count; once the S-1 officially discloses the actual share count, a Rebase (share adjustment) will be executed.&lt;/p&gt;

&lt;p&gt;SpaceX is far from the only case. Behind this wave of Pre-IPO mania is a microcosm of the crypto market's broader RWA (Real-World Asset tokenization) explosion. Tech unicorn targets already live or in the pipeline include:&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%2F1qfi9mj2fhwsx6akzgx0.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%2F1qfi9mj2fhwsx6akzgx0.png" alt=" " width="798" height="155"&gt;&lt;/a&gt;&lt;br&gt;
The on-chain perpetuals market is expanding rapidly — Hyperliquid's RWA open interest has broken through a record $2.5 billion high. This sector's boom is only just beginning.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. The Logic of "Sipping the Broth": Enter the Grid Strategy
&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%2F63q6i53h3dd7kkfuwbbf.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%2F63q6i53h3dd7kkfuwbbf.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
Of course, going all-in on a single Pre-IPO product is no different from buying a lottery ticket — violent volatility, limited liquidity, and news that's half-true, half-false.&lt;/p&gt;

&lt;p&gt;Capturing the full rally is hard for retail investors. But using a quant strategy to continuously harvest the spread amid the chop is a different matter entirely.&lt;/p&gt;

&lt;p&gt;That brings us to today's protagonist: &lt;strong&gt;the grid strategy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some say the grid strategy is too old. True — it isn't new. But in crypto markets, "old" is precisely a compliment. Having survived countless bull-bear cycles and extreme conditions, it remains one of the most battle-tested quant strategies and one of the best suited to high-volatility instruments. A tool isn't worse for being old — what matters is that it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 What Is a Grid Strategy?&lt;/strong&gt;&lt;br&gt;
The core idea of a grid strategy is extremely simple: slice a price range into a number of cells, buy at the bottom of each cell and sell at the top, repeating the cycle to earn the spread.&lt;/p&gt;

&lt;p&gt;Take SPACEX_USDT as an example. Suppose we judge the price will oscillate between 2100 and 3000:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upper bound: 3000
├── Cell 4: 2820 ~ 3000 ← buy@2820, sell@3000
├── Cell 3: 2640 ~ 2820 ← buy@2640, sell@2820
├── Cell 2: 2460 ~ 2640 ← buy@2460, sell@2640
├── Cell 1: 2280 ~ 2460 ← buy@2280, sell@2460
└── Cell 0: 2100 ~ 2280 ← buy@2100, sell@2280
Lower bound: 2100

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

&lt;/div&gt;



&lt;p&gt;Each time the price rises from the bottom of a cell to its top, it completes one "buy low, sell high," locking in one cell's profit. The more frequently the price oscillates, the more times you profit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 This Strategy's Core Upgrade: Dynamic Moving Grid&lt;/strong&gt;&lt;br&gt;
A static grid has a fatal flaw: once price breaks out of the range, the strategy is dead.&lt;/p&gt;

&lt;p&gt;This article uses the V4 dynamic moving version, which solves that pain point:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;① Percentage parameters, adaptable to any price magnitude&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Range width, shift amount, and trigger offset all use percentages
# GRID_WIDTH_PCT = 30 → range width = current price × 30%
# SHIFT_STEP_PCT → each shift = current price × x%
# BREAKOUT_TRIGGER_PCT → trigger offset = reference price × x%
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pct_to_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref_price&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ref_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This means whether SPACEX is 500 or 5000, the parameters don't need to change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;② Automatically moves the range after a breakout&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_breakout_and_shift&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_price&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ref_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;range_low&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;range_high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
    &lt;span class="n"&gt;trigger_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pct_to_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BREAKOUT_TRIGGER_PCT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Upper-bound breakout → shift range up
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;range_high&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;trigger_offset&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;_do_shift_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Lower-bound breakout → shift range down
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;range_low&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;trigger_offset&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;_do_shift_down_auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;When price breaks through the upper bound (e.g., SpaceX surges), the strategy automatically:&lt;/p&gt;

&lt;p&gt;Cancels all open orders&lt;br&gt;
Market-closes all positions&lt;br&gt;
Computes a new range&lt;br&gt;
Re-deploys the grid&lt;br&gt;
It doesn't miss trending moves, nor does it stubbornly hold onto losses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;③ Three directional modes for flexibility&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;direction = "long"  → only place buy orders below price (go long)
direction = "short" → only place sell orders above price (go short)
direction = "both"  → split at the range midline: lower half long, upper half short

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

&lt;/div&gt;



&lt;p&gt;Too many cells: each cell's profit can't cover the fees — wasted effort. Too few cells: capital sits idle. Dynamic calculation makes the cell count just right.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.3 Strategy Parameter Configuration Reference
&lt;/h2&gt;

&lt;p&gt;The strategy has the following core parameters, all configurable directly in the parameter panel on the FMZ platform:&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%2F2rg8oosv8vw43g77nebt.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%2F2rg8oosv8vw43g77nebt.png" alt=" " width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Why Are Pre-IPO Contracts Well Suited to Grids?
&lt;/h2&gt;

&lt;p&gt;Grid strategies love three kinds of market environments: high volatility, wide-range oscillation, and a trend that isn't a one-directional ramp.&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%2Fb3mdilbavt07m94ht7eu.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%2Fb3mdilbavt07m94ht7eu.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
SpaceX Pre-IPO contracts happen to satisfy all three:&lt;/p&gt;

&lt;p&gt;High volatility: Contract pricing is driven entirely by market sentiment and valuation expectations, with strong news-driven moves — a single roadshow headline can swing the price ±20%&lt;br&gt;
Oscillation-dominated: Plenty of uncertainty exists before the IPO (pricing, window, market conditions), so price won't ramp in one direction&lt;br&gt;
Bullish long-term: The big AI + space narrative isn't going away — every pullback is a chance for the grid to add positions&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Things to Note (Must Read)
&lt;/h2&gt;

&lt;p&gt;⚠️ Everything described here is a quant-strategy discussion and does not constitute any investment advice.&lt;/p&gt;

&lt;p&gt;Before actually operating, be sure to understand the following risks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The product is essentially a synthetic derivative.&lt;/strong&gt; What you hold is not real SpaceX stock and carries no shareholder rights. The price may trade at a significant premium or discount to the real share price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IPO failure risk.&lt;/strong&gt; If the SpaceX IPO is ultimately delayed or canceled, the platform reserves the right to settle at a self-determined price or delist the product, at which point liquidity could shrink sharply.&lt;/p&gt;

&lt;p&gt;Limitations of the grid strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-directional surge: **the range shifts up frequently, and each move requires closing and rebuilding, losing held-position profit&lt;br&gt;
**One-directional crash:&lt;/strong&gt; after shifting the range down, positions can sink ever deeper — strictly control total position size&lt;br&gt;
&lt;strong&gt;Recommendation:&lt;/strong&gt; keep the grid strategy's total position under 30% of total assets, leaving ample buffer&lt;br&gt;
Contract leverage risk. Pre-IPO contracts are extremely volatile on their own — keep leverage at 2~3x. Better to earn less than to get liquidated.&lt;/p&gt;

&lt;p&gt;Parameters need tuning against live trading. Backtest environments differ from live trading in slippage, depth, etc. Start GRID_WIDTH_PCT with a wide 30% range to run stably, then narrow it gradually.&lt;/p&gt;

&lt;p&gt;**Binance algorithmic trading requires manually enabling authorization. **Before using algorithmic trading (API auto-ordering) on Binance, you must find the "algorithmic trading" option in account settings and manually click to agree to the corresponding service agreement or authorization option — otherwise API order requests will be rejected. Confirm this step is complete before connecting the strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Summary
&lt;/h2&gt;

&lt;p&gt;The listings of SpaceX and OpenAI symbolize that the value of the AI era's most core assets is now officially being priced by public markets. While retail investors find it hard to grab the most-prioritized shares during the IPO window, the crypto market's Pre-IPO contracts already offer an entry point.&lt;/p&gt;

&lt;p&gt;Within that entry point, the goal isn't to guess up or down, but to use the grid strategy to continuously harvest the spread amid the chop — and that's the posture a quant practitioner should have.&lt;/p&gt;

&lt;p&gt;The big trend is irreversible; "AI is the primary productive force" is consensus. But before the trend becomes clear, oscillation is the norm. The grid — this plain, unassuming old strategy — will always have its place in the high-volatility crypto market.&lt;/p&gt;

&lt;p&gt;Disclaimer: This article is for strategy research and technical sharing only and does not constitute investment advice. Crypto derivatives trading is extremely high-risk; make independent judgments based on your own situation only after fully understanding the risks.&lt;/p&gt;

&lt;p&gt;Strategy source code: Universal Grid Strategy (Dynamic Moving Version)&lt;/p&gt;

</description>
      <category>strategy</category>
      <category>crypto</category>
      <category>ipo</category>
      <category>spacex</category>
    </item>
    <item>
      <title>TradFi Pairs Are Live: An Adaptive Grid Strategy</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Tue, 19 May 2026 08:03:37 +0000</pubDate>
      <link>https://dev.to/quant001/tradfi-pairs-are-live-an-adaptive-grid-strategy-1lb6</link>
      <guid>https://dev.to/quant001/tradfi-pairs-are-live-an-adaptive-grid-strategy-1lb6</guid>
      <description>&lt;h2&gt;
  
  
  Preface: Why Skip Crypto and Trade TradFi?
&lt;/h2&gt;

&lt;p&gt;Anyone who has run a grid strategy on crypto knows the same nightmare: the grid is set up, the price tanks, every level fills, and you're either chasing margin or getting liquidated outright. Crypto's charm is that prices can move without an upper bound — and that is exactly what kills grid strategies. Grids are built for chop; one-way moves destroy them.&lt;/p&gt;

&lt;p&gt;Is there a class of assets that still gives you enough intraday wiggle to keep the grid firing, but doesn't routinely throw 30% or 50% melt-ups or stampedes at you? Yes — TradFi instruments.&lt;/p&gt;

&lt;p&gt;TradFi (traditional finance) derivatives include perpetual contracts on classic assets: the S&amp;amp;P 500, Nasdaq, gold, crude oil, FX, and so on. Each one is anchored by real fundamentals. Equity indices are constrained by corporate earnings and Fed policy. Commodities are driven by supply and demand. FX rates are set by the relative dynamics between two sovereign economies. These assets don't 5× overnight for no reason, and they don't crater 80% over a tweet. Their prices have gravity — they can chop in the short term, but they revert to fundamentals in the long term.&lt;/p&gt;

&lt;p&gt;That property is a near-perfect fit for grid strategies: 1%–3% of normal intraday range, enough to repeatedly trip the cells; and even in extreme moves, the grid doesn't get blown out entirely, leaving room for stops and capital management. This strategy is built on exactly that logic — it scans every TradFi pair available, picks the most actively oscillating ones, runs circulating grids on each, and automatically rotates between pairs as the volatility structure shifts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background: Crypto Exchanges Have Quietly Listed a New Asset Class
&lt;/h2&gt;

&lt;p&gt;Over the past two years, top crypto exchanges like OKX and Bitget have quietly listed a large batch of TradFi perpetual contracts. The coverage spans US equity indices (S&amp;amp;P 500, Nasdaq 100), individual stocks (Apple, Nvidia, Tesla), commodities (gold, oil, natural gas), and FX (EUR, JPY). In plain English: you can now trade US stocks, gold, and FX from a crypto exchange account, 24/7, with leverage.&lt;/p&gt;

&lt;p&gt;This matters for quants. On one hand, these instruments inherit the fundamental anchoring of traditional finance — prices don't moonshot or capitulate without reason. On the other hand, they're listed as perpetual contracts on a crypto exchange, which means familiar trading mechanics, deep liquidity, transparent fees, and an API surface identical to regular crypto pairs. They drop right into any existing quant stack.&lt;/p&gt;

&lt;p&gt;In other words, this opens a new arbitrage surface: use crypto-native infrastructure to harvest oscillation on traditional finance assets. This strategy is purpose-built for that scenario — let the program pick the most volatile TradFi names, run grids on them, and eat the chop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Selection Logic: Trade Only the Most Volatile Pairs&lt;/strong&gt;&lt;br&gt;
Whether a grid makes money is 60% determined by what you put it on. Pick the right pair and the cells trip a dozen times a day and profit compounds naturally. Pick the wrong one and the cells sit untouched for a week, locking up margin for nothing.&lt;/p&gt;

&lt;p&gt;The strategy's selection criterion has one dimension only: average daily range over the past N daily bars.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;volatility score = Σ [ (High_i − Low_i) / Close_i × 100 ] / N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code-wise the logic is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def score_symbol(info):
    bars = exchange.GetRecords(info["sym"], PERIOD_D1, KLINE_COUNT + 2)
    if not bars or len(bars) &amp;lt; 3:
        return None
    bars = bars[-KLINE_COUNT:]
    atr_pcts = [(b["High"] - b["Low"]) / b["Close"] * 100 for b in bars if b["Close"] &amp;gt; 0]
    avg_atr = sum(atr_pcts) / len(atr_pcts)
    # Avg daily range must be at least 1.5x grid spacing, otherwise drop the symbol
    if avg_atr &amp;lt; GRID_RATIO * 100 * 1.5:
        return None
    return {"sym": info["sym"], "atr": round(avg_atr, 3), "price": bars[-1]["Close"]}

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

&lt;/div&gt;



&lt;p&gt;The strategy periodically scans every TradFi pair, ranks them, and holds positions on the top-N by range. The entry threshold matters: average daily range has to be at least 1.5× the grid spacing, otherwise the price might not cross a single cell in a day. Anything below that is discarded immediately, so capital doesn't sit idle on a dead pair while still consuming margin.&lt;/p&gt;

&lt;p&gt;Identifying which symbols are TradFi requires special handling. On FMZ, TradFi pairs are distinguished from regular crypto via the instCategory field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def scan_tradfi():
    markets = exchange.GetMarkets()
    for sym, mkt in markets.items():
        if not sym.endswith("USDT.swap"):
            continue
        info = mkt.get("Info") or {}
        # instCategory != 1 means it's a TradFi pair
        if int(info.get("instCategory", 1)) == 1:
            continue
        result.append({"sym": sym, "base": base, "cat": cat})

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Grid Structure: Buy Low, Sell High, Rinse, Repeat&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%2Ffdx5gdx2d8hquikqbn5q.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%2Ffdx5gdx2d8hquikqbn5q.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
On each selected pair, the grid is centered on the current price and extended a fixed percentage in each direction, then divided into geometrically-spaced cells. Every cell below current price is seeded with a buy order, waiting for price to come down and fill it.&lt;/p&gt;

&lt;p&gt;The core of grid construction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def build_grid(sym, price):
    low  = price * (1 - LOWER_RANGE)
    high = price * (1 + LOWER_RANGE)
    # geometric spacing
    grids, p = [], low
    while p &amp;lt;= high * 1.001:
        grids.append(round(p, g_states[sym]["pp"]))
        p = p * (1 + GRID_RATIO)

    for i in range(len(grids) - 1):
        buy_p, sell_p = grids[i], grids[i + 1]
        if buy_p &amp;lt; price:
            oid = buy_open(sym, buy_p, GRID_VALUE)  # below current price: place buy
            g["status"] = "pending_buy" if oid else "skip"
        else:
            g["status"] = "above"  # above current price: wait for price to drop

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

&lt;/div&gt;



&lt;p&gt;Grid sync is the strategy's main loop. It checks every cell's order status and reacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def sync(sym):
    for g in grids:
        if g["status"] == "pending_buy":
            s, deal, avgp = check_order(g["buy_oid"])
            if s == "filled":
                # buy filled → immediately place take-profit sell
                oid = sell_close(sym, g["sp"], ct)
                g["status"] = "pending_sell"

        elif g["status"] == "pending_sell":
            s, deal, avgp = check_order(g["sell_oid"])
            if s == "filled":
                # TP filled → log profit, re-place the buy at the same level
                profit = g["ct"] * cv * (avgp - g["fp"])
                g_total_profit += profit
                oid = buy_open(sym, g["bp"], GRID_VALUE)
                g["status"] = "pending_buy"

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

&lt;/div&gt;



&lt;p&gt;The runtime logic is simple: price drops through a cell → buy. Price rises through the next cell up → take profit. After TP, re-seed the buy at the same level. Around and around. Order cancellation, missed TP, and other anomalies all have automatic detection and re-placement, so the strategy doesn't break on the occasional bad fill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Smart Rotation: Always Keep Capital on the Most Active Pairs&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%2Fgv4ppablxeyhwijoiio3.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%2Fgv4ppablxeyhwijoiio3.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
TradFi pairs cycle their volatility regimes with macro events, earnings seasons, and policy shifts. Gold might be the most active name for a stretch; then it rolls over to oil, then to S&amp;amp;P futures. Lock yourself into one pair and eventually it enters a quiet phase and the cells go a full week without firing.&lt;/p&gt;

&lt;p&gt;This strategy re-scores and re-ranks every TradFi pair on a fixed cadence (default 48 hours), then decides whether to swap out a currently-held pair. To avoid churning fees from frequent rotations on marginal differences, it uses a hysteresis mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def needs_rebalance(new_selected):
    cur_scores = {s["sym"]: s["atr"] for s in g_score_log if s["sym"] in g_active}
    for s in new_selected:
        if s["sym"] in g_active:
            continue
        weakest_atr = min(cur_scores.values())
        threshold = weakest_atr * (1 + HYSTERESIS)  # must beat weakest by 20%
        if s["atr"] &amp;gt;= threshold:
            Log(f"{s['base']} ATR={s['atr']:.2f}% &amp;gt; threshold={threshold:.2f}%, rotating")
        else:
            Log(f"{s['base']} ATR={s['atr']:.2f}% &amp;lt; threshold={threshold:.2f}%, hysteresis holds")

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

&lt;/div&gt;



&lt;p&gt;Rotation only triggers when a candidate's average daily range is at least 20% above the weakest currently-held pair. The swap flow: cancel every open order on the outgoing pair, flatten all positions, then rebuild a complete grid on the incoming pair. The whole thing runs unattended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Key Parameters&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;TOP_N&lt;/em&gt; — number of pairs held concurrently. Default 3; capital is spread across the three most volatile names.&lt;br&gt;
&lt;em&gt;GRID_RATIO&lt;/em&gt; — grid spacing as a percentage. Default 1.5%; this is also the per-cell take-profit.&lt;br&gt;
&lt;em&gt;GRID_VALUE&lt;/em&gt; — fixed USDT amount allocated per cell. Default 50. Does not scale with price level.&lt;br&gt;
&lt;em&gt;LOWER_RANGE _— price range the grid covers. Default ±10% around current price.&lt;br&gt;
_REBALANCE_HOURS&lt;/em&gt; — rotation evaluation cadence. Default 48 hours.&lt;br&gt;
&lt;em&gt;HYSTERESIS&lt;/em&gt; — rotation threshold. Default 20%, prevents excessive churning.&lt;br&gt;
&lt;em&gt;LEVERAGE&lt;/em&gt; — leverage multiplier. Recommended ≤ 3×.&lt;br&gt;
&lt;em&gt;STOP_LOSS_RATIO&lt;/em&gt; — global stop-loss. When account loss exceeds this fraction of starting equity, auto-flatten and halt. Default 30%.&lt;br&gt;
&lt;em&gt;KLINE_COUNT&lt;/em&gt; — number of daily bars used for scoring. Default 20.&lt;br&gt;
&lt;em&gt;EXCLUDE_SYMBOLS&lt;/em&gt; — blacklist; comma-separated codes the strategy should never touch.&lt;br&gt;
&lt;strong&gt;5. Risk Controls&lt;/strong&gt;&lt;br&gt;
Global stop-loss is the final safety net. When account equity drawdown exceeds the configured fraction of starting equity, the strategy cancels everything, flattens everything, and halts all subsequent action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def check_stop():
    acc = exchange.GetAccount()
    loss = (g_init_equity - acc.Equity) / g_init_equity
    if loss &amp;gt;= STOP_LOSS_RATIO:
        Log(f"Stop-loss triggered! Loss={loss*100:.1f}% → close all and halt")
        for sym in list(g_active):
            close_all(sym)
        g_state = "STOP"

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

&lt;/div&gt;



&lt;p&gt;The selection phase's entry filter screens out low-volatility pairs, so every name that enters the strategy has enough intraday range to actually drive the grid. The blacklist lets you manually exclude pairs with poor liquidity, abnormal spreads, or unstable behavior. All order prices and sizes are strictly aligned to the exchange's precision requirements, eliminating rejected orders at the source. Capital is evenly distributed across pairs, so a loss on one pair doesn't drag the rest of the book down with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Suitable Regimes and Things to Watch&lt;/strong&gt;&lt;br&gt;
The strategy works best in choppy, range-bound markets. When a target pair oscillates inside a band, the cells get hit at high frequency and P&amp;amp;L accumulates linearly with time — almost no manual intervention required.&lt;/p&gt;

&lt;p&gt;A few things to watch:&lt;/p&gt;

&lt;p&gt;If price runs one-way and breaks below the grid's lower bound, every buy fill is stuck holding inventory until price recovers or the global stop triggers.&lt;br&gt;
Some TradFi pairs have sharply reduced liquidity during off-hours (e.g. when US equities are closed). Orders sitting unfilled for long stretches is normal.&lt;br&gt;
GRID_RATIO should be sized relative to the target pair's average daily range — somewhere between 1/3 and 1/2 of the daily range. Too wide and the trigger frequency drops; too tight and fees eat the profit.&lt;br&gt;
Keep leverage at 3× or below. High leverage in an extreme move accelerates losses faster than the stop-loss can react.&lt;/p&gt;

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

&lt;p&gt;The core logic of this strategy can be stated in one sentence: keep capital on the highest-volatility TradFi pairs at all times, and let the grid be friends with time. Selection, grid construction, rotation, and risk control — four modules, chained, fully automated. The fundamental anchoring of TradFi assets guarantees prices don't drift off to infinity, while the programmatic volatility filter keeps capital allocated to the highest-efficiency names. With reasonable parameters, the strategy can produce steady grid returns across most market regimes, while the stop-loss and hysteresis mechanisms keep downside risk inside an acceptable envelope.&lt;/p&gt;

</description>
      <category>tradfi</category>
      <category>strategy</category>
      <category>crypto</category>
      <category>trading</category>
    </item>
    <item>
      <title>Shorting Binance Delisted Perpetuals: A Grid Strategy from Monitoring to Auto-Execution</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Mon, 18 May 2026 09:18:20 +0000</pubDate>
      <link>https://dev.to/quant001/shorting-binance-delisted-perpetuals-a-grid-strategy-from-monitoring-to-auto-execution-27bb</link>
      <guid>https://dev.to/quant001/shorting-binance-delisted-perpetuals-a-grid-strategy-from-monitoring-to-auto-execution-27bb</guid>
      <description>&lt;h2&gt;
  
  
  Important Disclaimers
&lt;/h2&gt;

&lt;p&gt;Before reading and deploying this strategy, please pay attention to the following three points:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The strategy requires patience before it opens its first position&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Binance perpetual delistings are low-frequency events — they don't happen every day. After launching the strategy, you may need to wait several days, or even longer, before the first position is opened. The program spends most of its runtime in a "standby monitoring" state. Be mentally prepared for long idle periods, and don't assume the strategy has failed just because there has been no trading activity in the short term.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. There is still room to optimize the detection timing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article uses a scheme that polls the fapi/v1/exchangeInfo endpoint every 15 seconds and identifies delisting signals via changes in the deliveryDate field. Live testing shows this method has some latency and is not the fastest path. Readers can further optimize the detection mechanism based on their own needs — for example: shorten the polling interval, monitor Binance's announcement API in parallel, subscribe to WebSocket pushes, or cross-validate using multiple signal sources. The earlier you enter, the more of the first sharp drop you can capture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Shut the strategy down in time to prevent profit giveback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Live observation shows that not every delisted token grinds down all the way to the delisting moment. Some tokens drop sharply right after the announcement and then gradually rebound over several hours to a full day, sometimes recovering back to pre-announcement levels. If you don't shut the strategy down in time, the floating profits accumulated earlier will be heavily eaten into by the rebound — and can even flip from gain to loss.&lt;/p&gt;

&lt;p&gt;It is recommended to set any of the following as an active exit signal:&lt;/p&gt;

&lt;p&gt;Liquidate immediately once a preset profit target is reached;&lt;br&gt;
Force-close when the price rebounds beyond a certain percentage (e.g., 20%–30%) above the post-announcement low;&lt;br&gt;
Trigger a profit-protection close when drawdown from peak P&amp;amp;L reaches a certain percentage (e.g., 30%–50%).&lt;br&gt;
&lt;strong&gt;Do not passively wait for the T-60-minute force-close — that is a safety net, not an optimal exit.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In Binance's perpetual market, there is a special type of opportunity that most people overlook — contract delisting events.&lt;/p&gt;

&lt;p&gt;From time to time, Binance issues announcements declaring that certain illiquid or low-volume perpetual contracts will be delisted. The instant the announcement drops, the market reacts swiftly: long holders are forced to close out, panic selling cascades in, and the price typically plunges within minutes. After that comes a long, choppy downtrend that lasts all the way to delisting.&lt;/p&gt;

&lt;p&gt;Take the recent MLNUSDT case as an example:&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%2Fbvx5z1b8l77ip6fo8lkn.jpg" 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%2Fbvx5z1b8l77ip6fo8lkn.jpg" alt=" " width="800" height="302"&gt;&lt;/a&gt;&lt;br&gt;
Within half an hour the price lost nearly a third of its value, and the entire delisting cycle typically lasts several days, during which price keeps oscillating at depressed levels. For a short strategy, this is a natural breeding ground.&lt;/p&gt;

&lt;p&gt;However, trading these opportunities manually has two real difficulties:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, the time-sensitivity is extreme.&lt;/strong&gt; The first 5 minutes after the announcement is the highest-velocity drop window. If you miss the entry, chasing the short later carries significantly more risk. No human can realistically monitor this 24/7.&lt;/p&gt;

&lt;p&gt;**Second, the move is not a clean one-way waterfall. **The price keeps rebounding during the descent. Holding a pure short captures the trend, but you miss out on a large amount of high-frequency mean-reversion profit during those rebounds.&lt;/p&gt;

&lt;p&gt;To address both problems, this article presents a complete automated strategy: &lt;strong&gt;the program watches Binance for delisting signals in real time, immediately opens a short base position the moment an announcement is detected, simultaneously fires up a dynamic short grid to keep harvesting oscillation profits during the overall downtrend, and automatically closes out before delisting — all without manual intervention.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Why This Market Regime Works
&lt;/h2&gt;

&lt;p&gt;Before diving into the strategy, we need to understand the price behavior of delisted tokens — it's the foundation everything else is built on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.1 The Market Shock of a Delisting Announcement&lt;/strong&gt;&lt;br&gt;
When Binance announces that a perpetual is going to be delisted, the market's first reaction is panic. Long holders know the contract is about to disappear and must close out before delisting, otherwise they'll be force-settled. This concentrated selling produces strong short-term sell pressure and drives price down quickly.&lt;/p&gt;

&lt;p&gt;At the same time, market makers widen quotes or pull liquidity, which further amplifies the move. This is why the first few minutes after the announcement consistently see the largest drop of the entire delisting cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2 The Choppy Downtrend Pattern&lt;/strong&gt;&lt;br&gt;
After the initial plunge, the price doesn't drop in a straight line to the bottom. It exhibits a classic choppy downtrend:&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%2Fbldm7hnynvxv2zb0su1w.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%2Fbldm7hnynvxv2zb0su1w.png" alt=" " width="800" height="456"&gt;&lt;/a&gt;&lt;br&gt;
There's an inherent logic to this pattern: each rebound is a short-term trader thinking the drop is overdone and trying to bottom-fish. But the fundamentals haven't changed (the contract is still about to disappear), so the dip-buyers quickly get trapped and the price resumes its decline. Each rebound is lower than the previous one, until liquidity completely dries up just before delisting.&lt;/p&gt;

&lt;p&gt;This kind of &lt;strong&gt;structured oscillation&lt;/strong&gt; is exactly the regime a grid strategy is built for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.3 Two Sources of Returns&lt;/strong&gt;&lt;br&gt;
Based on the analysis above, we can design two independent profit paths:&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%2F2uzs2oex0jliyq7n19g0.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%2F2uzs2oex0jliyq7n19g0.png" alt=" " width="508" height="116"&gt;&lt;/a&gt;&lt;br&gt;
Stacked together, the strategy has solid earning capacity in delisting regimes. Even if a sizable rebound occurs, the grid keeps harvesting spread; and as long as the overall trend is down, the base position keeps producing profit.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The Monitoring Principle: How to Detect a Delisting Signal Instantly
&lt;/h2&gt;

&lt;p&gt;For monitoring contract information, this strategy uses a more direct approach: monitor the Binance perpetual API endpoint for data changes directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1 The Secret of the deliveryDate Field&lt;/strong&gt;&lt;br&gt;
The Binance fapi/v1/exchangeInfo endpoint returns detailed info on every contract, including a field called deliveryDate — the contract's settlement time.&lt;/p&gt;

&lt;p&gt;For perpetual contracts, this field is normally set to a far-future timestamp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4133404800000  →  corresponds to December 31, 2100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is effectively a "never expires" placeholder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight: when Binance decides to delist a perpetual, it updates that contract's deliveryDate to the actual delisting timestamp at the same moment the announcement is published.&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;Normal perpetual:       deliveryDate = 4133404800000 (never expires)
About-to-delist contract: deliveryDate = 1744106400000 (2026-04-08 17:00:00)

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

&lt;/div&gt;



&lt;p&gt;This change is reflected in the API data immediately — faster than the announcement page renders, and in structured form, with no HTML parsing required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 The Monitoring Code&lt;/strong&gt;&lt;br&gt;
Poll the endpoint every 15 seconds and filter for USDT perpetuals whose deliveryDate has been switched to a real timestamp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def fetch_delist_symbols():
    body   = HttpQuery("https://fapi.binance.com/fapi/v1/exchangeInfo")
    data   = json.loads(body)
    now_ms = get_now_ms()
    result = {}
    for s in data.get("symbols", []):
        if not s["symbol"].endswith("USDT"):
            continue
        if s.get("contractType") != "PERPETUAL":
            continue
        dd = s.get("deliveryDate", PERPETUAL_END)
        if dd &amp;lt; PERPETUAL_END and dd &amp;gt; now_ms:
            result[s["symbol"]] = dd
    return result

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

&lt;/div&gt;



&lt;p&gt;Sample return value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "HIPPOUSDT":  1744106400000,   # 2026-04-08 17:00:00
    "OLUSDT":     1744106400000,
    "RLSUSDT":    1744106400000,
    "PUFFERUSDT": 1744106400000,
}

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

&lt;/div&gt;



&lt;p&gt;Note: Live testing shows there is still some latency with this method. You may want to pair it with a faster verification path.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Strategy Architecture
&lt;/h2&gt;

&lt;p&gt;The whole system is split into two modules running in parallel:&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%2Fzdlgk88dez324u0hz1k2.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%2Fzdlgk88dez324u0hz1k2.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
When a new delisting contract is discovered, a separate task object is created for it, holding all the state for that contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task = {
    "symbol":         "HIPPO_USDT",
    "delist_time_ms": 1744106400000,
    "fund_per_task":  250.0,          # allocated capital
    "base_short_qty": 1500000,        # base-position size in contracts
    "range_high":     0.0005287,      # grid upper bound
    "range_low":      0.0004758,      # grid lower bound
    "grid_width":     0.0000529,      # range width (fixed)
    "grids":          [...],          # state of 10 grid cells
    "shift_count":    0,              # number of times the range has shifted
    ...
}

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

&lt;/div&gt;



&lt;p&gt;The task objects for different contracts are fully independent and don't interfere with each other; they run in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Capital Allocation
&lt;/h2&gt;

&lt;p&gt;After discovering N delisting contracts, dynamically split the available balance evenly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Capital per contract = available balance × 80% / N

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

&lt;/div&gt;



&lt;p&gt;20% is reserved as a margin buffer to guard against short-term rebounds triggering liquidation.&lt;/p&gt;

&lt;p&gt;Important detail: as multiple contracts are initialized one after another, the available balance shrinks with each initialization (the base position consumes margin). So you can't compute everyone's allocation in a single pass before the loop — instead, re-query the balance before each initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for idx, (binance_sym, delist_ms) in enumerate(delist_map.items()):
    update_global_account()
    remaining_count = total_new - idx
    available_now   = total_balance - margin_used
    fund_per_task   = available_now * 0.8 / remaining_count
    task = init_task(binance_sym, delist_ms, fund_per_task)

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

&lt;/div&gt;



&lt;p&gt;This way every contract gets a reasonable allocation, and you never end up with later contracts starved of capital because earlier ones consumed too much margin.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Base Position and Grid in Detail
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;5.1 Opening the Base Short&lt;/strong&gt;&lt;br&gt;
The moment the announcement is detected and initialization runs, open a short at market — don't wait for the grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base notional = allocated capital × 50% × leverage

Example: allocated capital = 250 USDT, leverage = 10x
Base notional = 250 × 50% × 10 = 1,250 USDT

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

&lt;/div&gt;



&lt;p&gt;The base position is held throughout, never participating in the grid's open/close cycles. It's only closed when the final force-close before delisting kicks in.&lt;/p&gt;

&lt;p&gt;The base position is the single biggest profit contributor in the strategy — if a delisted token drops 50% from announcement to delisting, the base position captures that full 50% of short P&amp;amp;L (multiplied by leverage).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.2 Setting Up the Short Grid&lt;/strong&gt;&lt;br&gt;
Take the current price as the upper bound, drop 10% below it for the lower bound, and divide into 10 equal grid cells:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example (current price 0.0005287, range width 10%):

Upper bound = 0.0005287
Lower bound = 0.0005287 × (1 - 10%) = 0.0004758
Cell width  = (0.0005287 - 0.0004758) / 10 = 0.0000053

Cell 9: short @ 0.0005287 → cover @ 0.0005234
Cell 8: short @ 0.0005234 → cover @ 0.0005181
Cell 7: short @ 0.0005181 → cover @ 0.0005128
...
Cell 0: short @ 0.0004811 → cover @ 0.0004758

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

&lt;/div&gt;



&lt;p&gt;Capital is split equally per cell, and the logic is simple: when price rebounds to the short entry, the sell order fills and a short is opened; when price drops to the cover price, the buy order fills and the short is closed; that completes one round trip, and a new short entry is placed waiting for the next rebound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.3 The Order-Placement Strategy at Startup&lt;/strong&gt;&lt;br&gt;
At startup, every cell whose short entry price is ≥ the current price gets a live order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current price: 0.0005287

Cell 9: short entry = 0.0005287 ≥ 0.0005287 → place order ✅
Cell 8: short entry = 0.0005234 &amp;lt;  0.0005287 → skip_below (price has already passed it)
Cell 7 and below:                              → skip_below

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

&lt;/div&gt;



&lt;p&gt;The reason to pre-place every cell above the current price is that, in a downtrend, price can spike up unexpectedly at any moment. Having all of them queued in advance guarantees we don't miss a short entry on any rebound.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Dynamic Range Shifting
&lt;/h2&gt;

&lt;p&gt;This is the most critical mechanism in the whole strategy. Price will not stay forever in the initial range — the grid must move with the price to keep harvesting spread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.1 Shifting Down (price breaks below the lower bound)&lt;/strong&gt;&lt;br&gt;
When price breaks below the grid's lower bound, the drop has exceeded the current range's coverage, and the whole range needs to shift down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old range: 0.0004758 ~ 0.0005287
Current price: 0.0004500 (broke below 0.0004758)

Compute new range (shift step = 5%):
shift_step = 0.0004500 × 5% = 0.0000225

New upper = 0.0005287 - 0.0000225 = 0.0005062
New lower = 0.0005062 - 0.0000529 = 0.0004533

New range: 0.0004533 ~ 0.0005062

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.2 Shifting Up (rebound exceeds the upper bound)&lt;/strong&gt;&lt;br&gt;
When the price rebounds above the upper bound, the range shifts up to follow it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old range: 0.0004494 ~ 0.0005023
Current price: 0.0005100 (broke above 0.0005023)

Shift the range up so current price falls inside the new range
New range: 0.0004758 ~ 0.0005287

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

&lt;/div&gt;



&lt;p&gt;This mechanism guarantees the grid always tracks the price no matter how it moves — you never end up in a situation where price has run away from the range and all the cells sit idle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.3 Range Width Stays Constant&lt;/strong&gt;&lt;br&gt;
Each shift only changes the range's position, never its width:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grid_width = round(range_high - range_low, 8)  # fixed at initialization

# when shifting down
range_high = fp(task, range_high - shift_step)
range_low  = fp(task, range_high - grid_width)  # compute from fixed width

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

&lt;/div&gt;



&lt;p&gt;This prevents floating-point drift from gradually making the range wider or narrower over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.4 Position-Protection Mechanism&lt;/strong&gt;&lt;br&gt;
When the range shifts, all open orders are canceled and the grid is rebuilt. But what about cells that already have an open short position waiting to be covered?&lt;/p&gt;

&lt;p&gt;If you simply rebuild, those positions become "orphaned" — there's an open short but no corresponding cover order, leaving you with a naked short fully exposed to rebound risk.&lt;/p&gt;

&lt;p&gt;The solution: &lt;strong&gt;before shifting, sum up the contracts held across all open-short cells; after shifting, place a cover order on the lowest cell of the new range:&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;# sum up holdings before the shift
holding_contracts = sum(
    g.get("sell_contracts", 0)
    for g in task["grids"]
    if g["status"] in ("pending_cover", "holding_no_cover")
)

cancel_all_orders(task)
activate_grids(task, new_high, new_low)

# after the shift, place a cover-protection order on the lowest cell
if holding_contracts &amp;gt; 0:
    _place_grid_cover(task, 0, holding_contracts)

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

&lt;/div&gt;



&lt;p&gt;This way, no matter how the range moves, no open short ever loses its cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Managing Multiple Contracts in Parallel
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;7.1 Handling Newly Discovered Contracts&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%2F71ffoy72u2v0rof9xwyx.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%2F71ffoy72u2v0rof9xwyx.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
Capital released when an old contract finishes its lifecycle re-enters the allocation pool, ensuring new contracts get adequate funding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.2 Lifecycle Management&lt;/strong&gt;&lt;br&gt;
The lifecycle of each contract:&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%2Fm6zvn0jjyynrgn5zkeue.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%2Fm6zvn0jjyynrgn5zkeue.png" alt=" " width="800" height="1126"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  8. A Few Key Details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;8.1 The Precision Trap for Small-Price Tokens&lt;/strong&gt;&lt;br&gt;
For tokens priced in the 0.0003 range like HIPPOUSDT, computing the range shift step hits a precision problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shift_step = 0.0003 × 5% = 0.000015

If PricePrecision = 4 (4 decimal places)
round(0.000015, 4) = 0.0  ← step rounds to zero!
→ the while loop never terminates: infinite loop

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

&lt;/div&gt;



&lt;p&gt;The fix is to skip the precision truncation on the step, and apply a minimum-step floor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shift_step = price * SHIFT_STEP_PCT
min_step   = 10 ** (-PricePrecision)
shift_step = max(shift_step, min_step)  # at least one tick

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8.2 Handling a Failed Base-Short Open&lt;/strong&gt;&lt;br&gt;
The market short can fail due to insufficient funds or a network issue. If it does, you can't proceed to build the grid — otherwise you'd have a naked grid with no base short underneath it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def open_base_short(task):
    oid = place_market_short(task, usdt_amount)
    Sleep(1000)
    amt, _, _ = get_short_position(task)
    task["base_short_qty"] = amt
    if amt &amp;lt;= 0:
        Log(f"Base short failed to open, skipping this contract")
        return False
    return True

# inside init_task
if not open_base_short(task):
    return None  # initialization failed; don't add to tasks

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8.3 Force-Close Timing and Method&lt;/strong&gt;&lt;br&gt;
The choice of T-60 minutes (rather than T-30 minutes, which is when Binance prohibits new positions) leaves a comfortable time window for closing. The closer you get to delisting, the worse the liquidity, and the harder it becomes to close.&lt;/p&gt;

&lt;p&gt;For the close, use a limit order slightly above market rather than a true market order, to avoid getting picked off by predatory matching in low-liquidity conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;buy_p = fp(task, price * 1.005)  # 0.5% above market
exchange.CreateOrder(swapcode, "closesell", buy_p, fc)

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

&lt;/div&gt;



&lt;p&gt;If a close doesn't fill in one go, retry up to 10 times, refetching the latest price each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.4 Bonus: Funding Rate Revenue&lt;/strong&gt;&lt;br&gt;
In a downtrend, the funding rate typically favors shorts (shorts get paid). This is extra income for holding the base position — it doesn't show up in the grid's spread statistics, but it does show up as growth in account equity.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Suggested Parameters
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LEVERAGE          = 10      # leverage; recommend 5-10, don't go too high
GRID_WIDTH_PCT    = 0.10    # 10% range width covers a normal oscillation amplitude
SHIFT_STEP_PCT    = 0.05    # 5% shift step, about half the range width
GRID_COUNT        = 10      # more cells means less capital per cell
BASE_SHORT_RATIO  = 0.5     # 50% base, 40% grid, 10% buffer
FORCE_CLOSE_MINS  = 60      # force-close 60 minutes before delisting
MONITOR_INTERVAL  = 15000   # poll every 15 seconds; balances responsiveness and rate limits

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Choosing the range width:&lt;/strong&gt; a wider range covers a larger oscillation amplitude, but each cell is also wider apart, so harvest frequency drops. Adjust it based on the historical volatility of the specific token. 10% is a reasonable starting point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choosing the cell count:&lt;/strong&gt; more cells means tighter spacing and higher harvest frequency, but smaller capital and smaller P&amp;amp;L per cell. Too many cells, and the per-cell capital can fall below the exchange's minimum order size. 10 cells is a balanced choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Risk Warnings
&lt;/h2&gt;

&lt;p&gt;Before deploying this strategy, you need to be fully aware of the following risks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebound risk:&lt;/strong&gt; if the news has already been priced in ahead of the announcement, you can get a "sell the news" rebound right after the announcement. The base short will be briefly underwater. The grid will still harvest spread on the rebound, but the strategy can experience a short-term drawdown overall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Liquidity risk:&lt;/strong&gt; the closer you get to the delisting time, the worse the contract's liquidity gets — bid-ask widens, slippage on exit increases. The strategy includes a 0.5% price premium and 10 retries to address this, but under extreme conditions some contracts may still not fully close.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False-signal risk:&lt;/strong&gt; in rare cases Binance cancels or postpones a delisting. The strategy would continue to hold the short until manual intervention or the next monitoring update overrides it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High leverage risk:&lt;/strong&gt; delisted tokens are extremely volatile. At 10x leverage, even a 10% rebound can wipe out close to your full position. Adjust leverage to match your risk tolerance and control overall exposure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start/stop in time:&lt;/strong&gt; live trading reveals that some tokens, after a day of choppy decline, gradually recover to pre-announcement levels. You need to shut the strategy down in time when this happens.&lt;/p&gt;

&lt;p&gt;Long runtime: the strategy needs to wait for opportunities — be patient.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Summary
&lt;/h2&gt;

&lt;p&gt;The core value of this strategy is converting an information advantage (detecting the delisting signal first) into a trading advantage (automated execution), and using the base + grid dual structure to capture both trend return and oscillation spread in a unidirectional downtrend.&lt;/p&gt;

&lt;p&gt;The key design decisions across the system:&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%2Frzejjlhow95woo26dy55.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%2Frzejjlhow95woo26dy55.png" alt=" " width="774" height="288"&gt;&lt;/a&gt;&lt;br&gt;
Delisting events don't happen every day, but every one of them is a relatively high-conviction trading opportunity. With programmatic monitoring and execution, you can participate in these opportunities steadily — without staring at the screen.&lt;/p&gt;

</description>
      <category>gridstradegy</category>
      <category>binance</category>
      <category>autotrading</category>
      <category>fmz</category>
    </item>
    <item>
      <title>From 99 Traders to One Signal: Implementing a Distilled KOL Consensus Strategy on FMZ</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Mon, 18 May 2026 08:37:43 +0000</pubDate>
      <link>https://dev.to/quant001/from-99-traders-to-one-signal-implementing-a-distilled-kol-consensus-strategy-on-fmz-n2a</link>
      <guid>https://dev.to/quant001/from-99-traders-to-one-signal-implementing-a-distilled-kol-consensus-strategy-on-fmz-n2a</guid>
      <description>&lt;p&gt;The word "distillation" has been showing up everywhere lately. In AI it usually means compressing complex capabilities into a more compact, reusable form. The same idea carries over to strategy research — and when you make it concrete, what you're really doing is taking knowledge that was previously scattered, fuzzy, and dependent on subjective experience, and turning it into something you can compute, verify, and continue to refine.&lt;/p&gt;

&lt;p&gt;The crypto-kol-quant project has been getting a lot of attention recently. What's actually interesting about it isn't the number of KOLs it scrapes, or the fact that it uses LLMs. It's that the project tries to do something quantitative research rarely attempts: distill traders' experience into a set of computable capability factors, and then aggregate those factors into a consensus signal. That's a question worth taking seriously. Because if a group of long-active, stylistically consistent traders really have built up their own cognitive frameworks for the market, those frameworks shouldn't only live in tweets, charts, and offhand remarks — they ought to be extractable, organized, and made part of a runnable strategy 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%2Foyf8t8w70jiyv0s2tyij.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%2Foyf8t8w70jiyv0s2tyij.png" alt=" " width="800" height="624"&gt;&lt;/a&gt;&lt;br&gt;
Working from that premise, we built an early implementation inside the FMZ environment. The goal wasn't to "port" the project — it was to wire up its core logic from end to end: pull market data, translate the market into structured state, decide which trading capabilities are being triggered by that state, map those capabilities back to trader profiles, and finally aggregate every trader's individual judgment into a weighted consensus signal. It's clearly not a finished trading system, but it does demonstrate one important thing: trader experience really can be compressed, structured, and inserted into a strategy's decision flow.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Gets Distilled Is Capability, Not Opinion
&lt;/h2&gt;

&lt;p&gt;When people first encounter a project like this, they tend to read it as "a KOL sentiment strategy." That's not quite right. The original project isn't trying to count who's bullish today or who's calling tops and bottoms. It's pushing further: how does this trader actually understand the market? Under what kind of structure does he lean long? Does he focus on trend, position, pattern, volatility, or macro context? And can the way he makes those calls be organized into a stable set of capability tags?&lt;/p&gt;

&lt;p&gt;Once you frame the problem that way, the strategy's center of gravity shifts. The system stops caring about any single statement and starts caring about the methodology behind it. Put differently: what gets distilled here isn't text — it's trading knowledge itself. The system tries to translate subjective experience, which used to require a human to interpret, into rules a program can recognize and call. That's the biggest difference between this and the usual sentiment models. It's not measuring how hot the market mood is. It's reconstructing how different trading frameworks would react to the current market.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Translate the Market Into State Variables
&lt;/h2&gt;

&lt;p&gt;For distillation to actually land, the first step can't be prediction — it has to be feature engineering. The reason is simple: a trader's language is written for humans, not programs. Take a sentence like "price pulled back to a key moving average, decent spot to add on the second tap." A trader gets it instantly, but a program has to break it down: which moving average — the 50-day or the 200-day? Is price actually near that line? Is the trend still intact? Did a confirming candle appear?&lt;/p&gt;

&lt;p&gt;So the system's first job isn't to produce a long/short call. It's to convert raw market data into a set of structured states. The most basic layer here uses price to build trend and momentum features. Moving averages, exponential moving averages, RSI, MACD — these aren't there to pile up indicators, they're there to answer a simple question: roughly what state is the market in right now?&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%2Fwlg9ho64zcjkwjcgdvl9.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%2Fwlg9ho64zcjkwjcgdvl9.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;br&gt;
The key code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use moving averages of different periods to describe price's position within trends
f['ma20']  = _sma(c, 20)
f['ma50']  = _sma(c, 50)
f['ma100'] = _sma(c, 100)
f['ma200'] = _sma(c, 200)

# Exponential moving averages weight recent price changes more heavily
f['ema20'] = _ema(c, 20)
f['ema50'] = _ema(c, 50)

# RSI captures whether the market is overbought/oversold or losing momentum
f['rsi14'] = _rsi(c, 14)

# MACD line, signal line, and histogram together track trend and momentum shifts
ml, ms, mh = _macd(c)
f['macd']      = ml
f['macd_sig']  = ms
f['macd_hist'] = mh

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

&lt;/div&gt;



&lt;p&gt;The code isn't doing anything complicated. Moving averages help the system locate current price relative to longer-term trend; RSI and MACD describe whether momentum is building or fading. None of this is a trading decision yet — it's purely a "market state" layer.&lt;/p&gt;

&lt;p&gt;The system also adds volatility and positional features, because a lot of trading judgments don't rely on trend alone — they also depend on whether we're in a volatility-compression phase, or whether price is sitting near a range high or low.&lt;/p&gt;

&lt;p&gt;The corresponding code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Log returns are the basis for volatility computation
logr = np.log(c / c.shift(1))

# 30-day annualized realized volatility, gauging current volatility level
# Note: sqrt(365) — crypto trades around the clock, so we annualize on
# calendar days rather than the 252 trading days used for equities
f['rv30'] = logr.rolling(30, min_periods=10).std() * np.sqrt(365)

# Recent 20-day and 50-day highs/lows, used to locate price within recent structure
f['high_20d'] = h.rolling(20, min_periods=1).max()
f['low_20d']  = l.rolling(20, min_periods=1).min()
f['high_50d'] = h.rolling(50, min_periods=1).max()
f['low_50d']  = l.rolling(50, min_periods=1).min()

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

&lt;/div&gt;



&lt;p&gt;rv30 captures the recent annualized volatility level. The range highs/lows tell the system where current price sits inside the recent price structure. On top of that, macro context gets folded into the state space too — there's a kind of trader who doesn't only look at the coin price; they're simultaneously watching the dollar index, equity risk appetite, and the rate environment. The code aligns these to the daily frame and then turns them into readable state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# DXY as a backdrop for dollar strength
if 'DXY' in macro:
    dxy = _align(macro['DXY'])
    f['dxy_ret_20d']    = dxy.pct_change(20)
    f['dxy_trend_down'] = (dxy.pct_change(20) &amp;lt; -0.01).astype(int)

# SPX as a proxy for risk appetite
if 'SPX' in macro:
    spx = _align(macro['SPX'])
    f['spx_ret_20d']  = spx.pct_change(20)
    f['spx_trend_up'] = (spx.pct_change(20) &amp;gt; 0).astype(int)

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

&lt;/div&gt;



&lt;p&gt;The point of this whole step compresses to one sentence: take "what the market looks like right now" and turn it into structured state the machine can keep reading. Without this layer, there's nothing left to distill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Encode Subjective Experience as Capability Factors
&lt;/h2&gt;

&lt;p&gt;Features alone aren't enough — features only describe the market, they don't say what that state means. The next step is to write traders' experience into rules: given the current state variables, decide which trading capabilities are being triggered.&lt;/p&gt;

&lt;p&gt;This is where the strategy's distillation flavor is strongest. We're no longer talking abstractly about "frameworks that matter"; we're committing them to actual program conditions. The current implementation's capability factors span pattern, structure, indicator, cycle, and macro layers. Some come from pattern recognition — bull flags, bear flags, double tops/bottoms, head-and-shoulders, triangles. Some come from structural analysis — Wyckoff, SMC, ICT-style frameworks. Some come from indicators themselves — RSI divergences, golden/death crosses, Bollinger band squeeze breakouts. And some come from cycle and macro context — halving cycles, trending vs. ranging market regime shifts, DXY drawdowns, risk-on rebounds.&lt;/p&gt;

&lt;p&gt;A textbook example is "trend pullback continuation." Plenty of traders share this pattern: if the larger trend is still up, price pulls back to a key moving average, and the current candle shows a confirming bid, that often means the trend continues. The program states it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Check whether current price is close to the 50-day moving average
near_ma50 = abs(close - ma50_v) / close &amp;lt; 0.02 if close &amp;gt; 0 else False

# If the 50-day MA is still above the 200-day MA, and a green candle shows up
# right at the pullback, score this as a trend-continuation capability signal
# (In production you'd want extra confirmation — volume, candle body, lower wick)
s['cap_014_trend_pullback_continuation'] = 0.6 if (ma50_gt and near_ma50 and is_green) else 0.0

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

&lt;/div&gt;



&lt;p&gt;There's nothing mysterious here — it's just breaking a sentence of human language into a few conditions a machine can evaluate one by one. Another example is "Bollinger squeeze breakout." For many traders, a long stretch of compressed volatility followed by a sudden expansion (up or down) usually signals a fresh directional choice. The rule looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# bb_w20_p1 is the 20-period mean of Bollinger bandwidth (the squeeze baseline).
# If the previous candle's bandwidth sits below that baseline, treat it
# as a volatility-contraction state.
squeezed = bb_w_p1 &amp;lt; bb_w20_p1 if bb_w20_p1 &amp;gt; 0 else False

# Squeeze followed by a breakout above the upper band -&amp;gt; positive signal
# Squeeze followed by a break below the lower band  -&amp;gt; negative signal
s['cap_021_bollinger_squeeze_breakout'] = (
    0.6  if (squeezed and close &amp;gt; bb_u) else
   -0.6  if (squeezed and close &amp;lt; bb_l) else 0.0
)

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

&lt;/div&gt;



&lt;p&gt;Macro factors get the same treatment. For traders who lean macro, BTC isn't an isolated price series — it's affected by the dollar, equities, and the rate environment, so those views are also written into capability checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Falling DXY is generally read as a positive backdrop for BTC
s['cap_027_dxy_inverse_btc']  = 0.4 if (not _nm(dxy_r20) and dxy_r20 &amp;lt; -0.01) else 0.0

# Rising S&amp;amp;P implies improving risk appetite
s['cap_028_spx_risk_on_off']  = 0.4 if (not _nm(spx_r20) and spx_r20 &amp;gt;  0.02) else 0.0

# Falling short-end yields imply a marginal liquidity tailwind
s['cap_029_yields_liquidity'] = 0.4 if (not _nm(y_r20)   and y_r20   &amp;lt; -0.02) else 0.0

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

&lt;/div&gt;



&lt;p&gt;What matters at this layer isn't how many rules we wrote — it's that we completed the most critical move in the entire distillation: compressing judgments that previously required subjective interpretation into computable conditions. Worth noting in passing: most capability factors in the current version are condition-triggered rather than continuously scored. That means the system is closer to detecting whether a particular structure holds than to repricing every micro-fluctuation. It's why this implementation fits daily or mid-to-low frequency reasoning better than high-frequency trading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Don't Sum Factors — Map Them Back to Trader Profiles
&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%2Fbiz0q8dtr0y9vi1m4o29.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%2Fbiz0q8dtr0y9vi1m4o29.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;br&gt;
If the strategy stopped at the factor layer, it would still just be a regular rule system. What makes the original project distinctive is that it doesn't stop there — it pushes one step further: factors don't directly determine direction; they get mapped back to trader profiles first.&lt;/p&gt;

&lt;p&gt;This part matters. Real traders don't "use all capabilities equally." Some lean into trend, some into structure, some into cycle, some into macro. Faced with the same market state, different traders care about completely different things. So instead of averaging all factors at once, the system reads each trader's capability preferences first, then computes that trader's individual signal given the current factor state.&lt;/p&gt;

&lt;p&gt;The profile-loading logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# For each trader, load the capability factors they actually use, with weights
caps = {c['id']: float(c.get('weight', 0.5))
        for c in p.get('capabilities_used', [])}

profiles.append({
    'handle': p.get('handle', item['name'][:-5]),
    'caps':   caps
})

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

&lt;/div&gt;



&lt;p&gt;Each profile is essentially answering one question: which capability factors does this trader actually rely on, and how heavily does each one weigh in his framework? Once profiles are in hand, the system computes each trader's "personal signal" for the current market:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for p in profiles:
    sig = 0.0
    wt  = 0.0

    # Iterate over every capability this trader cares about
    for cap_id, w in p['caps'].items():
        score = factor_scores.get(cap_id, 0.0)

        # Current factor score scaled by this trader's preference weight
        sig += w * score
        wt  += abs(w)

    # Normalize to get this trader's personal signal in the current market
    trader_raw = sig / wt if wt &amp;gt; 0 else 0.0

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

&lt;/div&gt;



&lt;p&gt;By this point the system feels different. It's no longer just looking at "which factors lit up." It's approximating something else: if you handed today's market to these 99 traders, how would each of them judge it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: From Personal Signals to Weighted Consensus
&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%2F030xtk18v9cs6xz0pssl.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%2F030xtk18v9cs6xz0pssl.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;br&gt;
Once each trader's personal signal is computed, the system finally enters the consensus layer. "Consensus" here isn't a vote, and it's definitely not whoever-shouts-loudest-wins — it factors in historical effectiveness as well.&lt;/p&gt;

&lt;p&gt;A quick note for readers new to the term: IC stands for Information Coefficient — the historical correlation between a signal and forward returns. Higher absolute IC means the signal carries more information.&lt;/p&gt;

&lt;p&gt;The two most important outputs in the current code are ic_weighted and trust_adjusted. The core logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# First: weight only traders with positive IC, producing ic_weighted
pos_w  = sum(max(t['ic'], 0) for t in trader_signals)
ic_wt  = (
    sum(t['signal'] * max(t['ic'], 0) for t in trader_signals) / pos_w
    if pos_w &amp;gt; 0 else 0.0
)

# trust_adjusted goes a step further:
# positive-IC traders are used directly; negative-IC traders are flipped
# and then everything is weighted by absolute IC magnitude
abs_w = sum(abs(t['ic']) for t in trader_signals)
trust = (
    sum((t['signal'] if t['ic'] &amp;gt;= 0 else -t['signal']) * abs(t['ic'])
        for t in trader_signals) / abs_w
    if abs_w &amp;gt; 0 else 0.0
)

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

&lt;/div&gt;



&lt;p&gt;Two simple but important principles are embedded here. First: traders who've been more effective historically carry more weight today. Second: traders whose historical IC is negative aren't thrown out — they may be used as inverse indicators instead. So trust_adjusted isn't just "what does the crowd think" — it's "who thinks what, and whose view do we actually trust."&lt;/p&gt;

&lt;p&gt;One caveat worth flagging: flipping a negative-IC trader assumes that negative IC reflects stable contrarian information, not statistical noise. In practice you'd want a significance test or a sample-size gate before using the inverse — otherwise you're amplifying randomness instead of correcting it.&lt;/p&gt;

&lt;p&gt;This is also why the system reads differently from a normal sentiment model. It isn't tallying voices — it's running a round of cognitive aggregation that's been checked against history. Compress the whole method into one sentence: turn the market into state variables, map state variables into capability factors, map capability factors into per-trader personal signals, and finally aggregate those personal signals by historical effectiveness into a consensus call.&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%2Fgbv1qj42l9kwe19cm8p0.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%2Fgbv1qj42l9kwe19cm8p0.png" alt=" " width="800" height="329"&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%2Ffrz8eowooy9k4ro9hmon.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%2Ffrz8eowooy9k4ro9hmon.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the FMZ Implementation Actually Achieves
&lt;/h2&gt;

&lt;p&gt;If the project stayed in research mode, the system would feel more like a "consensus analyzer." On FMZ, the priority is wiring the entire pipeline so it can keep running. The most important code is just three lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Step 1: turn raw price data and macro variables into structured state
feat_df = build_features(records, macro if macro else None)

# Step 2: evaluate which capability factors are triggered given the state
factor_scores = evaluate_factors(feat_df)

# Step 3: map factors back to trader profiles, then aggregate to consensus
consensus = compute_consensus(factor_scores)

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

&lt;/div&gt;



&lt;p&gt;These three lines are the strategy's three most important abstractions. Layer one handles market state; layer two handles capability evaluation; layer three handles trader consensus. Execution, risk control, and state display sit downstream of these, but from a research-logic perspective the critical part is already complete. The point of this implementation isn't how many runtime details it adds — it's that the original project's capability profiles are no longer static files, the factors are no longer just research outputs, and the consensus is no longer just a number in a report. They've been wired into a continuously running judgment loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It's Still Just a Prototype
&lt;/h2&gt;

&lt;p&gt;This implementation isn't the endgame. The current code uses BTC daily bars, so it's better suited to mid-to-low frequency consensus calls than to high-frequency trading. Its core still revolves around daily structure, cycle position, macro backdrop, and trader capability preferences. Trader profiles and IC values are still static inputs — the system hasn't yet entered an online-evolution phase. In other words, "knowledge distillation" step one is done, but "the distilled knowledge keeps self-correcting" hasn't fully landed.&lt;/p&gt;

&lt;p&gt;That doesn't take away from what the project does demonstrate, which is itself important: trader experience really can be compressed, structured, and inserted into a strategy pipeline. The value isn't that it produces stable returns yet — it's that it advances a research path that previously sat at the concept level into something runnable. How those capability factors should evolve, how trader weights should update, and how consensus should keep recalibrating against live markets — those are questions only more runtime data can answer.&lt;/p&gt;

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

&lt;p&gt;What's genuinely instructive about crypto-kol-quant isn't the volume of trendy concepts it strings together. It's that it pushes a hard-to-systematize problem one step forward: take traders' experience, turn it from expression into capabilities, from capabilities into factors, from factors into consensus. The FMZ implementation's job is to actually run that distillation pipeline end-to-end. It doesn't oversell itself as the final answer, and it doesn't try to hide the fact that it's still an early prototype. But it does prove one thing: trading experience doesn't have to live only in charts and language — it can be distilled, structured, executed, and even placed inside a system that continuously judges the market.&lt;/p&gt;

&lt;p&gt;If traditional quant is good at finding patterns inside price series, then a strategy of this kind points to a direction worth pushing: extract patterns from human cognition, then let those patterns participate in the market in turn. And that, more than anything else, may be why "distillation" deserves attention in strategy research.&lt;/p&gt;

&lt;p&gt;Original project: Tower of Locked Demons Skill — Refining 99 Crypto Traders (suo yao ta Skill)&lt;/p&gt;

&lt;p&gt;Special thanks to user GiantBin for the underlying ideas. If you have your own thoughts on this direction, we'd welcome the conversation.&lt;/p&gt;

</description>
      <category>kol</category>
      <category>strategy</category>
      <category>fmz</category>
      <category>traders</category>
    </item>
    <item>
      <title>Moving Average Screening Strategy: A Quantitative Approach Inspired by Harness Engineering</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Fri, 24 Apr 2026 08:14:48 +0000</pubDate>
      <link>https://dev.to/quant001/moving-average-screening-strategy-a-quantitative-approach-inspired-by-harness-engineering-4len</link>
      <guid>https://dev.to/quant001/moving-average-screening-strategy-a-quantitative-approach-inspired-by-harness-engineering-4len</guid>
      <description>&lt;p&gt;&lt;em&gt;Posted in: Community &amp;gt; MA Screening Quant Strategy: A Harness Engineer Approach in Practice&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro: The Harness Engineer Mindset
&lt;/h2&gt;

&lt;p&gt;Lately in the AI/ML engineering community, a way of thinking has been getting increasingly discussed — Harness Engineer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The core idea is simple:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of coming up with an answer by gut feeling, build a harness and let the data and experiments find the answer for you.&lt;/p&gt;

&lt;p&gt;The traditional engineer's approach is: I believe parameter A is better, so I'll write code to implement A. The Harness Engineer's approach is: I don't know whether A, B, or C is better, so I'll build a framework that runs A, B, and C in parallel and let the data tell me the answer.&lt;/p&gt;

&lt;p&gt;The engineer is responsible for defining the search space and the evaluation criteria; the system is responsible for automatically searching the space for the optimum. In ML this corresponds to walk-forward optimization and AutoML. In quant trading, there's actually a natural place for this idea to land.&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%2Fq8imokih2vft6p3ezadz.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%2Fq8imokih2vft6p3ezadz.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  "Demon Coins": Where the Trend Is Most Visible
&lt;/h2&gt;

&lt;p&gt;In the crypto perpetual contract market, there's a category of tokens worth paying special attention to — the high-volume "demon coins."&lt;/p&gt;

&lt;p&gt;These tokens share several traits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highly concentrated capital&lt;/strong&gt; — whale/market-maker behavior is unmistakable&lt;br&gt;
&lt;strong&gt;Persistent trends&lt;/strong&gt; — once a move gets going, it tends to last&lt;br&gt;
&lt;strong&gt;High volatility&lt;/strong&gt; — some high-volume tokens during specific periods exhibit strong trend behavior, where MA strategies historically perform reasonably well on these symbols&lt;br&gt;
For exactly these reasons, using the classic dual-MA crossover strategy on such tokens is a plain but sensible entry point. Fast line crosses above the slow line — trend is starting, go with it. Fast line crosses below the slow line — trend is reversing, get out. The logic is simple, but on symbols with clear trends, historical performance usually isn't bad.&lt;/p&gt;

&lt;p&gt;There's only one problem: &lt;strong&gt;which tokens are demon coins? Which MA parameter set should be used?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you try to answer these two questions by hand, it's too subjective — a different person could arrive at completely different answers. And the market is dynamic: today's demon coin isn't necessarily tomorrow's, and today's effective parameter combination could fail tomorrow.&lt;/p&gt;

&lt;p&gt;This is exactly where the Harness Engineer mindset comes in.&lt;/p&gt;

&lt;p&gt;Rather than hand-picking tokens and hand-tuning parameters, hand both problems off to the framework — define the evaluation criteria, let historical data run itself through the candidate space and produce the answer. The human only decides the criteria for good vs. bad; the system handles the rest.&lt;/p&gt;

&lt;p&gt;On this basis, the entire strategy is designed as a rolling screening framework, operating in three layers.&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%2F50p9nfs0kh14cjcwuomo.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%2F50p9nfs0kh14cjcwuomo.png" alt=" " width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Strategy Architecture: A Two-Stage Horse Race
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Building the Candidate Pool&lt;/strong&gt;&lt;br&gt;
From all contract symbols across the market, take the top 150 by USD trading volume as the candidate pool.&lt;/p&gt;

&lt;p&gt;Why volume? Because that's where capital is most concentrated, trends form most readily, and demon coins cluster most densely. This step involves no subjective judgment — the market's capital votes directly: whoever trades the most gets into the pool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const filtered = tickers
    .filter(t =&amp;gt; t.Symbol.endsWith('USDT.swap'))
    .map(t =&amp;gt; ({ symbol: t.Symbol, quoteVolume: t.Last * t.Volume }))
    .sort((a, b) =&amp;gt; b.quoteVolume - a.quoteVolume)
    .slice(0, topN)
    .map(t =&amp;gt; t.symbol);

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

&lt;/div&gt;



&lt;p&gt;Logic is very direct: filter USDT perpetual contracts, compute USD volume, sort descending, take the top N. No subjective calls — capital votes with its feet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Two-Stage Horse-Race Selection&lt;/strong&gt;&lt;br&gt;
This is the core of the whole strategy, and where the Harness mindset shows up most clearly.&lt;/p&gt;

&lt;p&gt;The correct order of execution is:&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%2F0rbry0r8dvi3mqgseay9.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%2F0rbry0r8dvi3mqgseay9.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
⚠️ &lt;strong&gt;Note:&lt;/strong&gt; Using the best-parameter score as a proxy for a token's "capability" itself carries some overfitting risk — the parameters that performed best on history won't necessarily perform equally well in the future. This limitation is discussed further in the second half of the article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backtest process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For every token in the candidate pool, run multiple MA parameter combinations in parallel. Each combination runs independently on historical K-lines, simulating real crossover entries and exits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Iterate: each coin × each parameter combo
for (const params of maParamsList) {
    const bt = backtest_MA(records, params.fast, params.slow);
    // Each backtest independently yields: win rate, profit factor, max drawdown, signal count
}

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

&lt;/div&gt;



&lt;p&gt;Each backtest's core logic is standard dual-MA crossover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const crossUp = fastMA[i-1] &amp;lt;= slowMA[i-1] &amp;amp;&amp;amp; fastMA[i] &amp;gt; slowMA[i];
const crossDown = fastMA[i-1] &amp;gt;= slowMA[i-1] &amp;amp;&amp;amp; fastMA[i] &amp;lt; slowMA[i];

if (crossUp) position = { side: 'long', entryPrice: records[i].Close };
if (crossDown) position = { side: 'short', entryPrice: records[i].Close };

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Composite score&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once backtests complete, each parameter set's results get scored. The scoring is made of two parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalized weighted score (weights sum to 0.80):&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;const score =
      Math.min(bt.winRate * 100, 100) * 0.30              // win rate, capped at 100
    + Math.min(bt.profitFactor * 20, 60) * 0.30           // profit factor, capped at 60
    + Math.max(0, 1 - bt.maxDrawdown / maxMDD) * 100 * 0.20  // max drawdown control
    + volPct * volPctBonus                                // volatility-percentile bonus

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Volatility-percentile bonus:&lt;/strong&gt; the last term volPct × volPctBonus (default coefficient 10) is a bonus separate from the weighted system, used to break ties by favoring tokens whose current volatility sits at a higher historical percentile — these tokens tend to be more active trend-wise.&lt;/p&gt;

&lt;p&gt;Note that all these weights and bonus coefficients are heuristic — they weren't derived through optimization. They can be further tuned based on actual market conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1 Competition: Parameter Competition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a given token, multiple parameter sets each get their own score. The highest-scoring set becomes that token's representative score and its best parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (score &amp;gt; bestScore) {
    bestScore = score;
    bestResult = bt;
    bestParams = params; // record the best-performing parameter combo so far
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stage 2 Competition: Token Competition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All tokens bring forward their best score. These are sorted, and the Top N enter the whitelist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;results.sort((a, b) =&amp;gt; b.score - a.score);
const whitelist = results.slice(0, topCoins).map(r =&amp;gt; r.coin);

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

&lt;/div&gt;



&lt;p&gt;The final output is: &lt;strong&gt;for each whitelisted token, its own dedicated best MA parameters&lt;/strong&gt; — not a single parameter set applied across the board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: Live Execution and Risk Controls
&lt;/h2&gt;

&lt;p&gt;Use the screened configuration for live trading, with multiple risk-control layers stacked on top:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal triggering:&lt;/strong&gt; continuously detect MA crossovers on whitelisted tokens — golden cross → long, death cross → short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const crossUp = fastPrev &amp;lt;= slowPrev &amp;amp;&amp;amp; fastCur &amp;gt; slowCur;
const crossDown = fastPrev &amp;gt;= slowPrev &amp;amp;&amp;amp; fastCur &amp;lt; slowCur;

if (crossUp) longList.push(sym);
if (crossDown &amp;amp;&amp;amp; allowShort) shortList.push(sym);

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trailing take-profit:&lt;/strong&gt; activates once floating profit hits a trigger threshold, with the pullback threshold tightening dynamically as floating profit grows. The three tiers are heuristic — the core idea is: the higher the floating profit, the lower the tolerance for pullback, locking in gains already made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getDynamicTrailDrawdown(maxPnl) {
    if (maxPnl &amp;gt;= 7) return 3;    // high profit, tighten pullback tolerance
    if (maxPnl &amp;gt;= 4) return 2;
    return 1.5;                    // lower profit, give the move a bit more room
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Market regime awareness&lt;/strong&gt;: detect BTC's volatility percentile — high-volatility environments automatically scale down position sizing, and extreme regimes outright disable shorting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (marketState === 'volatile')      positionScaleDown = 0.5;
else if (marketState === 'high_vol') positionScaleDown = 0.8;
else if (marketState === 'low_vol')  positionScaleDown = 0.7;

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

&lt;/div&gt;



&lt;p&gt;The entire screening pipeline re-runs on a rolling schedule — not sticking to any single configuration, but dynamically updating the whitelist and parameters as the market evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Underlying Assumption: Trend Persistence
&lt;/h2&gt;

&lt;p&gt;For this framework to hold up, it depends on one core assumption:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tokens and parameters that performed well in the recent past have some degree of continuity in the near future.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This isn't mysticism — there's real market logic supporting it. Capital inertia, continuation of market sentiment, and coherence in whale behavior all keep trends valid within some time window.&lt;/p&gt;

&lt;p&gt;But to be honest: this assumption hasn't gone through rigorous statistical validation — it's more of an empirical judgment. Whether the framework continues to work in live trading ultimately has to be verified against real trading data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Differs from a Real Harness Engineer Setup
&lt;/h2&gt;

&lt;p&gt;This has to be said plainly.&lt;/p&gt;

&lt;p&gt;This strategy has the shape of a Harness, but compared to a real Harness Engineer system, the gaps are obvious:&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%2F43ergf9sqibc5r0yzm7d.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%2F43ergf9sqibc5r0yzm7d.png" alt=" " width="800" height="173"&gt;&lt;/a&gt;&lt;br&gt;
The core gap: a real Harness keeps asking "does this result still hold out-of-sample?" — whereas the "best" coming out of this strategy's two-stage horse race is, fundamentally, just historical best. Parameter-level overfitting stacked on top of token-level overfitting — whether that carries forward is a permanently open question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing: Carving Marks on a Moving Boat — Still Worth Trying?
&lt;/h2&gt;

&lt;p&gt;In the quant world, prediction has always been incredibly hard.&lt;/p&gt;

&lt;p&gt;Plenty of people will say: picking parameters from historical data and deploying them live is essentially carving a notch on a moving boat to mark where your sword fell into the water. The sword's already sunk; the mark you carved on the boat won't help you find it. Markets change, effective parameters stop working, today's demon coin gets boring tomorrow, yesterday's optimal MA is today's noise.&lt;/p&gt;

&lt;p&gt;This critique isn't without merit.&lt;/p&gt;

&lt;p&gt;But that said — the experiments worth running still have to be run.&lt;/p&gt;

&lt;p&gt;The essence of quant isn't finding an answer that's permanently correct — it's systematically improving your win rate under uncertainty. Even if you're carving notches on a moving boat, you still need a boat and a mark first. Locating the strategy (the boat) is itself the beginning of quant.&lt;/p&gt;

&lt;p&gt;Of course, the framework itself doesn't guarantee profit. Having the framework is just a starting point — the real value is in continuous execution and iteration: the whitelist can be adjusted, scoring weights can be changed, the parameter space can be expanded, take-profit and stop-loss logic can be improved. Every adjustment is a new experiment, and every experiment pushes this framework closer to a real Harness.&lt;/p&gt;

&lt;p&gt;The path is walked, not thought.&lt;/p&gt;

</description>
      <category>strategy</category>
      <category>harness</category>
      <category>ai</category>
      <category>ma</category>
    </item>
    <item>
      <title>Risk Parity–Driven Dynamic Allocation for RWA Multi-Assets in Crypto Markets</title>
      <dc:creator>Dream</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:42:23 +0000</pubDate>
      <link>https://dev.to/quant001/risk-parity-driven-dynamic-allocation-for-rwa-multi-assets-in-crypto-markets-181c</link>
      <guid>https://dev.to/quant001/risk-parity-driven-dynamic-allocation-for-rwa-multi-assets-in-crypto-markets-181c</guid>
      <description>&lt;p&gt;&lt;em&gt;Posted in: Community &amp;gt; Crypto Market RWA Multi-Asset Dynamic Weight Allocation: Risk Parity Strategy&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The crypto market hasn't been easy lately. BTC has pulled back hard from the highs, altcoins have been hit even worse, and a lot of people are sitting on the sidelines or leaving altogether. But during this same stretch, something else has been quietly heating up — RWA, the tokenization of real-world assets. Gold, US equities, crude oil — assets from traditional markets are starting to show up on crypto exchanges in contract form. Major exchanges have rolled out SPY (tracking the S&amp;amp;P 500 ETF), gold XAU, crude CL, and so on. Combined with BTC, which was already there, a multi-asset trading environment covering US equities, gold, crude oil, and crypto has taken shape on-chain for the first time.&lt;/p&gt;

&lt;p&gt;Looking at this lineup, an idea came to mind: traditional finance has a classic approach called risk parity. In plain terms — don't put all your eggs in one basket, and how much you put in each basket depends on how risky that basket is. This logic has been used in traditional finance circles for decades. In a bear market like this one, everyone is looking for ways to not depend on a single asset for survival. Now that the crypto market finally has contracts for these assets, it's time to port that logic over and give it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Risk Parity Straight First
&lt;/h2&gt;

&lt;p&gt;Before getting into the specifics, it's worth explaining what "risk parity" actually means, otherwise the logic that follows is hard to track.&lt;/p&gt;

&lt;p&gt;A normal person allocating assets might think: I'll split my money evenly into four parts, 25% per asset. Sounds balanced, but the problem is that BTC can swing 10% in a day while gold might move 0.5%. With the same 25% allocation, BTC brings you twenty times the risk that gold does. On the surface the four assets are split evenly, but in reality the portfolio's fate is almost entirely determined by BTC alone — the other three are just there for show.&lt;/p&gt;

&lt;p&gt;Risk parity flips this around: don't split the money evenly, split the risk evenly. BTC is volatile, so allocate less to it; gold is stable, so allocate more — but that's only step one. The more critical piece is that assets aren't independent of one another. BTC and US equities can fall together under certain conditions. If two assets move strongly in the same direction, spreading risk between them is of limited value even if their individual volatilities differ. True risk parity has to simultaneously account for each asset's volatility and its correlation with the others, using the covariance matrix to measure each asset's marginal contribution to total portfolio risk, so that each asset ends up contributing roughly the same amount of risk. The upside of this logic is that a sudden blowup in any single asset won't take down the whole portfolio.&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%2Fw2w3lh2ou5kj7puapuyn.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%2Fw2w3lh2ou5kj7puapuyn.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Pick the Right Assets First — Diversification Only Makes Sense If You Do&lt;/strong&gt;&lt;br&gt;
To do risk parity, the first thing is picking what goes in the basket. If the asset selection is wrong, no amount of calculation precision downstream will help.&lt;/p&gt;

&lt;p&gt;The asset pool chosen was SPY, XAU, CL, and BTC. There's a logic to picking these four — they don't usually move in lockstep. Equities rise when the economy is good, gold rallies when risk-off sentiment picks up, crude and gold both move on inflation expectations, and crypto sometimes tracks equities and sometimes goes completely off-script. It's precisely because their correlations are low that putting them together actually diversifies risk. If all four went up and down together every day, "diversification" would be an illusion, and no method could save you.&lt;/p&gt;

&lt;p&gt;Once the assets are chosen, the next question is: how much to put in each? That's what risk parity actually needs to calculate.&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%2Fggvzy3hf5sikbn78cill.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%2Fggvzy3hf5sikbn78cill.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Compute Each Asset's Weight
&lt;/h2&gt;

&lt;p&gt;The strategy's computation is a two-step process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step one:&lt;/strong&gt; pull K-line data for the four assets and align by timestamp. The strategy uses 1-hour K-lines (PERIOD_H1) as its data input. This step looks simple, but in reality RWA contract products sometimes have missing data. If SPY has no quote at some timestamp, that entire row is dropped — only timestamps where all four assets have quotes are kept for later calculations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var k = 0; k &amp;lt; keys.length; k++) {
    var row = timeMap[keys[k]], ok = true;
    for (var i = 0; i &amp;lt; LABELS.length; i++) {
        if (row[LABELS[i]] === undefined) { ok = false; break; }
    }
    if (ok) timestamps.push(parseInt(keys[k]));
}

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

&lt;/div&gt;



&lt;p&gt;After alignment, closing price series are converted into log returns — that is, ln(current price / previous price). Log returns are used instead of simple percentage changes because they have time additivity — returns across multiple periods can be summed directly to get the total log return, which is easier to handle. Also, under the common assumption that asset prices follow geometric Brownian motion, log returns are normally distributed, which is compatible with the covariance matrix framework that follows. One caveat worth noting: actual returns on crypto assets tend to have fat tails, which is a known assumption bias — the covariance matrix can underestimate real risk in extreme conditions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function calcLogReturns(prices) {
    var r = [];
    for (var i = 1; i &amp;lt; prices.length; i++) {
        var prev = prices[i - 1], cur = prices[i];
        if (!prev || !cur || prev &amp;lt;= 0 || cur &amp;lt;= 0) { r.push(0); continue; }
        r.push(Math.log(cur / prev));
    }
    return r;
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step two:&lt;/strong&gt; use these return series to compute the covariance matrix. Just using the plain historical average covariance is too crude — what's used here is EWMA (exponentially weighted) — giving recent data higher weights so the covariance matrix can pick up on changes in market regime more quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var t = T - 1; t &amp;gt;= 0; t--) {
    var w = Math.pow(lambda, T - 1 - t);
    c += w * (retMat[i][t] - means[i]) * (retMat[j][t] - means[j]);
    ws += w;
}
cov[i][j] = c / ws;
cov[j][i] = cov[i][j];

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

&lt;/div&gt;



&lt;p&gt;lambda is the decay coefficient, and the strategy uses EWMA_LAMBDA = 0.94 by default — a value drawn from RiskMetrics' empirical setting for daily data. Note that RiskMetrics' 0.94 was designed for daily data, while the strategy uses 1-hour data at a higher frequency. If you want the covariance matrix's sensitivity to recent changes to match that of daily 0.94, in theory it should be converted to a value closer to 1. But in practice, 0.94 is still a reasonable starting point at the hourly level — it represents a configuration where recent data gets higher weights and historical data decays faster. You can adjust it based on actual backtest results. The closer lambda is to 1, the more persistent the historical data's influence becomes, and the slower the covariance matrix reacts to recent changes. The smaller lambda is, the more responsive the strategy is to regime changes, but it also becomes more prone to triggering unnecessary rebalances due to short-term noise.&lt;/p&gt;

&lt;p&gt;After the calculation, a regularization term is added to the diagonal, with a coefficient of 0.1% of the diagonal mean (eps = diagMean * 0.001). The 0.001 multiplier was chosen through testing — numerically sufficient to ensure positive definiteness of the matrix, while its impact on the covariance structure is negligible. One simplification worth noting: the strict form of EWMA covariance should use a weighted mean for computing deviations, but the code uses the simple arithmetic mean over the entire lookback window. When return means are close to zero (the common state of short-term financial asset returns), this simplification has negligible impact on results, but it introduces a small bias when means deviate significantly from zero.&lt;/p&gt;

&lt;p&gt;With the covariance matrix in hand, the next step is the actual risk parity solver — finding a set of weights where each asset contributes roughly equally to portfolio risk. Before iteration begins, the strategy has already determined the direction of each asset through the covariance of an equal-volatility portfolio, with results stored in the signs[] array (+1 for long, -1 for short). The iteration process uses these pre-determined fixed signs throughout, adjusting only the magnitude of the weights without changing direction — this is done for engineering stability, to prevent weights from repeatedly flipping direction during iteration due to numerical perturbation.&lt;/p&gt;

&lt;p&gt;There's no analytical solution to this problem, so it's approached iteratively. Each round computes the current risk contributions, pushes down the weights of assets contributing too much, lifts up the weights of those contributing too little, and repeats until convergence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var iter = 0; iter &amp;lt; 2000; iter++) {
    var trc = riskContribs(w, scaledCov);
    var pv = portVol(w, scaledCov);
    var target = pv / n;
    var obj = 0;
    for (var i = 0; i &amp;lt; n; i++) {
        for (var j = i + 1; j &amp;lt; n; j++) {
            var d = Math.abs(trc[i]) - Math.abs(trc[j]);
            obj += d * d;
        }
    }
    if (obj &amp;lt; 1e-12) break;
    var nw = [], ns = 0;
    for (var i = 0; i &amp;lt; n; i++) {
        var rc = Math.abs(trc[i]);
        var sign = signs[i]; // use the pre-determined fixed sign
        var a = target / rc;
        var v = sign * Math.max(Math.abs(w[i]) * Math.pow(a, 0.5), 1e-6);
        nw.push(v);
        ns += Math.abs(v);
    }
    // Absolute-value normalization: the sum of absolute weights equals 1
    for (var i = 0; i &amp;lt; n; i++) w[i] = nw[i] / ns;
}

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

&lt;/div&gt;



&lt;p&gt;The convergence criterion is that the sum of squared differences in risk contributions across all assets is below 1e-12, at which point risk contributions are considered close enough and iteration stops. The maximum iteration count is capped at 2000 to prevent infinite loops when the covariance matrix is ill-conditioned. In practice convergence usually happens within a few hundred iterations. If it hasn't converged by the cap, it means there's something wrong with the input data or the covariance matrix — in that case the strategy falls back to an equal-weight long portfolio, ensuring the system doesn't idle.&lt;/p&gt;

&lt;p&gt;Each asset's risk contribution is defined as its weight multiplied by its marginal risk — the result can be negative, meaning the asset is reducing overall portfolio volatility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function riskContribs(w, cov) {
    var sv = matVecMul(cov, w);
    var pv = portVol(w, cov);
    var trc = [];
    for (var i = 0; i &amp;lt; w.length; i++) trc.push(w[i] * sv[i] / pv);
    return trc;
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Weights Can Be Negative — i.e. Shorting Is Allowed
&lt;/h2&gt;

&lt;p&gt;Standard risk parity strategies are typically long-only, but the contract market allows shorting, so the strategy extends this by letting weights be negative.&lt;/p&gt;

&lt;p&gt;The logic is this: first compute an equal-volatility reference portfolio — high-volatility assets get low weights, low-volatility assets get high weights — then check whether each asset's covariance with this reference portfolio is positive or negative. If the covariance is negative, it means this asset tends to move inversely to the portfolio overall, so shorting it helps reduce total portfolio risk. The weight is set negative, which corresponds to opening a short position in the contract market.&lt;/p&gt;

&lt;p&gt;One caveat: this is an engineering approximation, not the canonical solution from long-short risk parity theory. Strict long-short risk parity requires constrained optimization based on the full covariance matrix, at higher computational cost. The approximation here gives reasonable directional judgments under most market conditions, but when multiple assets simultaneously have negative covariance with the reference portfolio, whether shorting all of them at once still reduces total portfolio risk needs to be verified case by case and cannot be taken for granted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var invVols = [], sumInvVol = 0;
for (var i = 0; i &amp;lt; n; i++) {
    var vol = Math.sqrt(Math.max(scaledCov[i][i], 1e-16));
    invVols.push(1 / vol);
    sumInvVol += 1 / vol;
}
var eqVolWeights = [];
for (var i = 0; i &amp;lt; n; i++) {
    eqVolWeights.push(invVols[i] / sumInvVol);
}
var covWithPortfolio = 0;
for (var j = 0; j &amp;lt; n; j++) {
    covWithPortfolio += eqVolWeights[j] * scaledCov[i][j];
}
var dir = covWithPortfolio &amp;lt; 0 ? -1 : 1;
Log('[Direction]', LABELS[i], 'covariance with equal-vol portfolio:',
    _N(covWithPortfolio, 8), '→', dir &amp;lt; 0 ? '🔴 short' : '🟢 long');

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

&lt;/div&gt;



&lt;p&gt;Once the direction is determined, it's stored in the signs[] array and never changes during the entire iterative solving process. The iteration only adjusts the magnitudes of the weights — direction stays fixed. With direction settled, there's still one more thing to handle: how much leverage the overall portfolio should carry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leverage Isn't About Going Higher — It Follows Volatility
&lt;/h2&gt;

&lt;p&gt;The strategy has a built-in leverage adjustment mechanism aimed at keeping portfolio volatility near a preset target. An analogy: you're driving, target speed 60 km/h. On a clear highway you can press the gas a little harder; in heavy city traffic you ease off. The strategy's leverage mechanism works similarly — when overall market volatility is low, leverage is dialed up to bring portfolio risk to target; when volatility is high, leverage is automatically reduced and exposure contracts.&lt;/p&gt;

&lt;p&gt;The strategy uses 1-hour K-lines, and there are exactly 8760 of them in a year, so the annualization factor is simply sqrt(8760). Switching to a different period means adjusting this factor: sqrt(35040) for 15-minute, sqrt(2190) for 4-hour, sqrt(365) for daily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function calcLeverage(w, cov) {
    var pv = portVol(w, cov) * Math.sqrt(8760);
    if (!isFinite(pv) || pv &amp;lt;= 0) return 1;
    return Math.min(TARGET_VOL / pv, MAX_LEVERAGE);
}

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

&lt;/div&gt;



&lt;p&gt;Dividing portfolio annualized volatility by target volatility gives the leverage multiplier to apply, with a cap to prevent runaway leverage under extreme conditions. This isn't about chasing maximum returns — it's risk budgeting. How much risk is traded for how much return is decided in advance, not drifting with the market.&lt;/p&gt;

&lt;p&gt;Once leverage is computed, the portfolio can open positions. At the same time, the strategy automatically adjusts the main loop's polling interval based on current volatility — the higher the volatility, the faster the refresh; the lower the volatility, the slower the pace, reducing unnecessary computation and trading friction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getAdaptiveSleep(w, cov) {
    var av = portVol(w, cov) * Math.sqrt(8760);
    if (av &amp;gt; 0.50) return FAST_INTERVAL;   // 15 min
    else if (av &amp;gt; 0.30) return MID_INTERVAL; // 30 min
    else return SLOW_INTERVAL;               // 60 min
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Rebalance
&lt;/h2&gt;

&lt;p&gt;The market moves every day, and weights will continuously drift away from the original design. Left alone, over time the actual portfolio risk can diverge significantly from the intended level. The strategy sets two rebalance triggers: one is time-based — every fixed number of hours, weights are forcibly recomputed. The other is drift-based — when an asset's actual weight deviates from its target weight beyond a threshold, or when its long/short direction flips, a rebalance is triggered immediately without waiting for the next scheduled one.&lt;/p&gt;

&lt;p&gt;When computing relative drift, the denominator is the absolute value of the previous weight. If an asset's weight has been squeezed very small (close to zero), relative drift is amplified and can trigger frequent rebalancing. The strategy has protection for this: when lastWeights[i] is zero, drift check is skipped to avoid false triggers from division by zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!lastWeights) {
    needRebal = true;
    rebalReason = 'Initial position build';
} else if ((now - lastTime) &amp;gt; REBALANCE_HOURS * 3600000) {
    needRebal = true;
    rebalReason = 'Scheduled rebalance (' + REBALANCE_HOURS + 'h)';
} else {
    for (var i = 0; i &amp;lt; weights.length; i++) {
        if ((weights[i] &amp;gt;= 0) !== (lastWeights[i] &amp;gt;= 0)) {
            needRebal = true;
            rebalReason = LABELS[i] + ' direction flip (long↔short)';
            break;
        }
        if (lastWeights[i] !== 0 &amp;amp;&amp;amp;
            Math.abs(weights[i] - lastWeights[i]) / Math.abs(lastWeights[i]) &amp;gt; DRIFT_THRESHOLD) {
            needRebal = true;
            rebalReason = LABELS[i] + ' weight drift exceeds threshold';
            break;
        }
    }
}

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

&lt;/div&gt;



&lt;p&gt;Once a rebalance is triggered, the strategy computes the target notional amount for each asset — positive for long, negative for short — then compares with current positions to decide whether to add, reduce, or switch direction. For direction switches, the opposite-side position is closed first before opening the new direction, to avoid holding both long and short at the same time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (targetSide === 'long' &amp;amp;&amp;amp; shortQty &amp;gt; 0) {
    exchange.CreateOrder(sym, "closesell", -1, shortQty);
}
if (targetSide === 'short' &amp;amp;&amp;amp; longQty &amp;gt; 0) {
    exchange.CreateOrder(sym, "closebuy", -1, longQty);
}
if (diffQty &amp;gt; 0) {
    exchange.CreateOrder(sym, targetSide === 'long' ? "buy" : "sell", -1, diffQty);
} else if (diffQty &amp;lt; 0) {
    exchange.CreateOrder(sym, targetSide === 'long' ? "closebuy" : "closesell",
                        -1, Math.abs(diffQty));
}

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

&lt;/div&gt;



&lt;p&gt;This avoids both excessive trading costs and letting the portfolio drift too far from its design intent. Under normal conditions, the rebalancing mechanism handles most market changes just fine. But markets aren't always normal, so an extra safety net is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  One More Layer — the Emergency Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;On top of normal rebalancing, the strategy has an emergency position-reduction mechanism. Every main loop cycle checks all current positions, compares against the last recorded prices, and sees how adversely each direction has moved. If longs drop more than 5% or shorts rally more than 5%, the strategy automatically cuts the position in half to contain losses without waiting for the next rebalance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (pos.side === 'long') {
    drop = (last - cur) / last;
} else if (pos.side === 'short') {
    drop = (cur - last) / last;
}
if (drop &amp;gt;= EMERGENCY_DROP) {
    Log('🚨 Emergency risk triggered! [' + label + '][' + pos.side + '] adverse move:',
        _N(drop * 100, 2) + '%', 'last:', _N(last, 4), '→ now:', _N(cur, 4));
    if (mode === 'live')
        emergencyReduceLive(sym, label, pos.side, EMERGENCY_REDUCE);
    else
        emergencyReducePaper(sym, label, cur, pos.side, EMERGENCY_REDUCE);
}

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

&lt;/div&gt;



&lt;p&gt;In live mode, the exchange's closing interface is called. In paper mode, margin and cash are updated in local state — margin is released proportionally to the position being closed, current P&amp;amp;L is calculated, and the trade is written to the log.&lt;/p&gt;

&lt;p&gt;One design detail worth calling out: after an emergency reduction fires, the baseline price is not immediately reset. The next check still uses the pre-trigger price as the reference. That means in markets that keep moving adversely, this mechanism will fire every loop cycle and keep cutting — reducing the position incrementally until it's exhausted, or until the next normal rebalance resets the baseline price. This is deliberate — in extreme one-way markets, continuously reducing controls losses better than reducing once and stopping. The side effect is that if the price briefly breaches the threshold and quickly rebounds, over-reduction may cause the strategy to miss the bounce. In normal markets this mechanism rarely triggers, but having it there means at least there's no scenario where you just ride a black swan all the way down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Looks Like Running Live
&lt;/h2&gt;

&lt;p&gt;Once the strategy is running on the platform, the dashboard displays in real time whether each asset is currently long or short, its weight, the current leverage multiplier, the portfolio's estimated annualized volatility, and the correlation matrix across the four assets. Floating P&amp;amp;L refreshes once per minute, and the strategy's core calculations switch polling frequency automatically based on market volatility.&lt;/p&gt;

&lt;p&gt;The 1-minute refresh isn't achieved through multi-threading — it's a nested sub-loop inside the main loop's wait period, waking up every minute to fetch latest prices, update floating P&amp;amp;L, and update the equity curve. When the accumulated sleep time reaches the strategy's polling interval, the sub-loop exits and the next strategy calculation cycle begins. The entire strategy is strictly single-threaded with no concurrency risk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function sleepWithPnlRefresh(totalSleepMs, weights, leverage, covMatrix, corrMatrix) {
    var elapsed = 0;
    while (elapsed &amp;lt; totalSleepMs) {
        var step = Math.min(PNL_INTERVAL, totalSleepMs - elapsed);
        Sleep(step);
        elapsed += step;
        var prices = getTickers();
        var equity = calcEquity(prices);
        var initCap = _G('pt_initCapital') || INIT_CAPITAL;
        _chart.add(0, [new Date().getTime(), equity]);
        LogProfit(equity - initCap, '&amp;amp;');
        renderDashboard(weights, leverage, covMatrix, corrMatrix, prices, totalSleepMs, true);
    }
}

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

&lt;/div&gt;



&lt;p&gt;The strategy also supports a paper-trading mode, letting you run the full logic and debug parameters without committing real capital. Once behavior confirms expectations, you can evaluate whether to switch to live. With this configuration, paper trading ran for a stretch — here's how it went.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paper Trading Results
&lt;/h2&gt;

&lt;p&gt;During the test period the strategy has been profitable overall for now. Position directions: BTC, XAU, and SPY all long; CL (crude) short. This happened to coincide with some easing in US–Iran tensions — geopolitical risk premium receded and crude prices fell quite visibly, and shorting crude was the main source of profit this round. The market movement during this period was friendly to the strategy — that doesn't mean the future will be. In fact, there are still quite a few unresolved issues in the strategy itself, and not being upfront about them wouldn't be honest.&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%2Fh5muo98ggada5kfd6zyp.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%2Fh5muo98ggada5kfd6zyp.png" alt=" " width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I Haven't Figured Out
&lt;/h2&gt;

&lt;p&gt;Through building this strategy, a few issues are still open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, liquidity for RWA contracts is far below BTC's. SPY, XAU, CL are still very new on-chain — slippage and order book depth are unknowns. Paper trading doesn't surface this, but it will show up for real in live trading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, the strategy relies on the assets maintaining some degree of non-correlation. But during periods of extreme market panic, correlations among risk assets tend to rise sharply, and the benefit of diversification gets eroded — precisely when you need protection most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third&lt;/strong&gt;, traditional assets have non-trading hours where prices barely move, while BTC trades 24/7. This means the covariance matrix underestimates true co-movement — after US equities close, BTC keeps running, and the volatility during that window has no corresponding SPY data. When traditional markets reopen, the strategy's view of co-movement may already be stale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth&lt;/strong&gt;, historical data is limited, and the short-term stability of the covariance matrix is questionable. The EWMA decay coefficient, target volatility, and rebalance thresholds are all heuristic values without rigorous validation. The short-side judgment uses the sign of covariance with an equal-volatility reference portfolio — an engineering approximation. When multiple assets simultaneously have negative covariance with the reference portfolio, whether shorting them all genuinely reduces total portfolio risk needs to be verified individually and cannot simply be assumed. Crypto assets themselves have fat tails, and real return distributions deviate from normal — in extreme conditions the covariance matrix will underestimate risk. That's a known bias you have to accept when working within the current framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fifth&lt;/strong&gt;, in live mode, during direction switches, after closing orders are sent the strategy waits a fixed duration and then proceeds to open new positions — without waiting for fill confirmation. If the exchange is slow to respond or there's network latency, there's a risk the old position isn't fully closed before the new one opens. Paper trading doesn't involve real fills so this isn't visible — before going live, the wait duration should be evaluated against the specific exchange's response time.&lt;/p&gt;

&lt;p&gt;None of these issues can be uncovered by paper trading alone. They need more detailed modeling and theoretical work — every assumption has to be checked carefully before you can know whether this logic actually holds up in the crypto market.&lt;/p&gt;

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

&lt;p&gt;The motivation for building this strategy was simple: the crypto market is in a bear cycle, RWA contracts happen to fill out the major asset class picture, and traditional finance has decades of allocation theory. The two came together, and it was worth a try. The code is complete, the logic is transparent, the parameters are tunable. But this is ultimately just a starting point — there's still a long way to go before it becomes an allocation strategy that genuinely stands up to live trading. If you have better ideas, let's improve it together.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The content above only documents the exploration process of a strategy and does not constitute investment advice. Contract trading carries significant risk. Paper-trading performance doesn't represent live results. Fully understand the risks before committing real capital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>rwa</category>
      <category>btc</category>
      <category>marketing</category>
    </item>
    <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://dev.to/quant001/designing-and-implementing-a-multi-role-ai-powered-trading-system-for-polymarket-2ceb</link>
      <guid>https://dev.to/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://dev.to/quant001/the-layering-rule-of-vibe-trading-what-to-leave-to-moving-averages-what-to-leave-to-ai-2k0o</link>
      <guid>https://dev.to/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://dev.to/quant001/multi-factor-strategies-arent-exclusive-to-big-firms-a-research-framework-for-independent-quants-38ka</link>
      <guid>https://dev.to/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://dev.to/quant001/binance-perpetual-futures-new-listing-automated-workflow-let-ai-watch-the-market-for-you-20n5</link>
      <guid>https://dev.to/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://dev.to/quant001/quantitative-trading-with-liquidation-data-an-ai-automated-strategy-based-on-liquidation-signals-k37</link>
      <guid>https://dev.to/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://dev.to/quant001/dont-panic-when-coins-pump-workflow-powered-shorting-to-catch-the-pullback-31g2</link>
      <guid>https://dev.to/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>
  </channel>
</rss>
