════════════════════════════════════════════════════════════════════ Primitive Strategy DSL - AI Code Generation Quick Reference ════════════════════════════════════════════════════════════════════ Version: 1.0 (Schema-driven) Contract bundle sha256:4324353df326db07 (schema + manifest + semantics; see scripts/primitive-contracts.lock.json) Purpose: Enable LLMs to generate valid primitive trading strategies ════════════════════════════════════════════════════════════════════ ⚠️ [0] ARCHITECTURE FIRST - DECLARATIVE GRAPH (NOT PROCEDURAL) ════════════════════════════════════════════════════════════════════ 🔴 CRITICAL: This is a DEPENDENCY GRAPH system, NOT step-by-step code! ❌ WRONG Mental Model: Sequential execution "indicators array executes first, then signals use the results" ✅ CORRECT Mental Model: Directed Acyclic Graph (DAG) - Each primitive = NODE with unique "id" - Dependencies = EDGES via "ref" in "inputs" - Execution order = topological sort (automatic, NOT array order) - Missing "ref" = broken edge = execution error ──────────────────────────────────────────────────────────────────── Example Graph Structure: indicators: [ {id: "sma20", type: "SMA", params: {period: 20, column: "Close"}}, {id: "sma50", type: "SMA", params: {period: 50, column: "Close"}} ], signals: [ {id: "buy", type: "Crossover", inputs: [{ref: "sma20"}, {ref: "sma50"}]}, {id: "sell", type: "Crossunder", inputs: [{ref: "sma20"}, {ref: "sma50"}]} ] Dependency Graph: sma20 ─┬─→ buy └─→ sell sma50 ─┴─→ (shared by both) Execution Flow: 1. Calculate sma20, sma50 (parallel - no dependencies) 2. Calculate buy, sell (after sma20/sma50 ready) ──────────────────────────────────────────────────────────────────── Key Rules: 1. "ref" creates dependency edges - MUST point to existing "id" in same or previous section - NO forward references within same section - Example: {ref: "my_indicator"} links to {id: "my_indicator"} 2. Array order does NOT determine execution - ✅ Valid: [signal_b, signal_a] if signal_b doesn't depend on signal_a - ❌ Invalid: Using ref before target is defined (compiler will catch) 3. Complex signals chain via refs indicators: [{id: "rsi", ...}], signals: [ {id: "oversold", type: "LessThan", inputs: [{ref: "rsi"}, {value: 30}]}, {id: "buy", type: "And", inputs: [{ref: "oversold"}, {ref: "trend_up"}]} ] Graph: rsi → oversold → buy 4. market_indicators use same graph model transformers: [ {name: "vix_raw", type: "IdentityTransformer", params: {indicator: "VIX", field: "Close"}}, // Depends on indicators {name: "vix_ma", type: "MovingAverageTransformer", params: {indicator: "VIX", window: 20}} // Also depends on indicators ] ════════════════════════════════════════════════════════════════════ ⛔ ABSOLUTE PROHIBITIONS - Model Constraints ════════════════════════════════════════════════════════════════════ ❌ FORBIDDEN #1: Inline Signal Definitions in inputs ──────────────────────────────────────────────────────────────────── NEVER nest "type" inside "inputs" array - all signals MUST have "id" and be pre-defined in the signals array. ❌ WRONG Example (Gemini-style error): { "id": "buy_cond", "type": "And", "inputs": [ { "type": "GreaterThan", // ❌ FATAL: Inline definition forbidden! "inputs": [{"column": "Close"}, {"ref": "ma"}] }, { "type": "GreaterThan", // ❌ Same error "inputs": [{"column": "Close"}, {"ref": "chandelier"}] } ] } ✅ CORRECT Pattern (Define-then-Reference): // Step 1: Define atomic comparisons { "id": "price_gt_ma", "type": "GreaterThan", "inputs": [{"column": "Close"}, {"ref": "ma"}] }, { "id": "price_gt_chandelier", "type": "GreaterThan", "inputs": [{"column": "Close"}, {"ref": "chandelier"}] }, // Step 2: Combine via references only { "id": "buy_cond", "type": "And", "inputs": [ {"ref": "price_gt_ma"}, // ✅ Reference only {"ref": "price_gt_chandelier"} // ✅ Reference only ] } 🎓 Rule: inputs array can ONLY contain: - {"ref": "signal_id"} (signal reference) - {"column": "Close"} (raw column) - {"market": "VIX", "transformer": "name"} (market data) - {"value": 50} (constant) NEVER {"type": ...} inside inputs! ──────────────────────────────────────────────────────────────────── ❌ FORBIDDEN #2: Direct Column/Value in Logic Signals ──────────────────────────────────────────────────────────────────── And/Or/Not signals can ONLY reference other signals via {ref: "id"}. ❌ WRONG: {"type": "And", "inputs": [{"column": "Close"}, {"ref": "ma"}]} ✅ CORRECT: First create comparison: {"id": "price_gt_ma", "type": "GreaterThan", ...} Then reference: {"type": "And", "inputs": [{"ref": "price_gt_ma"}, ...]} ──────────────────────────────────────────────────────────────────── ❌ FORBIDDEN #3: Missing Required "id" Field ──────────────────────────────────────────────────────────────────── Every indicator and signal MUST have a unique "id" field. ❌ WRONG: {"type": "SMA", "params": {...}} ✅ CORRECT: {"id": "sma20", "type": "SMA", "params": {...}} ──────────────────────────────────────────────────────────────────── ❌ FORBIDDEN #4: Extra Fields (additionalProperties: false) ──────────────────────────────────────────────────────────────────── Schema rejects any field not defined. NO "description", "comment", etc. ❌ WRONG: {"id": "buy", "type": "GreaterThan", "comment": "RSI > 70", ...} ✅ CORRECT: {"id": "buy", "type": "GreaterThan", "inputs": [...]} ════════════════════════════════════════════════════════════════════ 🔴 [1] SCHEMA HARD CONSTRAINTS (MUST FOLLOW EXACTLY) ════════════════════════════════════════════════════════════════════ The JSON Schema has "additionalProperties": false - any extra field = ERROR Type Strictness: - period/window: MUST be integer (20 ✅, "20" ❌) - type: MUST match enum exactly (case-sensitive) ✅ "SMA", "RSI", "GreaterThan" ❌ "SimpleMA", "Rsi", "Greater" - boolean: true/false (not "true"/"false" strings) - column: string enum ("Open", "High", "Low", "Close", "Volume") Required Fields: - id: required for all indicators/signals (unique within section) - type: required for all primitives - inputs: signal-dependent (many signals require it; verify per type in schema) - params: required if primitive has parameters Forbidden: - ❌ Extra fields not in Schema (e.g., "description", "comment") - ❌ Null values where not allowed - ❌ Wrong types (string for integer, etc.) Reference Rules: - ref: must point to existing id in indicators/signals - ref format for sub-values: "indicator_id.field" (e.g., "macd.signal") - column: direct column reference {column: "Close"} - value: literal value {value: 30} - market: market indicator reference {market: "VIX", transformer: "vix_ma"} ════════════════════════════════════════════════════════════════════ 🔴 [1.5] FUNDAMENTAL DATA CONSTRAINTS (MUST FOLLOW) ════════════════════════════════════════════════════════════════════ Scope: - Fundamental primitives are for SINGLE STOCKS only (CN/US) - Do NOT use fundamental_inputs for ETFs, indices, or crypto symbols Configuration Requirements: - Output scope for primitive DSL = strategy_definition fragment only (top-level keys in fragment: market_indicators / fundamental_inputs / trade_strategy), EXCLUDE capital_strategy - When fundamentals are used, MUST include top-level fundamental_inputs in the fragment - Do NOT wrap with outer strategy_definition object - point_in_time MUST be true for backtests to avoid look-ahead bias Metric Availability: - CN stocks: roe, pe_ttm, revenue_yoy, operating_cashflow, debt_ratio - US stocks: roe, revenue_yoy, operating_cashflow, debt_ratio - US pe_ttm: historical PIT timeseries is not available (typically null/NaN) Hard Rules for LLM Generation: - NEVER use US pe_ttm as hard entry/exit gate - If a fundamental metric is null/NaN, treat as risk-off: - block entry OR - reduce target_weight via ConditionalWeight/LinearScaleWeight ════════════════════════════════════════════════════════════════════ 🔴 [1.6] MARKET_INDICATORS CODE CONSTRAINTS (MUST FOLLOW) ════════════════════════════════════════════════════════════════════ market_indicators[].code accepts exactly two kinds of value: 1. A market index code from the published list below. 2. A tradable symbol -- stock, ETF or crypto -- that you have VERIFIED exists via the platform's symbol search API. Do not write a symbol from memory: vendor tickers differ (BTC, not BTC-USD / BTCUSDT / XBTUSD). The same string is used in all three places: 1. market_indicators.indicators[].code 2. transformer params.indicator 3. signal input {"market": "...", "transformer": "..."} ──────────────────────────────────────────────────────────────────── Published market index codes (20) -- the closed set ──────────────────────────────────────────────────────────────────── 000001.SH 上证综指 000300.SH 沪深300 000905.SH 中证500 399001.SZ 深证成指 399006.SZ 创业板指 SPX 标普500指数 IXIC 纳斯达克指数 VIX 波动率指数 NDX 纳斯达克100指数 DJIA 道琼斯工业指数 DJI 道琼斯指数 RUT 罗素2000指数 HSI 恒生指数 GDAXI 德国DAX指数 N225 日经225指数 KS11 韩国综合指数 AS51 澳大利亚标普200指数 SENSEX 印度孟买SENSEX指数 BTC_MVRV_ZSCORE BTC_MVRV_ZSCORE BTC_NVT BTC_NVT ──────────────────────────────────────────────────────────────────── Data shape: every supported indicator returns an OHLCV-shaped table ──────────────────────────────────────────────────────────────────── This includes the published non-price series -- BTC_MVRV_ZSCORE and BTC_NVT are on-chain metrics and are fully supported, because the provider returns them in the same table. What decides support is whether a real code exists whose provider returns such a table, NOT whether the underlying quantity is a price. transformer params.field must name a field that indicator actually provides. Prefer the canonical fields published by the contract: Open, High, Low, Close, Volume Close is the normal default. Providers may also expose additional fields such as Adj Close or Adj Factor; the engine validates against the fields actually returned, so those are valid where present. A field the indicator does not provide is REJECTED by the engine. It used to fall back to Close silently, which meant a wrong field produced a complete, plausible, WRONG strategy. It now fails with the fields actually available. ❌ NEVER invent a semantic code QDII_PREMIUM, FUND_FLOW, VALUATION_PERCENTILE, FEAR_GREED and anything like them do not exist. A code that is neither in the list above nor verifiable through symbol search is wrong even if the concept is real. ❌ NEVER invent a field premium_rate, value, fund_flow and similar names exist on no indicator. If the quantity a user asks for has no real code behind it, build the strategy from what does exist rather than approximating the missing part. Correct market indicator pattern: { "market_indicators": { "indicators": [{"code": "BTC"}], "transformers": [ { "name": "btc_rsi", "type": "RSITransformer", "params": {"indicator": "BTC", "window": 14, "field": "Close"} } ] } } Crypto naming rule: - Bitcoin price symbol = BTC (NOT BTC-USD, XBTUSD, BTCUSDT) - BTC on-chain MVRV Z-Score = BTC_MVRV_ZSCORE - With BTC_MVRV_ZSCORE use IdentityTransformer/ZScoreTransformer or a direct threshold comparison ════════════════════════════════════════════════════════════════════ 🔴 [1.7] FIXED-UNIVERSE CROSS-SECTIONAL STRATEGIES (RANK / TOP-N / ROTATION) ════════════════════════════════════════════════════════════════════ Choose one final execution/output shape per strategy: A. PER-SYMBOL SIGNAL EXECUTION (default, everything in section [2]) Each symbol is evaluated independently against its own history and trades on its own signal. "Buy QQQ when its 20d EMA crosses above its 60d EMA." -> outputs.buy_signal / outputs.sell_signal, no target_weight. B. CROSS-SECTIONAL COMPLETE TARGET-VECTOR EXECUTION (this section) All symbols of the Portfolio are COMPARED AGAINST EACH OTHER on the same decision session, then the best N are held as one complete weight vector. "Among my Portfolio's symbols, hold the 3 strongest each month." -> outputs.buy_signal + outputs.sell_signal + target_weight. Use B when the user asks to RANK, COMPARE, SELECT TOP N, ROTATE, or ALLOCATE AMONG the Portfolio's existing candidates. Anything else uses A. What is forbidden is emitting BOTH final execution contracts from one graph -- a per-symbol signal contract and a cross-sectional complete target vector are mutually exclusive ways to execute the same Portfolio. Combining INPUTS is not only allowed, it is the normal case. A Cross-sectional graph may freely use: - per-symbol technical features (SMA, RSI, RateOfChange, MACD, ...); - point-in-time fundamental features where the market supports them; - per-symbol boolean eligibility; - market_indicators / market-wide timing conditions (VIX percentile, an index trend, ...) as inputs to Eligibility or to Feature construction. A market-wide timing gate ANDed into Eligibility is a supported and useful pattern: when the gate is false, every symbol becomes ineligible, TopN selects nothing and EqualWeight emits an all-zero vector, i.e. the Portfolio goes to cash. ──────────────────────────────────────────────────────────────────── Cross-sectional primitives (evaluation_domain = cross_sectional) ──────────────────────────────────────────────────────────────────── | Type | Input semantic types | Output | Key Parameters | |--------------------|----------------------------|-----------|----------------| | RebalanceCadence | (none) | cadence | frequency:str=monthly_first_session, bootstrap_on_first_session:bool=true | | CrossSectionalRank | numeric + boolean + cadence | rank | direction:str=descending | | TopN | rank + cadence | selection | n:int=1 | | EqualWeight | selection + cadence | weight | rebalance_policy:str=scheduled | Semantic input types are POSITIONAL and must be wired in the listed order. numeric = a per-symbol indicator/expression (the ranking feature) boolean = a per-symbol boolean signal (eligibility gate) cadence = the RebalanceCadence node rank = the CrossSectionalRank node selection = the TopN node ──────────────────────────────────────────────────────────────────── THE CANONICAL CHAIN (do not deviate) ──────────────────────────────────────────────────────────────────── Feature (numeric, per-symbol) │ ├──> CrossSectionalRank ──> TopN ──┬──> Not ──> sell_signal │ ▲ ▲ ▲ │ Eligibility (boolean) ─┘ │ └──> EqualWeight ──> target_weight │ ▲ RebalanceCadence ────────────────┴────────────────┘ (ONE node, shared by Rank, TopN and EqualWeight) Feature -> Eligibility -> RebalanceCadence -> CrossSectionalRank -> TopN -> Not(TopN) -> EqualWeight 🔴 The SAME cadence node id must be referenced by CrossSectionalRank, TopN and EqualWeight. Three separate RebalanceCadence nodes with identical params are NOT equivalent -- ranking, selection and weighting would drift out of step. Canonical outputs (all three are required, no substitutes): outputs.buy_signal = the TopN node id outputs.sell_signal = the Not(TopN) node id outputs.target_weight = the EqualWeight node id ──────────────────────────────────────────────────────────────────── CADENCE AND EMISSION ──────────────────────────────────────────────────────────────────── RebalanceCadence.frequency: daily | weekly_first_session | monthly_first_session - Weekly/monthly mean the first ACTUAL Portfolio trading session of the ISO week/month -- not a wall-clock Monday or the 1st of the month. RebalanceCadence.bootstrap_on_first_session: always set it EXPLICITLY. - true -> act on the first observed Portfolio session - false -> wait for the next full calendar period - Daily cadence ignores this flag. EqualWeight.rebalance_policy: scheduled | membership_change - scheduled -> emit a complete weight vector on EVERY cadence session, even when the selected set did not change (re-levels weights that have drifted; more turnover). - membership_change -> emit only when the selected membership actually changes (less turnover; weights drift between changes). Between emissions target_weight is intentionally NaN. NaN means "no instruction on this session" -- it must never be forward-filled or replaced with 0. ──────────────────────────────────────────────────────────────────── CAPITAL STRATEGY (hard requirement) ──────────────────────────────────────────────────────────────────── EqualWeight emits a COMPLETE target-weight vector covering every symbol at once. Executing it needs a capital strategy that applies the whole vector atomically (atomic_complete_vector_compatible). In the current contract that is: RebalancingCapitalStrategy Every other capital strategy is REJECTED during validation, before any data is loaded -- a Cross-sectional graph paired with one will not run. Gross exposure and retained cash belong to the capital strategy (gross_exposure), NOT to EqualWeight. The capital strategy is chosen by the App/user, NOT emitted in your fragment. ──────────────────────────────────────────────────────────────────── COMPLETE VALIDATED EXAMPLE (US mega-cap momentum, monthly Top 3) ──────────────────────────────────────────────────────────────────── { "trade_strategy": { "indicators": [ {"id": "feature", "type": "RSI", "params": {"period": 14, "column": "Close"}}, {"id": "trend_ma", "type": "SMA", "params": {"period": 150, "column": "Close"}} ], "signals": [ {"id": "eligible", "type": "GreaterThan", "inputs": [{"column": "Close"}, {"ref": "trend_ma"}], "params": {}}, {"id": "cadence", "type": "RebalanceCadence", "inputs": [], "params": {"frequency": "monthly_first_session", "bootstrap_on_first_session": true}}, {"id": "rank", "type": "CrossSectionalRank", "inputs": [{"ref": "feature"}, {"ref": "eligible"}, {"ref": "cadence"}], "params": {"direction": "descending"}}, {"id": "selected", "type": "TopN", "inputs": [{"ref": "rank"}, {"ref": "cadence"}], "params": {"n": 3}}, {"id": "not_selected", "type": "Not", "inputs": [{"ref": "selected"}], "params": {}}, {"id": "weight", "type": "EqualWeight", "inputs": [{"ref": "selected"}, {"ref": "cadence"}], "params": {"rebalance_policy": "membership_change"}} ], "outputs": { "buy_signal": "selected", "sell_signal": "not_selected", "target_weight": "weight", "indicators": [ {"id": "feature", "output_name": "cross_sectional_feature"}, {"id": "eligible", "output_name": "cross_sectional_eligible"} ] } } } outputs.indicators is optional explainability. It resolves against the PER-SYMBOL cache only, so it may reference the feature and eligibility nodes but NEVER CrossSectionalRank / TopN / EqualWeight. Such an entry is REJECTED during validation -- both when authoring through the App and in the engine's own semantic validation -- because the engine could never write that column. Selection is already visible through the signal output and target_weight. ──────────────────────────────────────────────────────────────────── REFERENCE SYNTAX ──────────────────────────────────────────────────────────────────── - Multi-output per-symbol features need the dotted field: {"ref": "macd.histogram"} - Cross-sectional operators have exactly ONE output -- reference them bare: {"ref": "rank"} ✅ {"ref": "rank.rank"} ❌ {"ref": "selected.selection"} ❌ ──────────────────────────────────────────────────────────────────── OUTPUT SCOPE ──────────────────────────────────────────────────────────────────── Emit the strategy_definition FRAGMENT only. Allowed top-level keys: market_indicators / fundamental_inputs / trade_strategy NEVER emit: symbols, candidate tickers, Portfolio name/code/market/currency, start_date, capital_strategy, or any scope/discriminator field. The Portfolio supplies its own fixed symbol list. ──────────────────────────────────────────────────────────────────── ❌ CROSS-SECTIONAL ANTI-PATTERNS ──────────────────────────────────────────────────────────────────── ❌ No cadence node at all CrossSectionalRank / TopN / EqualWeight each REQUIRE a cadence input. Omitting it is a schema error, not a "rebalance every bar" shortcut. ⚠️ Every HELD or positively weighted symbol must trade on the rebalance session Complete-vector execution requires a REAL bar on the rebalance session for every symbol that is currently held OR receives a target weight above zero. A candidate that is neither held nor selected can be missing a bar without stopping anything. When the rule is broken the engine RAISES and the run fails -- deliberate, because a complete weight vector priced off a stale bar corrupts the equity curve silently. Two separate causes, and picking ETFs only removes the first: 1. A symbol cannot trade that day. An A-share stock suspended, or locked limit-up/limit-down, has no real bar. ETFs are largely immune here: a suspended constituent does not stop the ETF itself trading. 2. The candidates do not share one trading calendar. Distinct ETFs differ by a few sessions over the years -- a single-day halt, or a cross-border QDII ETF closed for an overseas holiday while domestic ETFs trade. The Portfolio's calendar is the UNION of its candidates, so a session one candidate misses while another trades is a session where a rebalance CAN fail -- it does fail only if that candidate is held or selected then. Mixing domestic ETFs with QDII or commodity ETFs is the common way to set this up. Real example: 159915 / 513100 / 518880 over 2017-2026 span 2320 union sessions; 159915 misses 2021-02-08 and 513100 misses 2022-01-13. The run replays full history, so this is not a "bad day" that clears by itself: a fixed historical gap is still there on the next run from the same start date. Cadence decides WHICH sessions become actionable and therefore get checked; it does not soften the outcome -- daily, weekly or monthly, one actionable session hitting a missing bar fails the entire run. Before committing to a pool, compare each candidate's set of trading days. Screen individual stocks for suspension and liquidity as well. ❌ A separate cadence node per consumer {"id":"cadence_rank",...}, {"id":"cadence_topn",...}, {"id":"cadence_w",...} Use one node and reference it three times. ❌ Partial chain Rank without TopN, TopN without EqualWeight, or target_weight without buy_signal/sell_signal. All three canonical outputs must be present. ❌ Inline construction instead of node refs "inputs": [{"type": "CrossSectionalRank", ...}] -- inputs take {"ref": id} only. ❌ TopN.n larger than the Portfolio's candidate count n must be <= the number of symbols. Authoring supports 1-10 symbols, so n > 10 is always wrong. ❌ Pairing with a non-atomic capital strategy Anything outside [RebalancingCapitalStrategy] cannot execute a complete vector. ❌ Dynamic Universe fields No screening, no filters, no index-constituent rebuilds, no "universe" key. The candidate pool is FIXED and comes from the Portfolio. ❌ Leverage or long/short Cross-sectional output is long-only, daily data only, gross exposure <= 1. Negative weights and >1 gross exposure are not supported. ════════════════════════════════════════════════════════════════════ 🔴 [1.8] TARGET ALLOCATION (TargetAllocation) — fixed/dynamic desired target exposure ════════════════════════════════════════════════════════════════════ Use TargetAllocation for a desired target exposure over assets the user already knows -- fixed ("SPY 60% + TLT 40%, rebalanced quarterly") OR dynamic (the target follows a signal). It is a third execution shape, distinct from per-symbol signals and Cross-sectional ranking: A. PER-SYMBOL SIGNAL EXECUTION (section [2]) -> outputs.buy_signal/sell_signal B. CROSS-SECTIONAL RANK / TOP-N (section [1.7]) -> EqualWeight complete vector C. TARGET ALLOCATION (this section) -> authored weight or dynamic target_signal + optional gate Canonical semantics: - portfolio_construction.type = "TargetAllocation" - allocations[]: EXACTLY ONE of weight OR target_signal per allocation: weight = the FINAL executable target weight (NOT multiplied by any exposure scalar); target_signal = the id of a real supported weight-output signal (e.g. ConditionalWeight / LinearScaleWeight) - unused weight = cash (sum of authored weights <= 1.0) - optional allocations[].enabled_by = a boolean signal id; false -> target 0 - enabled_by may reference an external market_indicators observation symbol, which does NOT become a holding and is NOT required in symbols[] - optional allocations[].direction = both | increase_only | decrease_only (default both); applies at explicit rebalance events, not continuous drift - optional allocations[].staged_deployment (bounded deployment): static weight only + direction MUST be decrease_only; levels / unit / anchor_lookback_sessions / gate / reset_on_new_high are ALL required. Never approximate unsupported state machines or invent ADD/REDUCE commands. - rebalance.frequency is REQUIRED: none | monthly | quarterly - capital_strategy.name = "RebalancingCapitalStrategy" - capital_strategy.params.gross_exposure = 1.0 - trade_strategy.outputs = {} (empty — target weights come from portfolio_construction, never from outputs) Do NOT use Cross-sectional EqualWeight / CrossSectionalRank / TopN for a fixed allocation. Those rank and re-select candidates; TargetAllocation keeps the assets you authored and only re-levels their authored weights. Minimal canonical Agent Lab 60/40 example (full strategy_definition; NOT a Web-editor fragment): { "strategy_definition": { "trade_strategy": { "indicators": [], "signals": [], "outputs": {} }, "portfolio_construction": { "type": "TargetAllocation", "allocations": [ { "symbol": "SPY", "weight": 0.60 }, { "symbol": "TLT", "weight": 0.40 } ], "rebalance": { "frequency": "quarterly" } }, "capital_strategy": { "name": "RebalancingCapitalStrategy", "params": { "initial_capital": 100000, "gross_exposure": 1.0 } } } } This example is the Agent Lab / full-StrategyDefinition shape only. Web primitive-editor fragment authoring must continue following the fragment-only rules above: top-level keys market_indicators / fundamental_inputs / trade_strategy, EXCLUDE capital_strategy, and do NOT wrap the fragment in an outer strategy_definition object. Note the capital_strategy shape is { name, params } — NOT a primitive node { type, id, params }. TargetAllocation and Cross-sectional share RebalancingCapitalStrategy, but they are different execution contracts. Full semantics: https://www.myinvestpilot.com/docs/primitives/target-allocation ════════════════════════════════════════════════════════════════════ 📋 [2] PRIMITIVE QUICK REFERENCE (Auto-generated from Schema) ════════════════════════════════════════════════════════════════════ ──────────────────────────────────────────────────────────────────── INDICATORS (Technical Analysis Primitives) ──────────────────────────────────────────────────────────────────── | Type | Key Parameters | |----------------------|-----------------------------------------------------| | ChandelierExit | period:int=60, multiplier:num=4, add_ma:bool=false, ma_period:int=250 | | Constant | value:num=1 | | DonchianChannel | period:int=20, upper_column:str=High, lower_column:str=Low, exclude_current:bool=true | | HighestValue | column:str=Close, period:int=14, output_format:str=series | | LowestValue | column:str=Close, period:int=14, output_format:str=series | | PercentFromHighest | column:str=Close, period:int=14, output_format:str=series | | PercentFromLowest | column:str=Close, period:int=14, output_format:str=series | | DebtRatio | column:str=debt_ratio | | OperatingCashflow | column:str=operating_cashflow | | PE_TTM | column:str=pe_ttm | | ROE | column:str=roe | | RevenueYoY | column:str=revenue_yoy | | MACD | fast_period:int=12, slow_period:int=26, signal_period:int=9, column:str=Close, output_format:str=dict | | RSI | period:int=14, column:str=Close | | RateOfChange | periods:int=1, column:str=Close | | EMA | period:int=20, column:str=Close, adjust:bool=false | | SMA | period:int=20, column:str=Close | | Stochastic | k_period:int=14, d_period:int=3, d_method:str=simple, output_format:str=dict | | ATR | period:int=14, method:str=sma | | BollingerBands | period:int=20, std_dev:num=2, column:str=Close, method:str=sma | ──────────────────────────────────────────────────────────────────── SIGNALS (Comparison & Logic Primitives) ──────────────────────────────────────────────────────────────────── | Type | Key Parameters | |----------------------|-----------------------------------------------------| | Comparison | comparison:str=greater, threshold:str=null, epsilon:num=1e-10 | | Crossover | mode:str=cross | | Crossunder | mode:str=cross | | GreaterThan | epsilon:num=0 | | InRange | include_lower:bool=true, include_upper:bool=true | | LessThan | epsilon:num=0 | | CrossAbove | threshold:num=0, strict:bool=false | | CrossBelow | threshold:num=0, strict:bool=false | | CrossSectionalRank | direction:str=descending | | EqualWeight | rebalance_policy:str=scheduled | | RebalanceCadence | frequency:str=monthly_first_session, bootstrap_on_first_session:bool=true | | TopN | n:int=1 | | And | fill_method:str=null | | Not | fill_method:str=null, preserve_na:bool=false | | Or | fill_method:str=null | | Add | operation:str=add, comparison:str=greater, threshold:str=null, absolute:bool=false, return_calculation:bool=false | | Divide | operation:str=divide, comparison:str=greater, threshold:str=null, absolute:bool=false, return_calculation:bool=false | | Multiply | operation:str=multiply, comparison:str=greater, threshold:str=null, absolute:bool=false, return_calculation:bool=false | | Subtract | operation:str=subtract, comparison:str=greater, threshold:str=null, absolute:bool=false, return_calculation:bool=false | | PercentChange | periods:int=1, threshold:num=0, comparison:str=greater, is_absolute:bool=false | | Streak | min_length:int=2, max_length:str=null, match_type:str=true | | StockBondSwitch | default_to_stock:bool=true | | Lag | periods:int=1, fill_value:str=null | | ConditionalWeight | true_weight:num, false_weight:num | | LinearScaleWeight | min_indicator:num, max_indicator:num, min_weight:num, max_weight:num, clip:bool=true | ──────────────────────────────────────────────────────────────────── TRANSFORMERS (Market Indicator Processing) ──────────────────────────────────────────────────────────────────── | Type | Key Parameters | |-----------------------------|----------------------------------------------| | ChandelierExitTransformer | indicator:str, period:int=30, multiplier:num=2 | | IdentityTransformer | indicator:str, field:str=Close | | MovingAverageTransformer | indicator:str, window:int, method:str=simple, field:str=Close | | PercentileRankTransformer | indicator:str, lookback:int, field:str=Close | | RSITransformer | indicator:str, window:int=14, field:str=Close | | RelativeStrengthTransformer | indicator:str, reference:str, window:str=null, value:str=null, field:str=Close | | ZScoreTransformer | indicator:str, window:int, field:str=Close | ════════════════════════════════════════════════════════════════════ 📚 [3] PROGRESSIVE EXAMPLES (From Simple to Complex) ════════════════════════════════════════════════════════════════════ ──────────────────────────────────────────────────────────────────── LEVEL 1: Minimal Valid Strategy (Golden Cross) ──────────────────────────────────────────────────────────────────── Description: 最经典的趋势跟踪策略,使用短期和长期移动平均线的交叉信号进行买卖决策。买入条件:短期均线上穿长期均线;卖出条件:短期均线下穿长期均线 { "trade_strategy": { "indicators": [ { "id": "shortMA", "type": "SMA", "params": { "period": 11, "column": "Close" } }, { "id": "longMA", "type": "SMA", "params": { "period": 22, "column": "Close" } } ], "signals": [ { "id": "buy_signal", "type": "Crossover", "params": { "mode": "simple" }, "inputs": [ { "ref": "shortMA" }, { "ref": "longMA" } ] }, { "id": "sell_signal", "type": "Crossunder", "params": { "mode": "simple" }, "inputs": [ { "ref": "shortMA" }, { "ref": "longMA" } ] } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "sell_signal" } } } Complexity: 2 indicators → 2 signals → outputs Graph: sma20 ─┬─→ buy (Crossover) sma50 ─┴─→ sell (Crossunder) 🎓 Foundation Pattern: Define-then-Reference Step 1: Define indicators (shortMA, longMA) Step 2: Define signals (buy_signal, sell_signal) Step 3: Reference in outputs ⚠️ NEVER inline "type" inside "inputs" array! ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 2: Multi-Indicator with Logic (RSI + Dual MA) ──────────────────────────────────────────────────────────────────── Description: 结合RSI超买超卖信号与双均线趋势确认的复合策略。买入条件:RSI超卖(≤30)或20日均线上穿50日均线;卖出条件:RSI超买(≥70)或价格跌破20日均线 { "trade_strategy": { "indicators": [ { "id": "rsi", "type": "RSI", "params": { "period": 14, "column": "Close" } }, { "id": "sma20", "type": "SMA", "params": { "period": 20, "column": "Close" } }, { "id": "sma50", "type": "SMA", "params": { "period": 50, "column": "Close" } }, { "id": "threshold_30", "type": "Constant", "params": { "value": 30 } }, { "id": "threshold_70", "type": "Constant", "params": { "value": 70 } } ], "signals": [ { "id": "rsi_oversold", "type": "LessThan", "inputs": [ { "ref": "rsi" }, { "ref": "threshold_30" } ] }, { "id": "rsi_overbought", "type": "GreaterThan", "inputs": [ { "ref": "rsi" }, { "ref": "threshold_70" } ] }, { "id": "price_above_sma20", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "sma20" } ] }, { "id": "sma20_above_sma50", "type": "GreaterThan", "inputs": [ { "ref": "sma20" }, { "ref": "sma50" } ] }, { "id": "buy_condition", "type": "Or", "inputs": [ { "ref": "rsi_oversold" }, { "ref": "sma20_above_sma50" } ] }, { "id": "price_not_above_sma20", "type": "Not", "inputs": [ { "ref": "price_above_sma20" } ] }, { "id": "sell_condition", "type": "Or", "inputs": [ { "ref": "rsi_overbought" }, { "ref": "price_not_above_sma20" } ] } ], "outputs": { "buy_signal": "buy_condition", "sell_signal": "sell_condition", "indicators": [ { "id": "rsi", "output_name": "rsi" }, { "id": "sma20", "output_name": "sma20" }, { "id": "sma50", "output_name": "sma50" }, { "id": "threshold_30", "output_name": "rsi_threshold_30" }, { "id": "threshold_70", "output_name": "rsi_threshold_70" }, { "id": "rsi_oversold", "output_name": "rsi_oversold" }, { "id": "rsi_overbought", "output_name": "rsi_overbought" }, { "id": "price_above_sma20", "output_name": "price_above_sma20" }, { "id": "sma20_above_sma50", "output_name": "sma20_above_sma50" }, { "id": "buy_condition", "output_name": "buy_condition" }, { "id": "price_not_above_sma20", "output_name": "price_not_above_sma20" }, { "id": "sell_condition", "output_name": "sell_condition" } ] } } } Complexity: 5 indicators → 7 signals (Or, Not logic) → outputs Key Features: - Multiple indicators (RSI, SMA20, SMA50) - Constant thresholds (30, 70) - Boolean logic (Or, Not) - Complex buy/sell conditions 🎓 Logic Composition Pattern: Step 1: Define base comparisons (rsi_oversold, price_above_sma20, etc.) Step 2: Combine via Or/Not/And (buy_condition, sell_condition) Step 3: Reference combined signals in outputs ⚠️ Or/Not/And can ONLY reference signals, not raw data! ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 3: Market Filter + Multi-Confirmation (VIX + Chandelier) ──────────────────────────────────────────────────────────────────── Description: 使用VIX波动率指标过滤市场环境,结合250日均线和吊灯止损的趋势跟踪策略。买入条件:价格高于均线和吊灯止损,且VIX百分位低于75或VIX下降;卖出条件:价格低于均线和吊灯止损,且市场环境恶化。(美股用VIX,其他市场需调整) { "market_indicators": { "indicators": [ { "code": "VIX" } ], "transformers": [ { "name": "vix_raw", "type": "IdentityTransformer", "params": { "indicator": "VIX", "field": "Close" } }, { "name": "vix_percentile", "type": "PercentileRankTransformer", "params": { "indicator": "VIX", "lookback": 252, "field": "Close" } }, { "name": "vix_ma", "type": "MovingAverageTransformer", "params": { "indicator": "VIX", "window": 20, "method": "simple", "field": "Close" } } ] }, "trade_strategy": { "indicators": [ { "id": "ma_indicator", "type": "SMA", "params": { "period": 250, "column": "Close" } }, { "id": "atr_indicator", "type": "ATR", "params": { "period": 60 } }, { "id": "chandelier_exit_indicator", "type": "ChandelierExit", "params": { "period": 60, "multiplier": 4 } }, { "id": "constant_75", "type": "Constant", "params": { "value": 75 } } ], "signals": [ { "id": "price_gt_ma", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "ma_indicator" } ] }, { "id": "price_gt_ce", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "chandelier_exit_indicator" } ] }, { "id": "market_volatility_low", "type": "LessThan", "epsilon": 0.5, "inputs": [ { "market": "VIX", "transformer": "vix_percentile" }, { "ref": "constant_75" } ] }, { "id": "market_volatility_declining", "type": "LessThan", "inputs": [ { "market": "VIX", "transformer": "vix_raw" }, { "market": "VIX", "transformer": "vix_ma" } ] }, { "id": "price_lt_ma", "type": "LessThan", "inputs": [ { "ref": "ma_indicator" }, { "column": "Close" } ] }, { "id": "price_lt_ce", "type": "LessThan", "inputs": [ { "ref": "chandelier_exit_indicator" }, { "column": "Close" } ] }, { "id": "market_condition_good", "type": "Or", "inputs": [ { "ref": "market_volatility_low" }, { "ref": "market_volatility_declining" } ] }, { "id": "price_conditions", "type": "And", "inputs": [ { "ref": "price_gt_ma" }, { "ref": "price_gt_ce" } ] }, { "id": "technical_buy_conditions", "type": "And", "inputs": [ { "ref": "price_conditions" }, { "ref": "price_gt_ma" } ] }, { "id": "buy_signal_condition", "type": "And", "inputs": [ { "ref": "technical_buy_conditions" }, { "ref": "market_condition_good" } ] }, { "id": "price_conditions_sell", "type": "And", "inputs": [ { "ref": "price_lt_ma" }, { "ref": "price_lt_ce" } ] }, { "id": "market_condition_bad", "type": "Not", "inputs": [ { "ref": "market_condition_good" } ] }, { "id": "sell_signal_condition", "type": "And", "inputs": [ { "ref": "price_conditions_sell" }, { "ref": "market_condition_bad" } ] } ], "outputs": { "buy_signal": "buy_signal_condition", "sell_signal": "sell_signal_condition", "indicators": [ { "id": "ma_indicator", "output_name": "ma" }, { "id": "atr_indicator", "output_name": "atr" }, { "id": "chandelier_exit_indicator", "output_name": "chandelier_stop" }, { "id": "market_volatility_low", "output_name": "vix_percentile_low" }, { "id": "market_volatility_declining", "output_name": "vix_declining" }, { "id": "market_condition_good", "output_name": "market_ok" }, { "id": "price_conditions", "output_name": "price_conditions" }, { "id": "technical_buy_conditions", "output_name": "tech_buy" }, { "id": "buy_signal_condition", "output_name": "buy_condition" }, { "id": "sell_signal_condition", "output_name": "sell_condition" }, { "id": "market_condition_bad", "output_name": "market_bad" } ], "market_indicators": [ { "market": "VIX", "transformer": "vix_raw", "output_name": "vix_raw" }, { "market": "VIX", "transformer": "vix_percentile", "output_name": "vix_percentile" }, { "market": "VIX", "transformer": "vix_ma", "output_name": "vix_ma" } ] } } } Complexity: market_indicators + 3 indicators → 12 signals → multiple outputs Key Features: - market_indicators section (VIX data) - transformers (PercentileRank, MovingAverage) - Market filter logic (VIX percentile > MA) - Chandelier Exit stop loss - Multiple And/Or combinations - Rich outputs (10+ indicator outputs + 3 market outputs) 🎓 Multi-Level Composition Pattern: Step 1: Define all atomic comparisons (price_gt_ma, price_gt_ce, market_volatility_low, etc.) Step 2: Combine into intermediate signals (price_conditions, market_condition_good) Step 3: Compose intermediate signals into final (buy_signal_condition) ⚠️ Each level references previous level - NO inline definitions! Example: price_gt_ma (atomic) ──┐ price_gt_ce (atomic) ──┴─→ price_conditions (intermediate) ↓ market_volatility_low ─────→ market_condition_good ↓ buy_signal_condition (final) ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 4: Streak Confirmation + Stock-Bond Switch ──────────────────────────────────────────────────────────────────── Description: 基于大盘指数趋势的资产轮动策略,使用120日均线判断市场趋势,并要求连续3日确认减少假信号。指数价格高于均线3日则持有股票ETF,否则切换到债券ETF。(中国用沪深300,其他市场需替换指数) { "market_indicators": { "indicators": [ { "code": "000300.SH" } ], "transformers": [ { "name": "hs300_raw", "type": "IdentityTransformer", "params": { "indicator": "000300.SH", "field": "Close" } }, { "name": "hs300_ma120", "type": "MovingAverageTransformer", "params": { "indicator": "000300.SH", "window": 120, "method": "simple", "field": "Close" } } ] }, "trade_strategy": { "indicators": [ { "id": "constant_one", "type": "Constant", "params": { "value": 1 } } ], "signals": [ { "id": "market_trend_up_raw", "type": "GreaterThan", "inputs": [ { "market": "000300.SH", "transformer": "hs300_raw" }, { "market": "000300.SH", "transformer": "hs300_ma120" } ] }, { "id": "market_trend_up_confirmed", "type": "Streak", "params": { "condition": "true", "min_length": 3 }, "inputs": [ { "ref": "market_trend_up_raw" } ] }, { "id": "stock_bond_buy", "type": "StockBondSwitch", "params": { "default_to_stock": true }, "inputs": [ { "ref": "market_trend_up_confirmed" } ] }, { "id": "stock_bond_sell", "type": "Not", "inputs": [ { "ref": "stock_bond_buy" } ] } ], "outputs": { "buy_signal": "stock_bond_buy", "sell_signal": "stock_bond_sell", "indicators": [ { "id": "market_trend_up_raw", "output_name": "trend_signal_raw" }, { "id": "market_trend_up_confirmed", "output_name": "trend_signal" } ], "market_indicators": [ { "market": "000300.SH", "transformer": "hs300_raw", "output_name": "hs300_price" }, { "market": "000300.SH", "transformer": "hs300_ma120", "output_name": "hs300_ma120" } ] } } } Complexity: market_indicators + Streak + StockBondSwitch Key Features: - Streak signal (consecutive days confirmation) - StockBondSwitch (special signal for asset rotation) - Market indicator transformers - Reduces false signals with min_length=3 ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 5: Lag + And Pattern (2-Day Confirmation for Volatility Control) ──────────────────────────────────────────────────────────────────── Description: 使用QQQ相对强度判断趋势,配合Lag原语实现2日连续确认机制,过滤杠杆ETF交易中的假突破。买入:RS>101;卖出:RS<99连续2日确认。该策略在2020-2025近6年回测中,将最大回撤从-93.83%降至-53.96%,同时年化收益达到27.28%,展示了Lag+And组合实现信号延迟确认的经典模式。 { "trade_strategy": { "outputs": { "buy_signal": "rs_above_101", "indicators": [ { "id": "rs_above_101", "output_name": "buy_cond" }, { "id": "rs_below_99", "output_name": "sell_cond_raw" }, { "id": "rs_below_99_yesterday", "output_name": "sell_cond_yesterday" }, { "id": "rs_below_99_confirmed", "output_name": "sell_cond_confirmed" } ], "sell_signal": "rs_below_99_confirmed", "market_indicators": [ { "market": "QQQ", "output_name": "qqq_rs", "transformer": "qqq_rs_ma200" } ] }, "signals": [ { "id": "rs_above_101", "type": "GreaterThan", "inputs": [ { "market": "QQQ", "transformer": "qqq_rs_ma200" }, { "ref": "threshold_buy" } ] }, { "id": "rs_below_99", "type": "LessThan", "inputs": [ { "market": "QQQ", "transformer": "qqq_rs_ma200" }, { "ref": "threshold_sell" } ] }, { "id": "rs_below_99_yesterday", "type": "Lag", "inputs": [ { "ref": "rs_below_99" } ], "params": { "periods": 1, "fill_value": 0 } }, { "id": "rs_below_99_confirmed", "type": "And", "inputs": [ { "ref": "rs_below_99" }, { "ref": "rs_below_99_yesterday" } ] } ], "indicators": [ { "id": "threshold_buy", "type": "Constant", "params": { "value": 101 } }, { "id": "threshold_sell", "type": "Constant", "params": { "value": 99 } } ] }, "market_indicators": { "indicators": [ { "code": "QQQ" } ], "transformers": [ { "name": "qqq_rs_ma200", "type": "RelativeStrengthTransformer", "params": { "field": "Close", "window": 200, "indicator": "QQQ", "reference": "ma" } } ] } } Complexity: market_indicators + RelativeStrength + Lag + And confirmation Key Features: - Lag signal (delay N periods for historical reference) - And combination (require consecutive day confirmation) - RelativeStrengthTransformer for trend detection - Pattern: signal → Lag(signal) → And(signal, Lag(signal)) - Use case: Filter false breakouts in volatile 3x leveraged ETFs - Real performance: Max drawdown reduced from -93.83% to -53.96% ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 6: Arithmetic Operations (Subtract + Divide for Custom Indicators) ──────────────────────────────────────────────────────────────────── Description: 使用价格与MA20的百分比偏离度进行均值回归交易。买入:偏离度<-8%(超卖);卖出:偏离度>+8%(超买)。展示Subtract和Divide算术运算原语实现自定义指标计算的经典模式。 { "trade_strategy": { "indicators": [ { "id": "ma20", "type": "SMA", "params": { "period": 20, "column": "Close" } }, { "id": "threshold_buy", "type": "Constant", "params": { "value": -0.08 } }, { "id": "threshold_sell", "type": "Constant", "params": { "value": 0.08 } } ], "signals": [ { "id": "price_minus_ma", "type": "Subtract", "params": { "return_calculation": true }, "inputs": [ { "column": "Close" }, { "ref": "ma20" } ] }, { "id": "deviation_pct", "type": "Divide", "params": { "return_calculation": true }, "inputs": [ { "ref": "price_minus_ma" }, { "ref": "ma20" } ] }, { "id": "oversold", "type": "LessThan", "inputs": [ { "ref": "deviation_pct" }, { "ref": "threshold_buy" } ] }, { "id": "overbought", "type": "GreaterThan", "inputs": [ { "ref": "deviation_pct" }, { "ref": "threshold_sell" } ] } ], "outputs": { "buy_signal": "oversold", "sell_signal": "overbought", "indicators": [ { "id": "ma20", "output_name": "ma20" }, { "id": "price_minus_ma", "output_name": "price_diff" }, { "id": "deviation_pct", "output_name": "deviation" } ] } } } Complexity: Arithmetic operations chain (Subtract → Divide) Key Features: - Subtract: Calculate price difference from MA - Divide: Convert to percentage deviation - return_calculation: true (required for arithmetic operations) - Pattern: raw values → arithmetic → comparison → signals - Use case: Custom indicator calculations not provided by built-in indicators - Formula: deviation = (Price - MA20) / MA20 ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 7: Dynamic Position Sizing (LinearScaleWeight + Multiple Arithmetic Ops) ──────────────────────────────────────────────────────────────────── Description: 结合趋势过滤和动态仓位管理的均值回归策略。买入:价格在MA200上方且偏离1个标准差以上;仓位分配:根据偏离程度线性映射(1σ→30%,2σ→100%)。展示LinearScaleWeight动态仓位原语和Multiply/Subtract/Divide算术组合的完整应用。 { "trade_strategy": { "indicators": [ { "id": "bb", "type": "BollingerBands", "params": { "period": 20, "std_dev": 1, "column": "Close", "method": "sma" } }, { "id": "ma200", "type": "SMA", "params": { "period": 200, "column": "Close" } }, { "id": "minus_one", "type": "Constant", "params": { "value": -1 } } ], "signals": [ { "id": "price_minus_middle", "type": "Subtract", "params": { "return_calculation": true }, "inputs": [ { "column": "Close" }, { "ref": "bb.middle" } ] }, { "id": "band_width", "type": "Subtract", "params": { "return_calculation": true }, "inputs": [ { "ref": "bb.upper" }, { "ref": "bb.middle" } ] }, { "id": "deviation_in_sigma", "type": "Divide", "params": { "return_calculation": true }, "inputs": [ { "ref": "price_minus_middle" }, { "ref": "band_width" } ] }, { "id": "uptrend", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "ma200" } ] }, { "id": "oversold_1sigma", "type": "LessThan", "inputs": [ { "ref": "deviation_in_sigma" }, { "ref": "minus_one" } ] }, { "id": "buy_signal", "type": "And", "inputs": [ { "ref": "uptrend" }, { "ref": "oversold_1sigma" } ] }, { "id": "above_upper", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "bb.upper" } ] }, { "id": "negate_deviation", "type": "Multiply", "params": { "return_calculation": true }, "inputs": [ { "ref": "deviation_in_sigma" }, { "ref": "minus_one" } ] }, { "id": "dynamic_weight", "type": "LinearScaleWeight", "inputs": [ { "ref": "negate_deviation" } ], "params": { "min_indicator": 1, "max_indicator": 2, "min_weight": 0.3, "max_weight": 1, "clip": true } } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "above_upper", "target_weight": "dynamic_weight", "indicators": [ { "id": "bb.middle", "output_name": "bb_mid" }, { "id": "bb.upper", "output_name": "bb_upper" }, { "id": "bb.lower", "output_name": "bb_lower" }, { "id": "ma200", "output_name": "ma200" }, { "id": "deviation_in_sigma", "output_name": "deviation_sigma" }, { "id": "dynamic_weight", "output_name": "position_weight" } ] } } } Complexity: BollingerBands + Arithmetic chain + LinearScaleWeight + target_weight output Key Features: - LinearScaleWeight: Map indicator value to position weight - target_weight output: Dynamic position sizing (not just buy/sell signals) - Arithmetic chain: Subtract → Divide → Multiply (normalize & transform) - BollingerBands sub-fields: bb.upper, bb.middle, bb.lower - Pattern: Calculate deviation → Transform to weight → Output position size - Real-world: Gradual position building (1σ → 30%, 2σ → 100%) - Requires: RebalancingCapitalStrategy capital strategy ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 8: Conditional Position Sizing (ConditionalWeight - Discrete Adjustment) ──────────────────────────────────────────────────────────────────── Description: 双均线趋势跟踪策略,配合RSI进行动态仓位管理。买入:SMA20上穿SMA60;卖出:SMA20下穿SMA60;仓位分配:RSI>70时半仓(50%),RSI≤70时满仓(100%)。展示ConditionalWeight条件权重原语实现离散型动态仓位调整。 { "trade_strategy": { "indicators": [ { "id": "rsi", "type": "RSI", "params": { "period": 14, "column": "Close" } }, { "id": "sma_short", "type": "SMA", "params": { "period": 20, "column": "Close" } }, { "id": "sma_long", "type": "SMA", "params": { "period": 60, "column": "Close" } }, { "id": "threshold_70", "type": "Constant", "params": { "value": 70 } } ], "signals": [ { "id": "rsi_overbought", "type": "GreaterThan", "inputs": [ { "ref": "rsi" }, { "ref": "threshold_70" } ] }, { "id": "buy_signal", "type": "Crossover", "inputs": [ { "ref": "sma_short" }, { "ref": "sma_long" } ] }, { "id": "sell_signal", "type": "Crossunder", "inputs": [ { "ref": "sma_short" }, { "ref": "sma_long" } ] }, { "id": "dynamic_weight", "type": "ConditionalWeight", "inputs": [ { "ref": "rsi_overbought" } ], "params": { "true_weight": 0.5, "false_weight": 1 } } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "sell_signal", "target_weight": "dynamic_weight", "indicators": [ { "id": "rsi", "output_name": "rsi" }, { "id": "sma_short", "output_name": "sma20" }, { "id": "sma_long", "output_name": "sma60" }, { "id": "rsi_overbought", "output_name": "is_overbought" }, { "id": "dynamic_weight", "output_name": "position_weight" } ] } } } Complexity: Dual MA trend + RSI filter + ConditionalWeight Key Features: - ConditionalWeight: Discrete weight adjustment based on boolean condition - true_weight / false_weight: Different position sizes for different conditions - Use case: RSI > 70 → 50% position, RSI ≤ 70 → 100% position - Difference from LinearScaleWeight: Discrete (2 values) vs Continuous (range mapping) - Pattern: Trend signal → RSI condition → Conditional weight → Position size - Risk management: Reduce exposure during overbought conditions ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 9: Drawdown Control (PercentFromHighest - Dynamic Stop Loss) ──────────────────────────────────────────────────────────────────── Description: 基于PercentFromHighest实现动态止损的趋势策略。买入:价格上穿MA50;卖出:价格下穿MA50 OR 回撤超过20%。展示PercentFromHighest指标测量价格距离历史最高点的百分比,实现动态止损机制。 { "trade_strategy": { "indicators": [ { "id": "ma50", "type": "SMA", "params": { "period": 50, "column": "Close" } }, { "id": "drawdown", "type": "PercentFromHighest", "params": { "period": 252, "field": "Close" } }, { "id": "threshold_drawdown", "type": "Constant", "params": { "value": -20 } } ], "signals": [ { "id": "price_above_ma", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "price_below_ma", "type": "LessThan", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "excessive_drawdown", "type": "LessThan", "inputs": [ { "ref": "drawdown" }, { "ref": "threshold_drawdown" } ] }, { "id": "buy_signal", "type": "Crossover", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "trend_exit", "type": "Crossunder", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "sell_signal", "type": "Or", "inputs": [ { "ref": "trend_exit" }, { "ref": "excessive_drawdown" } ] } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "sell_signal", "indicators": [ { "id": "ma50", "output_name": "ma50" }, { "id": "drawdown", "output_name": "drawdown_pct" }, { "id": "price_above_ma", "output_name": "above_ma" }, { "id": "excessive_drawdown", "output_name": "stop_loss_triggered" } ] } } } Complexity: Trend following + PercentFromHighest + Or combination Key Features: - PercentFromHighest: Calculate percentage from highest price in lookback period - Dynamic stop loss: Exit if drawdown > 20% from 252-day high - Or combination: Exit on either trend break OR excessive drawdown - Pattern: Track historical high → Calculate drawdown → Trigger stop loss - Use case: Protect profits during corrections, limit downside risk - Formula: drawdown = (Current Price - Highest Price) / Highest Price × 100 ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 10: Range Detection (InRange - Bounded Trading) ──────────────────────────────────────────────────────────────────── Description: 基于RSI在中性区间(40-60)内的震荡交易策略。买入:RSI进入40-60区间 AND 价格上穿MA20;卖出:RSI离开40-60区间 OR 价格下穿MA20。展示InRange原语判断指标是否在特定区间内,适用于震荡市场的区间交易。 { "trade_strategy": { "indicators": [ { "id": "rsi", "type": "RSI", "params": { "period": 14, "column": "Close" } }, { "id": "ma20", "type": "SMA", "params": { "period": 20, "column": "Close" } }, { "id": "rsi_lower", "type": "Constant", "params": { "value": 40 } }, { "id": "rsi_upper", "type": "Constant", "params": { "value": 60 } } ], "signals": [ { "id": "rsi_neutral", "type": "InRange", "params": { "lower_inclusive": true, "upper_inclusive": true }, "inputs": [ { "ref": "rsi" }, { "ref": "rsi_lower" }, { "ref": "rsi_upper" } ] }, { "id": "rsi_outside", "type": "Not", "inputs": [ { "ref": "rsi_neutral" } ] }, { "id": "price_above_ma", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "ma20" } ] }, { "id": "price_cross_above", "type": "Crossover", "inputs": [ { "column": "Close" }, { "ref": "ma20" } ] }, { "id": "price_cross_below", "type": "Crossunder", "inputs": [ { "column": "Close" }, { "ref": "ma20" } ] }, { "id": "buy_signal", "type": "And", "inputs": [ { "ref": "rsi_neutral" }, { "ref": "price_cross_above" } ] }, { "id": "sell_signal", "type": "Or", "inputs": [ { "ref": "rsi_outside" }, { "ref": "price_cross_below" } ] } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "sell_signal", "indicators": [ { "id": "rsi", "output_name": "rsi" }, { "id": "ma20", "output_name": "ma20" }, { "id": "rsi_neutral", "output_name": "in_neutral_zone" }, { "id": "rsi_outside", "output_name": "outside_neutral" } ] } } } Complexity: InRange + Not + And/Or combinations Key Features: - InRange: Check if value is within specified bounds [lower, upper] - lower_inclusive / upper_inclusive: Control boundary inclusion - Three inputs: value to check, lower bound, upper bound - Pattern: RSI in neutral zone (40-60) → Range-bound trading - Not combination: Detect when value exits range - Use case: Trade within sideways/consolidation periods - Alternative: Can be implemented with (value >= lower) AND (value <= upper) ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 11: Market Sentiment Filter (ZScoreTransformer - Normalized Fear Index) ──────────────────────────────────────────────────────────────────── Description: 使用VIX的Z分数判断市场恐慌程度,过滤交易时机。买入:VIX-ZScore<1.5(低恐慌) AND 价格上穿MA50;卖出:VIX-ZScore>2.0(高恐慌) OR 价格下穿MA50。展示ZScoreTransformer计算标准化分数,实现基于市场情绪的动态过滤。 { "trade_strategy": { "indicators": [ { "id": "ma50", "type": "SMA", "params": { "period": 50, "column": "Close" } }, { "id": "vix_threshold_buy", "type": "Constant", "params": { "value": 1.5 } }, { "id": "vix_threshold_sell", "type": "Constant", "params": { "value": 2 } } ], "signals": [ { "id": "vix_low", "type": "LessThan", "inputs": [ { "market": "VIX", "transformer": "vix_zscore" }, { "ref": "vix_threshold_buy" } ] }, { "id": "vix_high", "type": "GreaterThan", "inputs": [ { "market": "VIX", "transformer": "vix_zscore" }, { "ref": "vix_threshold_sell" } ] }, { "id": "price_cross_above", "type": "Crossover", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "price_cross_below", "type": "Crossunder", "inputs": [ { "column": "Close" }, { "ref": "ma50" } ] }, { "id": "buy_signal", "type": "And", "inputs": [ { "ref": "vix_low" }, { "ref": "price_cross_above" } ] }, { "id": "sell_signal", "type": "Or", "inputs": [ { "ref": "vix_high" }, { "ref": "price_cross_below" } ] } ], "outputs": { "buy_signal": "buy_signal", "sell_signal": "sell_signal", "market_indicators": [ { "market": "VIX", "transformer": "vix_zscore", "output_name": "vix_z" } ], "indicators": [ { "id": "ma50", "output_name": "ma50" }, { "id": "vix_low", "output_name": "low_fear" }, { "id": "vix_high", "output_name": "high_fear" } ] } }, "market_indicators": { "indicators": [ { "code": "VIX" } ], "transformers": [ { "name": "vix_zscore", "type": "ZScoreTransformer", "params": { "indicator": "VIX", "window": 252, "field": "Close" } } ] } } Complexity: market_indicators + ZScoreTransformer + Conditional entry/exit Key Features: - ZScoreTransformer: Calculate standardized score (z-score) over rolling window - window: 252 (1 year lookback for normalization) - Formula: z = (current_value - mean) / std_dev - Interpretation: z < 1.5 (low fear), z > 2.0 (high fear/panic) - Market filter: Only enter when fear is low, exit when fear spikes - Use case: Avoid buying into panic, exit during crisis - Pattern: External indicator → Standardize → Filter trading signals ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 12: Crypto On-Chain Data (MVRV - Market Value to Realized Value) ──────────────────────────────────────────────────────────────────── Description: 基于BTC链上MVRV指标的极端值交易策略。买入:MVRV≤1.0(当前价格≤市场平均成本,极度恐慌);卖出:MVRV≥5.0(当前价格远高于成本,市场狂热)。该策略在2018-2026年8年间仅2次交易,年化收益31.03%,跑赢基准2.23%。展示market_indicators接入外部链上数据源和IdentityTransformer的经典应用。 { "market_indicators": { "indicators": [ { "code": "BTC_MVRV_ZSCORE" } ], "transformers": [ { "name": "mvrv_raw", "type": "IdentityTransformer", "params": { "indicator": "BTC_MVRV_ZSCORE", "field": "Close" } } ] }, "trade_strategy": { "indicators": [ { "id": "mvrv_buy_threshold", "type": "Constant", "params": { "value": 1 } }, { "id": "mvrv_sell_threshold", "type": "Constant", "params": { "value": 5 } } ], "signals": [ { "id": "mvrv_undervalued", "type": "LessThan", "inputs": [ { "market": "BTC_MVRV_ZSCORE", "transformer": "mvrv_raw" }, { "ref": "mvrv_buy_threshold" } ] }, { "id": "mvrv_overvalued", "type": "GreaterThan", "inputs": [ { "market": "BTC_MVRV_ZSCORE", "transformer": "mvrv_raw" }, { "ref": "mvrv_sell_threshold" } ] } ], "outputs": { "buy_signal": "mvrv_undervalued", "sell_signal": "mvrv_overvalued", "market_indicators": [ { "market": "BTC_MVRV_ZSCORE", "transformer": "mvrv_raw", "output_name": "mvrv_value" } ], "indicators": [ { "id": "mvrv_undervalued", "output_name": "is_undervalued" }, { "id": "mvrv_overvalued", "output_name": "is_overvalued" } ] } } } Complexity: market_indicators (external on-chain data) + IdentityTransformer Key Features: - market_indicators: Access external data source (BTC_MVRV_ZSCORE) - IdentityTransformer: Convert raw data to referenceable data stream - MVRV ≤ 1.0: Current price ≤ average cost basis (extreme fear, buy signal) - MVRV ≥ 5.0: Current price >> average cost (market euphoria, sell signal) - Real performance: 8 years, only 2 trades, 31.03% annual return - Use case: Capture extreme market cycles in crypto (2018 bottom, 2021 top) - Pattern: External data → Identity transform → Extreme threshold detection - Crypto-specific: Demonstrates platform's support for on-chain indicators ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 13: Fundamental Risk Filter (CN Single Stock) ──────────────────────────────────────────────────────────────────── Description: A股个股的趋势+基本面风险过滤策略。买入:EMA50>EMA200 且 ROE、PE_TTM、负债率、营收增速、经营现金流通过阈值;卖出:EMA50EMA120 且质量条件通过;卖出:EMA20 Eligibility -> Cadence -> Rank -> TopN -> Not / EqualWeight - ONE cadence node ("cadence") referenced by Rank, TopN and EqualWeight - All three canonical outputs present: buy_signal / sell_signal / target_weight - SMA150 eligibility gate: a symbol below its trend is excluded from ranking entirely, so a weak month can hold fewer than 3 names -- or none at all - rebalance_policy "membership_change": no trades while the selected set holds - Requires an atomic target-weight capital strategy (see section [1.7]) - outputs.indicators only names per-symbol nodes (feature, eligible) ──────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────── LEVEL 16: Fixed-Universe Cross-Sectional (Daily Top 1, CN Cross-Asset) ──────────────────────────────────────────────────────────────────── Description: 在组合的固定候选标的中横向比较:每个交易日按 21 日涨幅降序排名,EqualWeight 对排名第一的标的输出 100% 模型权重,排名第一的标的换人时才调仓。模型权重不等于最终仓位:实际风险暴露和现金缓冲由 RebalancingCapitalStrategy.gross_exposure 决定。适合股票/海外/黄金等相关性较低的跨资产 ETF 池;单一持仓比等权持有更集中,回撤也更依赖单个标的。资金管理需选择 RebalancingCapitalStrategy(可用 gross_exposure 保留现金缓冲)。 { "trade_strategy": { "indicators": [ { "id": "momentum", "type": "RateOfChange", "params": { "periods": 21, "column": "Close" } }, { "id": "zero", "type": "Constant", "params": { "value": 0 } } ], "signals": [ { "id": "eligible", "type": "GreaterThan", "inputs": [ { "column": "Close" }, { "ref": "zero" } ], "params": {} }, { "id": "cadence", "type": "RebalanceCadence", "inputs": [], "params": { "frequency": "daily", "bootstrap_on_first_session": true } }, { "id": "rank", "type": "CrossSectionalRank", "inputs": [ { "ref": "momentum" }, { "ref": "eligible" }, { "ref": "cadence" } ], "params": { "direction": "descending" } }, { "id": "selected", "type": "TopN", "inputs": [ { "ref": "rank" }, { "ref": "cadence" } ], "params": { "n": 1 } }, { "id": "not_selected", "type": "Not", "inputs": [ { "ref": "selected" } ], "params": {} }, { "id": "weight", "type": "EqualWeight", "inputs": [ { "ref": "selected" }, { "ref": "cadence" } ], "params": { "rebalance_policy": "membership_change" } } ], "outputs": { "buy_signal": "selected", "sell_signal": "not_selected", "target_weight": "weight", "indicators": [ { "id": "momentum", "output_name": "cross_sectional_feature" }, { "id": "eligible", "output_name": "cross_sectional_eligible" } ] } } } Complexity: cross-sectional domain -- daily cadence, single holding Key Features: - Same canonical chain as LEVEL 15 with frequency "daily" and n = 1 - Eligibility here is a permissive Close > 0 gate: use this shape when the ranking feature alone should decide, not a trend filter - n = 1 means the whole vector is one symbol at weight 1.0 -- concentrated, with drawdown driven by that single holding - "membership_change" keeps turnover down even on a daily cadence: it only trades when the leader actually changes ──────────────────────────────────────────────────────────────────── ════════════════════════════════════════════════════════════════════ ❌ [4] ANTI-EXAMPLES (Common Mistakes to AVOID) ════════════════════════════════════════════════════════════════════ ──────────────────────────────────────────────────────────────────── ❌ CRITICAL ERROR #1: Inline Signal Definitions (Gemini-style mistake) ──────────────────────────────────────────────────────────────────── This is the MOST COMMON LLM error - NEVER nest "type" inside "inputs"! ❌ WRONG (Gemini output): { "signals": [ { "id": "buy_cond", "type": "And", "inputs": [ { "type": "GreaterThan", // ❌ FATAL: Inline definition forbidden! "inputs": [{"column": "Close"}, {"ref": "ma_trend"}] }, { "type": "GreaterThan", // ❌ Same error "inputs": [{"column": "Close"}, {"ref": "chandelier"}] } ] } ] } ✅ CORRECT (Define-then-Reference Pattern): { "signals": [ { "id": "price_gt_ma", // ✅ Step 1: Define comparison "type": "GreaterThan", "inputs": [{"column": "Close"}, {"ref": "ma_trend"}] }, { "id": "price_gt_chandelier", // ✅ Step 2: Define another comparison "type": "GreaterThan", "inputs": [{"column": "Close"}, {"ref": "chandelier"}] }, { "id": "buy_cond", // ✅ Step 3: Combine via references "type": "And", "inputs": [ {"ref": "price_gt_ma"}, {"ref": "price_gt_chandelier"} ] } ] } 🎓 Key Rule: inputs array can ONLY contain: - {"ref": "signal_id"} (reference to signal) - {"column": "Close"} (raw price column) - {"market": "VIX", "transformer": "name"} (market data) - {"value": 50} (constant value) NEVER {"type": ...} inside inputs! ──────────────────────────────────────────────────────────────────── Type Errors: ──────────────────────────────────────────────────────────────────── ❌ {id: "sma", type: "SMA", params: {period: "20"}} ✅ {id: "sma", type: "SMA", params: {period: 20}} (period must be integer, not string) ❌ {id: "rsi", type: "Rsi", params: {period: 14}} ✅ {id: "rsi", type: "RSI", params: {period: 14}} (type is case-sensitive enum) ❌ {id: "sma", type: "SimpleMA", params: {period: 20}} ✅ {id: "sma", type: "SMA", params: {period: 20}} (must use exact Schema enum value) ──────────────────────────────────────────────────────────────────── Missing Required Fields: ──────────────────────────────────────────────────────────────────── ❌ {type: "SMA", params: {period: 20}} ✅ {id: "sma", type: "SMA", params: {period: 20, column: "Close"}} (id is required for all primitives) ❌ {id: "buy", type: "Crossover"} ✅ {id: "buy", type: "Crossover", inputs: [{ref: "sma20"}, {ref: "sma50"}]} (inputs required for signal primitives) ──────────────────────────────────────────────────────────────────── Reference Errors: ──────────────────────────────────────────────────────────────────── ❌ {id: "buy", type: "GreaterThan", inputs: [{ref: "undefined_id"}, {value: 50}]} ✅ {id: "buy", type: "GreaterThan", inputs: [{ref: "rsi"}, {value: 50}]} (ref must point to existing id) ❌ signals: [{id: "a", inputs: [{ref: "b"}]}, {id: "b", type: "RSI"}] ✅ indicators: [{id: "b", type: "RSI", ...}], signals: [{id: "a", inputs: [{ref: "b"}]}] (ref must point to defined indicator/signal, not forward reference in same section) ──────────────────────────────────────────────────────────────────── Extra Fields (Schema has additionalProperties: false): ──────────────────────────────────────────────────────────────────── ❌ {id: "sma", type: "SMA", params: {period: 20}, description: "20-day MA"} ✅ {id: "sma", type: "SMA", params: {period: 20, column: "Close"}} (no extra fields allowed - description will cause validation error) ❌ {id: "buy", type: "GreaterThan", inputs: [{ref: "rsi"}], comment: "RSI > 70"} ✅ {id: "buy", type: "GreaterThan", inputs: [{ref: "rsi"}, {value: 70}]} (comment field not allowed) ──────────────────────────────────────────────────────────────────── Wrong Input Format: ──────────────────────────────────────────────────────────────────── ❌ {id: "buy", type: "GreaterThan", inputs: ["rsi", 70]} ✅ {id: "buy", type: "GreaterThan", inputs: [{ref: "rsi"}, {value: 70}]} (inputs must be array of objects, not primitive values) ❌ {id: "buy", type: "And", inputs: {left: {ref: "a"}, right: {ref: "b"}}} ✅ {id: "buy", type: "And", inputs: [{ref: "a"}, {ref: "b"}]} (inputs is array, not object with left/right) ──────────────────────────────────────────────────────────────────── Missing params Object: ──────────────────────────────────────────────────────────────────── ❌ {id: "sma", type: "SMA", period: 20, column: "Close"} ✅ {id: "sma", type: "SMA", params: {period: 20, column: "Close"}} (parameters must be in "params" object) ──────────────────────────────────────────────────────────────────── Wrong MACD Output Reference: ──────────────────────────────────────────────────────────────────── ❌ {id: "cross", type: "Crossover", inputs: [{ref: "macd"}, {ref: "macd"}]} ✅ {id: "cross", type: "Crossover", inputs: [{ref: "macd.macd"}, {ref: "macd.signal"}]} (MACD has multiple outputs: macd, signal, histogram - must specify which) ──────────────────────────────────────────────────────────────────── Wrong market_indicators code: ──────────────────────────────────────────────────────────────────── ❌ {"market_indicators": {"indicators": [{"code": "BTC-USD"}]}} ✅ {"market_indicators": {"indicators": [{"code": "BTC"}]}} (Bitcoin price code on MyInvestPilot is BTC, not Yahoo-style BTC-USD) ❌ {"params": {"indicator": "BTC-USD"}} ✅ {"params": {"indicator": "BTC"}} (transformer params.indicator must match a declared platform-supported code) ❌ {"market": "BTC-USD", "transformer": "btc_rsi"} ✅ {"market": "BTC", "transformer": "btc_rsi"} (market input must use the same supported code as indicators[].code) ════════════════════════════════════════════════════════════════════ ⚠️ [5] MISSING EXAMPLE COVERAGE (需要补充的例子类型) ════════════════════════════════════════════════════════════════════ 以下原语类型在当前示例中未覆盖: 1. 高级指标: - PercentFromLowest: 价格相对最低点百分比(与PercentFromHighest对称,使用频率极低) 2. 通道指标: - DonchianChannel: 多输出 High/Low 通道(upper/lower/middle/width), exclude_current=true 默认 prior-completed-bar 语义。canonical 20/10 breakout DSL(两实例 + GreaterThan/LessThan)见 https://www.myinvestpilot.com/docs/primitives/indicators 的 DonchianChannel 小节。 注:以下原语已在示例中完整覆盖(可参考对应 Level): ✅ 基础指标: SMA, EMA, RSI, MACD, BollingerBands, ATR, ChandelierExit, Constant ✅ 高级指标: PercentFromHighest (Level 9) ✅ 比较信号: GreaterThan, LessThan, InRange (Level 10) ✅ 交叉信号: Crossover, Crossunder ✅ 逻辑组合: And, Or, Not ✅ 算术运算: Subtract, Divide, Multiply (Level 6-7) ✅ 动态仓位: LinearScaleWeight (Level 7), ConditionalWeight (Level 8) ✅ 高级信号: Lag (Level 5), Streak (Level 4), StockBondSwitch (Level 4) ✅ 转换器: IdentityTransformer (Level 12), RelativeStrengthTransformer (Level 5) ✅ 转换器: ZScoreTransformer (Level 11), PercentileRank, MovingAverage ✅ 外部数据: market_indicators (Level 3, 5, 11, 12 - VIX, QQQ, BTC_MVRV_ZSCORE) ════════════════════════════════════════════════════════════════════ ════════════════════════════════════════════════════════════════════ END OF QUICK REFERENCE ════════════════════════════════════════════════════════════════════ For detailed documentation, visit: https://www.myinvestpilot.com/docs/primitives/getting-started For full examples, see: https://www.myinvestpilot.com/docs/primitives/_llm/llm-full.txt Generated by: scripts/generate-llm-quickstart.js Schema source: https://media.i365.tech/myinvestpilot/primitives_schema.json