{
  "meta": {
    "generator": "scripts.generate_primitive_semantics",
    "primitive_count": 58,
    "category_counts": {
      "indicators": 20,
      "signals": 25,
      "market_transformers": 7,
      "capital_strategies": 6
    },
    "schema_version": 1
  },
  "primitives": {
    "ATR": {
      "type": "ATR",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "ATR",
      "name": "Average True Range indicator primitive.",
      "description": "Calculates the ATR technical indicator which measures market volatility by decomposing the entire range of an asset price for a specified period.",
      "summary": "Volatility indicator that measures the recent trading range and is often used for stop distance or regime filters.",
      "typical_role": "volatility_measure",
      "behavior_axes": [
        "indicator",
        "range",
        "risk",
        "sizing",
        "volatility"
      ],
      "tags": [
        "indicator",
        "volatility_measure"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for calculating ATR",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "method": {
          "type": "string",
          "description": "The calculation method",
          "default": "sma",
          "enum": [
            "sma",
            "ema"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        }
      },
      "key_params": [
        "period",
        "method"
      ],
      "when_to_use": [
        "Use when you need a volatility-aware stop distance or regime check.",
        "Often paired with Chandelier-style exits and trailing stops."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "Add": {
      "type": "Add",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Add",
      "name": "Signal that adds two time series and compares the result.",
      "description": "This signal adds two input series and compares the result against either a threshold value or a third input series.",
      "summary": "This signal adds two input series and compares the result against either a threshold value or a third input series",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal",
        "threshold"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "dynamic",
      "capabilities": {},
      "params": {
        "operation": {
          "type": "string",
          "description": "Type of operation to perform",
          "default": "add",
          "enum": [
            "add",
            "subtract",
            "multiply",
            "divide"
          ]
        },
        "comparison": {
          "type": "string",
          "description": "Type of comparison",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal",
            "greater_equal",
            "less_equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "threshold": {
          "type": "number",
          "description": "Value to compare result against (if not comparing against another series)",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "absolute": {
          "type": "boolean",
          "description": "Whether to take the absolute value of the result before comparison",
          "default": false
        },
        "return_calculation": {
          "type": "boolean",
          "description": "If True, return the numeric calculation result instead of boolean comparison result. Supports multiple operands.",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "comparison",
        "operation",
        "absolute"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "And": {
      "type": "And",
      "category": "signal",
      "source_category": "signals",
      "class_name": "And",
      "name": "Logical AND operator for signal primitives.",
      "description": "Signal is True when both input signals are True.",
      "summary": "Logical gate that requires every input condition to be true before a signal fires.",
      "typical_role": "signal_gate",
      "behavior_axes": [
        "filtering",
        "logic",
        "signal",
        "strictness"
      ],
      "tags": [
        "signal",
        "signal_gate"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "fill_method": {
          "type": "string",
          "description": "Method to handle NaN values",
          "default": null,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "fill_method"
      ],
      "when_to_use": [
        "Use when multiple conditions must agree before you act.",
        "Common for combining trend confirmation with market or risk filters."
      ],
      "tuning_hints": [
        "If trade count is too low, reduce the number of strict conditions before adding more filters."
      ],
      "common_pitfalls": [
        "Too many AND conditions can make the strategy so strict that it barely trades."
      ]
    },
    "BollingerBands": {
      "type": "BollingerBands",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "BollingerBands",
      "name": "Bollinger Bands indicator primitive.",
      "description": "Calculates Bollinger Bands, which consist of: - Middle band: SMA/EMA of the price - Upper band: Middle band + (standard deviation * multiplier) - Lower band: Middle band - (standard deviation * multiplier) This is a multi-output indicator returning a dictionary of Series.",
      "summary": "Calculates Bollinger Bands, which consist of: - Middle band: SMA/EMA of the price - Upper band: Middle band + (standard deviation * multiplier) - Lower band: Middle band - (standard deviation * multiplier) This is a multi-output indicator returning a dictionary of Series",
      "typical_role": "volatility_filter",
      "behavior_axes": [
        "indicator",
        "volatility"
      ],
      "tags": [
        "indicator",
        "volatility_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Dict",
        "names": [
          "middle",
          "upper",
          "lower"
        ]
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for the moving average",
          "default": 20,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "std_dev": {
          "type": "number",
          "description": "Standard deviation multiplier",
          "default": 2.0
        },
        "column": {
          "type": "string",
          "description": "The column to calculate bands on",
          "default": "Close"
        },
        "method": {
          "type": "string",
          "description": "MA calculation method",
          "default": "sma",
          "enum": [
            "sma",
            "ema"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        }
      },
      "key_params": [
        "period",
        "std_dev",
        "column",
        "method"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "CashSweepPercentCapitalStrategy": {
      "type": "CashSweepPercentCapitalStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "CashSweepPercentCapitalStrategy",
      "name": "Percent-based capital strategy with optional cash sweep behavior.",
      "description": "- Main signal-driven positions use percent allocation semantics. - Optional sweep symbol is managed by capital hooks, not by trade signals.",
      "summary": "Percent-based capital allocation with additional cash sweep behavior for idle capital management.",
      "typical_role": "capital_exposure",
      "behavior_axes": [
        "capital",
        "cash_management",
        "position_sizing",
        "risk"
      ],
      "tags": [
        "capital",
        "capital_exposure"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "target_percent",
        "target_weight_compatible": false,
        "atomic_complete_vector_compatible": false,
        "audited_target_weight_override": false
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "percents": {
          "type": "number",
          "description": "Target percentage per traded symbol (same semantics as PercentCapitalStrategy)",
          "default": 20.0,
          "impact_axes": [
            "capital_exposure",
            "drawdown",
            "return_magnitude"
          ],
          "effect_when_higher": "Raises capital exposure and amplifies both gains and losses without changing signal timing.",
          "effect_when_lower": "Reduces capital exposure and usually lowers drawdown and upside together."
        },
        "max_positions": {
          "type": "integer",
          "description": "Maximum number of non-auxiliary positions allowed, None for unlimited",
          "default": null,
          "impact_axes": [
            "concentration",
            "diversification"
          ],
          "effect_when_higher": "Allows more positions and can reduce concentration if the strategy actually uses them.",
          "effect_when_lower": "Forces concentration into fewer holdings and can make each position matter more."
        },
        "sweep_symbol": {
          "type": "string",
          "description": "Optional symbol used as cash sweep destination",
          "default": null
        },
        "min_sweep_cash": {
          "type": "number",
          "description": "Minimum idle cash required before placing a sweep order",
          "default": 1000.0
        },
        "max_sweep_percent": {
          "type": "number",
          "description": "Upper bound for sweep target allocation to keep a cash safety buffer",
          "default": 99.0
        }
      },
      "key_params": [
        "percents",
        "max_positions",
        "initial_capital",
        "sweep_symbol"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use."
      ],
      "tuning_hints": [
        "Treat capital-exposure tuning as separate from signal-quality tuning so you can tell whether performance changes come from sizing or from better timing."
      ],
      "common_pitfalls": []
    },
    "ChandelierExit": {
      "type": "ChandelierExit",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "ChandelierExit",
      "name": "Chandelier Exit indicator primitive.",
      "description": "Calculates the Chandelier Exit technical indicator which combines the highest price over a period with ATR to identify potential stop levels.",
      "summary": "Volatility-based trailing stop reference that usually helps define slower exits or protective stop levels.",
      "typical_role": "exit_reference",
      "behavior_axes": [
        "exit",
        "indicator",
        "trailing_stop",
        "volatility"
      ],
      "tags": [
        "exit_reference",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for highest high and ATR",
          "default": 60,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "multiplier": {
          "type": "number",
          "description": "The ATR multiplier",
          "default": 4.0,
          "impact_axes": [
            "exit_distance",
            "drawdown",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually creates a looser buffer or stop, which can hold trends longer but may also allow larger drawdowns.",
          "effect_when_lower": "Usually creates a tighter buffer or stop, which can cut losses faster but may also exit winners earlier."
        },
        "add_ma": {
          "type": "boolean",
          "description": "Whether to also calculate MA",
          "default": false,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        },
        "ma_period": {
          "type": "integer",
          "description": "The period for MA calculation if add_ma=True",
          "default": 250,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        }
      },
      "key_params": [
        "period",
        "multiplier",
        "add_ma",
        "ma_period"
      ],
      "when_to_use": [
        "Use when you want exits to follow the trend while adapting to volatility.",
        "Often used to avoid exiting too early on ordinary pullbacks."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea.",
        "Multiplier and period usually control how tight or loose the stop behaves.",
        "Tighter settings can reduce losses faster but may also cut winners early."
      ],
      "common_pitfalls": [
        "Combining a very slow trend filter with a very loose trailing stop can make the strategy too slow overall."
      ]
    },
    "ChandelierExitTransformer": {
      "type": "ChandelierExitTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "ChandelierExitTransformer",
      "name": "Transformer that calculates Chandelier Exit stop level for market indicator data.",
      "description": "Chandelier Exit = Highest High(period) - ATR(period) * multiplier This is useful for trend-following strategies that use trailing stops.",
      "summary": "Converts market indicator data into a Chandelier-style trailing-stop reference for regime checks.",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context",
        "trailing_stop",
        "volatility"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "period": {
          "type": "integer",
          "description": "Period for highest high and ATR calculation",
          "default": 30,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "multiplier": {
          "type": "number",
          "description": "ATR multiplier for stop calculation",
          "default": 2.0,
          "impact_axes": [
            "exit_distance",
            "drawdown",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually creates a looser buffer or stop, which can hold trends longer but may also allow larger drawdowns.",
          "effect_when_lower": "Usually creates a tighter buffer or stop, which can cut losses faster but may also exit winners earlier."
        }
      },
      "key_params": [
        "period",
        "multiplier",
        "indicator"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "Comparison": {
      "type": "Comparison",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Comparison",
      "name": "General comparison signal primitive.",
      "description": "Compares values using a specified comparison operator. Supports different comparison types and an optional threshold value.",
      "summary": "General comparison primitive for greater/less/equal style threshold checks.",
      "typical_role": "threshold_gate",
      "behavior_axes": [
        "comparison",
        "filtering",
        "signal",
        "threshold"
      ],
      "tags": [
        "signal",
        "threshold_gate"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "comparison": {
          "type": "string",
          "description": "Type of comparison to perform",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal",
            "greater_equal",
            "less_equal",
            "not_equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "threshold": {
          "type": "number",
          "description": "Threshold value for comparison when only one input is provided",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "epsilon": {
          "type": "number",
          "description": "Small value to avoid floating point precision issues",
          "default": 1e-10,
          "impact_axes": [
            "comparison_tolerance"
          ],
          "effect_when_higher": "Makes the comparison less sensitive to very small differences.",
          "effect_when_lower": "Makes the comparison more exact and more sensitive to tiny differences."
        }
      },
      "key_params": [
        "threshold",
        "comparison",
        "epsilon"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "ConditionalWeight": {
      "type": "ConditionalWeight",
      "category": "signal",
      "source_category": "signals",
      "class_name": "ConditionalWeight",
      "name": "条件权重原语：根据布尔条件选择权重",
      "description": "用途： - PE>25时半仓，否则满仓 - RSI>70时减仓，否则正常仓位 - 任何基于条件的离散权重调整 输入：布尔条件序列 (pd.Series of bool) 输出：权重序列 (pd.Series of float, True→true_weight, False→false_weight) 示例配置： { \"id\": \"pe_weight\", \"type\": \"ConditionalWeight\", \"inputs\": [{\"ref\": \"pe_high\"}], // pe_high是布尔条件 \"params\": { \"true_weight\": 0.5, // PE高时 → 50%仓位 \"false_weight\": 1.0 // PE正常时 → 100%仓位 } }",
      "summary": "Turns a condition into discrete target weights, useful for rule-based position sizing.",
      "typical_role": "weight_control",
      "behavior_axes": [
        "position_sizing",
        "risk",
        "signal",
        "weighting"
      ],
      "tags": [
        "signal",
        "weight_control"
      ],
      "inputs": {
        "count": "variable",
        "types": [
          {
            "name": "condition",
            "type": "_empty"
          }
        ]
      },
      "outputs": {
        "type": "_empty"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "weight",
      "capabilities": {},
      "params": {
        "true_weight": {
          "type": "number",
          "description": "条件为True时的权重",
          "impact_axes": [
            "capital_exposure",
            "position_sizing"
          ],
          "effect_when_higher": "Assigns a larger target weight and increases exposure when that branch is active.",
          "effect_when_lower": "Assigns a smaller target weight and reduces exposure when that branch is active."
        },
        "false_weight": {
          "type": "number",
          "description": "条件为False时的权重",
          "impact_axes": [
            "capital_exposure",
            "position_sizing"
          ],
          "effect_when_higher": "Assigns a larger target weight and increases exposure when that branch is active.",
          "effect_when_lower": "Assigns a smaller target weight and reduces exposure when that branch is active."
        }
      },
      "key_params": [
        "true_weight",
        "false_weight"
      ],
      "when_to_use": [
        "Use when you want exposure to change while keeping the entry and exit logic mostly the same.",
        "Use when a condition should change exposure without changing entry timing."
      ],
      "tuning_hints": [
        "Usually tune the weights after you understand whether the condition itself is useful."
      ],
      "common_pitfalls": []
    },
    "Constant": {
      "type": "Constant",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "Constant",
      "name": "Constant indicator primitive.",
      "description": "Generates a time series with a fixed value, with the same length as the input data.",
      "summary": "Generates a time series with a fixed value, with the same length as the input data",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": "variable",
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          },
          {
            "name": "kwargs",
            "type": "_empty"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "value": {
          "type": "number",
          "description": "The constant value to generate",
          "default": 1.0
        }
      },
      "key_params": [
        "value"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "CrossAbove": {
      "type": "CrossAbove",
      "category": "signal",
      "source_category": "signals",
      "class_name": "CrossAbove",
      "name": "CrossAbove signal primitive.",
      "description": "Detects when one signal crosses above another signal. True when signal_a was below signal_b in previous period and is now above signal_b.",
      "summary": "Detects when one signal crosses above another signal",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "threshold": {
          "type": "number",
          "description": "Additional threshold to require for crossing",
          "default": 0.0,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "strict": {
          "type": "boolean",
          "description": "If True, requires strict comparison for prior period",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "strict"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "CrossBelow": {
      "type": "CrossBelow",
      "category": "signal",
      "source_category": "signals",
      "class_name": "CrossBelow",
      "name": "CrossBelow signal primitive.",
      "description": "Detects when one signal crosses below another signal. True when signal_a was above signal_b in previous period and is now below signal_b.",
      "summary": "Detects when one signal crosses below another signal",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "threshold": {
          "type": "number",
          "description": "Additional threshold to require for crossing",
          "default": 0.0,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "strict": {
          "type": "boolean",
          "description": "If True, requires strict comparison for prior period",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "strict"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "CrossSectionalRank": {
      "type": "CrossSectionalRank",
      "category": "signal",
      "source_category": "signals",
      "class_name": "CrossSectionalRank",
      "name": "Deterministic ordinal rank across eligible symbols on a decision session.",
      "description": "",
      "summary": "Builds deterministic same-session ordinal ranks across eligible configured symbols.",
      "typical_role": "cross_sectional_rank",
      "behavior_axes": [
        "deterministic_ties",
        "eligibility",
        "ranking",
        "signal"
      ],
      "tags": [
        "cross_sectional_rank",
        "signal"
      ],
      "inputs": {
        "count": 3,
        "types": [
          "numeric",
          "boolean",
          "cadence"
        ]
      },
      "outputs": {
        "type": "rank"
      },
      "evaluation_domain": "cross_sectional",
      "output_type": "rank",
      "capabilities": {},
      "params": {
        "direction": {
          "type": "string",
          "description": "Feature ordering direction",
          "default": "descending"
        }
      },
      "key_params": [
        "direction"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Crossover": {
      "type": "Crossover",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Crossover",
      "name": "Detects when one series crosses above another.",
      "description": "Signal is True when series_a crosses above series_b. Supports two modes: - 'cross': Requires actual crossing (previous <= and current >) - 'simple': Simple comparison (current >), used to replicate DualMovingAverageStrategy",
      "summary": "Signal that detects when one series moves above another or is simply above it, depending on mode.",
      "typical_role": "entry_trigger",
      "behavior_axes": [
        "signal",
        "timing",
        "trend",
        "trigger"
      ],
      "tags": [
        "entry_trigger",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "mode": {
          "type": "string",
          "description": "Comparison mode: 'cross' for actual crossing, 'simple' for basic comparison",
          "default": "cross",
          "enum": [
            "cross",
            "simple"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes how the primitive interprets the same inputs, so it can materially change strategy behavior."
        }
      },
      "key_params": [
        "mode"
      ],
      "when_to_use": [
        "Use when you want Crossover to control timing for entering or exiting rather than capital sizing.",
        "Use for cross-based trend entries or confirmation changes."
      ],
      "tuning_hints": [
        "Mode changes the meaning a lot: cross is event-driven, simple is state-driven."
      ],
      "common_pitfalls": []
    },
    "Crossunder": {
      "type": "Crossunder",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Crossunder",
      "name": "Detects when one series crosses below another.",
      "description": "Signal is True when series_a crosses below series_b. Supports two modes: - 'cross': Requires actual crossing (previous >= and current <) - 'simple': Simple comparison (current <), used to replicate DualMovingAverageStrategy",
      "summary": "Signal that detects when one series moves below another or is simply below it, depending on mode.",
      "typical_role": "exit_trigger",
      "behavior_axes": [
        "signal",
        "timing",
        "trend",
        "trigger"
      ],
      "tags": [
        "exit_trigger",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "mode": {
          "type": "string",
          "description": "Comparison mode: 'cross' for actual crossing, 'simple' for basic comparison",
          "default": "cross",
          "enum": [
            "cross",
            "simple"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes how the primitive interprets the same inputs, so it can materially change strategy behavior."
        }
      },
      "key_params": [
        "mode"
      ],
      "when_to_use": [
        "Use when you want Crossunder to control timing for entering or exiting rather than capital sizing."
      ],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "DebtRatio": {
      "type": "DebtRatio",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "DebtRatio",
      "name": "Debt ratio fundamental indicator primitive.",
      "description": "Reads the debt ratio series from the configured column.",
      "summary": "Reads the debt ratio series from the configured column",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Debt ratio column",
          "default": "debt_ratio"
        }
      },
      "key_params": [
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Divide": {
      "type": "Divide",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Divide",
      "name": "Signal that divides one time series by another and compares the result.",
      "description": "This signal divides the first input series by the second and compares the result against either a threshold value or a third input series. Special handling is included to safely handle division by zero.",
      "summary": "This signal divides the first input series by the second and compares the result against either a threshold value or a third input series",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal",
        "threshold"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "dynamic",
      "capabilities": {},
      "params": {
        "operation": {
          "type": "string",
          "description": "Type of operation to perform",
          "default": "divide",
          "enum": [
            "add",
            "subtract",
            "multiply",
            "divide"
          ]
        },
        "comparison": {
          "type": "string",
          "description": "Type of comparison",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal",
            "greater_equal",
            "less_equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "threshold": {
          "type": "number",
          "description": "Value to compare result against (if not comparing against another series)",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "absolute": {
          "type": "boolean",
          "description": "Whether to take the absolute value of the result before comparison",
          "default": false
        },
        "return_calculation": {
          "type": "boolean",
          "description": "If True, return the numeric calculation result instead of boolean comparison result. Supports multiple operands.",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "comparison",
        "operation",
        "absolute"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "DonchianChannel": {
      "type": "DonchianChannel",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "DonchianChannel",
      "name": "Donchian Channel multi-output indicator.",
      "description": "Calculates the rolling High/Low range over a lookback period with audited prior-completed-bar semantics, plus the derived midpoint and width of the channel. Outputs (all sharing the same index and NaN validity mask): * ``upper`` -- rolling maximum of ``upper_column`` * ``lower`` -- rolling minimum of ``lower_column`` * ``middle`` -- ``(upper + lower) / 2`` * ``width`` -- ``upper - lower`` Example (20-day upper breakout / 10-day lower exit, both excluding the current bar):: {\"id\": \"entry_channel\", \"type\": \"DonchianChannel\", \"params\": {\"period\": 20, \"upper_column\": \"High\", \"lower_column\": \"Low\", \"exclude_current\": true}} {\"id\": \"exit_channel\", \"type\": \"DonchianChannel\", \"params\": {\"period\": 10, \"upper_column\": \"High\", \"lower_column\": \"Low\", \"exclude_current\": true}} Entry compares ``Close`` against ``entry_channel.upper``, exit compares ``Close`` against ``exit_channel.lower``. The existing signal state machine remains the authority for BUY/HOLD/SELL/EMPTY transitions.",
      "summary": "Multi-output High/Low range channel for breakout entry and exit levels. With the default exclude_current=true, the channel at bar t is formed only from completed bars ending at t-1, so a bar's own new extreme can never move the threshold it is compared against.",
      "typical_role": "breakout_level",
      "behavior_axes": [
        "breakout",
        "channel",
        "extremes",
        "indicator",
        "range",
        "trend_following"
      ],
      "tags": [
        "breakout_level",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Dict",
        "names": [
          "upper",
          "lower",
          "middle",
          "width"
        ]
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "Number of bars in the channel window. Must be >= 1.",
          "default": 20,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "upper_column": {
          "type": "string",
          "description": "Column used to build the upper channel (rolling maximum).",
          "default": "High"
        },
        "lower_column": {
          "type": "string",
          "description": "Column used to build the lower channel (rolling minimum).",
          "default": "Low"
        },
        "exclude_current": {
          "type": "boolean",
          "description": "If True, the channel value at bar t uses only completed bars ending at t-1 (upper[t]=max(column[t-period .. t-1])). This is the breakout-safe default. If False, the channel is an ordinary rolling range that includes bar t.",
          "default": true
        }
      },
      "key_params": [
        "period",
        "upper_column",
        "lower_column",
        "exclude_current"
      ],
      "when_to_use": [
        "Use as the audited entry/exit level reference for daily channel breakout, e.g. entry = Close > entry_channel.upper and exit = Close < exit_channel.lower with separate windows.",
        "Keep exclude_current=true (the default) for breakout research; the current bar must never be part of its own decision threshold."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea.",
        "Entry and exit windows are usually tuned separately; a common baseline is 20-bar entry / 10-bar exit.",
        "Longer periods raise the breakout threshold and usually reduce trade frequency; shorter periods trigger more often and react faster."
      ],
      "common_pitfalls": [
        "Do not set exclude_current=false for breakout signals: the current bar would enter its own channel and systematically mask true breakouts.",
        "A DonchianChannel graph is an entry/exit LEVEL primitive only, not a complete Turtle system: ATR/N sizing, pyramiding and intraday stops are separate Capital Strategy / Execution work.",
        "Warm-up requires the complete window; partial-window channels are never emitted, so signals during warm-up stay EMPTY."
      ]
    },
    "EMA": {
      "type": "EMA",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "EMA",
      "name": "Exponential Moving Average indicator primitive.",
      "description": "Uses pandas' exponential weighted moving average (ewm) for EMA calculation. EMA gives more weight to recent prices compared to SMA.",
      "summary": "Exponential moving average that reacts faster than SMA because recent data gets more weight.",
      "typical_role": "trend_filter",
      "behavior_axes": [
        "faster_response",
        "indicator",
        "smoothing",
        "trend"
      ],
      "tags": [
        "indicator",
        "trend_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for the moving average",
          "default": 20,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "column": {
          "type": "string",
          "description": "The column to calculate EMA on",
          "default": "Close"
        },
        "adjust": {
          "type": "boolean",
          "description": "Specify if the weights should be normalized",
          "default": false,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "period",
        "column",
        "adjust"
      ],
      "when_to_use": [
        "Use when you want a slower baseline to judge whether price is aligned with the prevailing trend.",
        "Use when you want a trend baseline that reacts faster than SMA.",
        "Often paired with another moving average for crossover-style entries or exits."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea.",
        "Shortening period makes EMA react faster than an equivalent SMA.",
        "Compare against SMA when you want to test whether lag is the main issue."
      ],
      "common_pitfalls": []
    },
    "EqualWeight": {
      "type": "EqualWeight",
      "category": "signal",
      "source_category": "signals",
      "class_name": "EqualWeight",
      "name": "Complete equal-weight vector on rebalance dates and NaN between them.",
      "description": "",
      "summary": "Produces normalized complete equal-weight model vectors on every scheduled decision or only when selected membership changes.",
      "typical_role": "portfolio_construction",
      "behavior_axes": [
        "atomic_rebalance",
        "complete_vector",
        "emission_policy",
        "position_sizing",
        "signal",
        "weighting"
      ],
      "tags": [
        "portfolio_construction",
        "signal"
      ],
      "inputs": {
        "count": 2,
        "types": [
          "selection",
          "cadence"
        ]
      },
      "outputs": {
        "type": "weight"
      },
      "evaluation_domain": "cross_sectional",
      "output_type": "weight",
      "capabilities": {},
      "params": {
        "rebalance_policy": {
          "type": "string",
          "description": "Emit on every scheduled decision or only when selected membership changes",
          "default": "scheduled",
          "enum": [
            "scheduled",
            "membership_change"
          ]
        }
      },
      "key_params": [
        "rebalance_policy"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": [
        "Non-emission target weights are intentionally NaN and must never be forward-filled.",
        "Gross exposure and cash buffers belong to RebalancingCapitalStrategy, not EqualWeight."
      ]
    },
    "FixedInvestmentStrategy": {
      "type": "FixedInvestmentStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "FixedInvestmentStrategy",
      "name": "定期定投资金策略，支持年度和月度定投",
      "description": "",
      "summary": "Capital strategy for periodic fixed-amount investing, typically used for DCA-style flows.",
      "typical_role": "cash_flow",
      "behavior_axes": [
        "capital",
        "cash_flow",
        "periodic_investing"
      ],
      "tags": [
        "capital",
        "cash_flow"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "cash_based",
        "target_weight_compatible": true,
        "atomic_complete_vector_compatible": false,
        "audited_target_weight_override": true
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "investment_amount": {
          "type": "number",
          "description": "Amount to invest periodically",
          "default": 10000.0
        },
        "investment_frequency": {
          "type": "string",
          "description": "Investment frequency: 'y' for yearly, 'm' for monthly",
          "default": "y",
          "enum": [
            "y",
            "m"
          ]
        },
        "percents": {
          "type": "number",
          "description": "Maximum percentage per position",
          "default": 20.0,
          "impact_axes": [
            "capital_exposure",
            "drawdown",
            "return_magnitude"
          ],
          "effect_when_higher": "Raises capital exposure and amplifies both gains and losses without changing signal timing.",
          "effect_when_lower": "Reduces capital exposure and usually lowers drawdown and upside together."
        },
        "fund_val_start": {
          "type": "number",
          "description": "Initial fund value",
          "default": 100.0
        },
        "max_positions": {
          "type": "integer",
          "description": "Maximum number of positions allowed, None for unlimited",
          "default": null,
          "impact_axes": [
            "concentration",
            "diversification"
          ],
          "effect_when_higher": "Allows more positions and can reduce concentration if the strategy actually uses them.",
          "effect_when_lower": "Forces concentration into fewer holdings and can make each position matter more."
        }
      },
      "key_params": [
        "percents",
        "max_positions",
        "initial_capital",
        "investment_amount"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use."
      ],
      "tuning_hints": [
        "Treat capital-exposure tuning as separate from signal-quality tuning so you can tell whether performance changes come from sizing or from better timing."
      ],
      "common_pitfalls": []
    },
    "GreaterThan": {
      "type": "GreaterThan",
      "category": "signal",
      "source_category": "signals",
      "class_name": "GreaterThan",
      "name": "Compares if one series is greater than another.",
      "description": "Signal is True when series_a is greater than series_b.",
      "summary": "Threshold-style signal that checks whether the first input is above the second.",
      "typical_role": "threshold_gate",
      "behavior_axes": [
        "comparison",
        "filtering",
        "signal",
        "threshold"
      ],
      "tags": [
        "signal",
        "threshold_gate"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "epsilon": {
          "type": "number",
          "description": "Small value for floating point comparison tolerance",
          "default": 0.0,
          "impact_axes": [
            "comparison_tolerance"
          ],
          "effect_when_higher": "Makes the comparison less sensitive to very small differences.",
          "effect_when_lower": "Makes the comparison more exact and more sensitive to tiny differences."
        }
      },
      "key_params": [
        "epsilon"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "HighestValue": {
      "type": "HighestValue",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "HighestValue",
      "name": "Indicator that calculates the highest value over a lookback period.",
      "description": "This indicator finds the highest value of a specified column (e.g., High, Close) over a given lookback period. This can be used for identifying potential resistance levels, calculating Donchian channels, or as components in other technical indicators.",
      "summary": "This indicator finds the highest value of a specified column (e.g., High, Close) over a given lookback period",
      "typical_role": "extreme_reference",
      "behavior_axes": [
        "extremes",
        "indicator"
      ],
      "tags": [
        "extreme_reference",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Column name to use from the input DataFrame",
          "default": "Close"
        },
        "period": {
          "type": "integer",
          "description": "Lookback period for finding extreme values",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "output_format": {
          "type": "string",
          "description": "Output format",
          "default": "series",
          "enum": [
            "series",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "period",
        "column",
        "output_format"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "IdentityTransformer": {
      "type": "IdentityTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "IdentityTransformer",
      "name": "Identity transformer that returns original data without transformation.",
      "description": "This is useful when you need to reference raw market indicator values in signal calculations.",
      "summary": "This is useful when you need to reference raw market indicator values in signal calculations",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "indicator",
        "field"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "InRange": {
      "type": "InRange",
      "category": "signal",
      "source_category": "signals",
      "class_name": "InRange",
      "name": "Check if a series is within a specified range.",
      "description": "Signal is True when the value is between lower and upper bounds. Supports inclusive and exclusive range boundaries.",
      "summary": "Checks whether a value stays inside a target band or range.",
      "typical_role": "range_filter",
      "behavior_axes": [
        "comparison",
        "filtering",
        "range",
        "signal"
      ],
      "tags": [
        "range_filter",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "include_lower": {
          "type": "boolean",
          "description": "Whether to include the lower bound",
          "default": true,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        },
        "include_upper": {
          "type": "boolean",
          "description": "Whether to include the upper bound",
          "default": true,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "include_lower",
        "include_upper"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Lag": {
      "type": "Lag",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Lag",
      "name": "Lag (shift) a time series by a specified number of periods.",
      "description": "This primitive shifts data forward in time, meaning at any given date, you see the value from `periods` days ago. This is essential for: 1. Confirmation logic: Combine with And to require \"today AND yesterday\" 2. Avoiding look-ahead bias: Use previous day's values for signals 3. Implementing \"wait N days\" patterns Example usage for \"2 consecutive days below threshold\": ```json { \"id\": \"confirmed_sell\", \"type\": \"And\", \"inputs\": [ { \"ref\": \"below_threshold\" }, { \"type\": \"Lag\", \"params\": { \"periods\": 1 }, \"inputs\": [{ \"ref\": \"below_threshold\" }] } ] } ``` This evaluates: today_below AND yesterday_below → True only on day 2+",
      "summary": "This primitive shifts data forward in time, meaning at any given date, you see the value from `periods` days ago",
      "typical_role": "general_component",
      "behavior_axes": [
        "lagging",
        "signal",
        "threshold"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "dynamic",
      "capabilities": {},
      "params": {
        "periods": {
          "type": "integer",
          "description": "Number of periods to shift. Positive values shift forward (look back at past values).",
          "default": 1,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "fill_value": {
          "type": "integer | number",
          "description": "Value to use for filling NaN positions created by the shift. For boolean series, use 0 (False) or 1 (True). None keeps NaN.",
          "default": null
        }
      },
      "key_params": [
        "periods",
        "fill_value"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "LessThan": {
      "type": "LessThan",
      "category": "signal",
      "source_category": "signals",
      "class_name": "LessThan",
      "name": "Compares if one series is less than another.",
      "description": "Signal is True when series_a is less than series_b.",
      "summary": "Threshold-style signal that checks whether the first input is below the second.",
      "typical_role": "threshold_gate",
      "behavior_axes": [
        "comparison",
        "filtering",
        "signal",
        "threshold"
      ],
      "tags": [
        "signal",
        "threshold_gate"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "epsilon": {
          "type": "number",
          "description": "Small value for floating point comparison tolerance",
          "default": 0.0,
          "impact_axes": [
            "comparison_tolerance"
          ],
          "effect_when_higher": "Makes the comparison less sensitive to very small differences.",
          "effect_when_lower": "Makes the comparison more exact and more sensitive to tiny differences."
        }
      },
      "key_params": [
        "epsilon"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "LinearScaleWeight": {
      "type": "LinearScaleWeight",
      "category": "signal",
      "source_category": "signals",
      "class_name": "LinearScaleWeight",
      "name": "线性缩放权重原语：将指标值线性映射到权重范围",
      "description": "用途： - PE从15到30，仓位从100%到30%（平滑过渡） - RSI从30到70，仓位从30%到100% - 任何基于指标的连续权重调整 输入：数值指标序列 (pd.Series of float) 输出：线性映射的权重序列 (pd.Series of float) 映射公式： normalized = (indicator - min_indicator) / (max_indicator - min_indicator) weight = min_weight + normalized * (max_weight - min_weight) 示例配置： { \"id\": \"pe_smooth_weight\", \"type\": \"LinearScaleWeight\", \"inputs\": [{\"ref\": \"pe\"}], // pe是数值指标 \"params\": { \"min_indicator\": 15, // PE=15 \"max_indicator\": 30, // PE=30 \"min_weight\": 1.0, // → 100%仓位 \"max_weight\": 0.3, // → 30%仓位 \"clip\": true // 超出范围裁剪 } } 效果： PE值: 10 15 20 25 30 35 权重: 1.0 1.0 0.77 0.53 0.3 0.3 (clip=true)",
      "summary": "Maps an indicator value smoothly into a target weight range, useful for gradual exposure changes.",
      "typical_role": "weight_control",
      "behavior_axes": [
        "position_sizing",
        "risk",
        "scaling",
        "signal",
        "weighting"
      ],
      "tags": [
        "signal",
        "weight_control"
      ],
      "inputs": {
        "count": "variable",
        "types": [
          {
            "name": "indicator",
            "type": "_empty"
          }
        ]
      },
      "outputs": {
        "type": "_empty"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "weight",
      "capabilities": {},
      "params": {
        "min_indicator": {
          "type": "number",
          "description": "指标最小值",
          "impact_axes": [
            "weight_mapping"
          ],
          "effect_when_changed": "Changes how quickly indicator values map into the target weight range."
        },
        "max_indicator": {
          "type": "number",
          "description": "指标最大值",
          "impact_axes": [
            "weight_mapping"
          ],
          "effect_when_changed": "Changes how quickly indicator values map into the target weight range."
        },
        "min_weight": {
          "type": "number",
          "description": "映射到的最小权重",
          "impact_axes": [
            "capital_exposure",
            "position_sizing"
          ],
          "effect_when_higher": "Assigns a larger target weight and increases exposure when that branch is active.",
          "effect_when_lower": "Assigns a smaller target weight and reduces exposure when that branch is active."
        },
        "max_weight": {
          "type": "number",
          "description": "映射到的最大权重",
          "impact_axes": [
            "capital_exposure",
            "position_sizing"
          ],
          "effect_when_higher": "Assigns a larger target weight and increases exposure when that branch is active.",
          "effect_when_lower": "Assigns a smaller target weight and reduces exposure when that branch is active."
        },
        "clip": {
          "type": "boolean",
          "description": "是否裁剪超出范围的值",
          "default": true,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "min_weight",
        "max_weight",
        "min_indicator",
        "max_indicator"
      ],
      "when_to_use": [
        "Use when you want exposure to change while keeping the entry and exit logic mostly the same."
      ],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "LowestValue": {
      "type": "LowestValue",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "LowestValue",
      "name": "Indicator that calculates the lowest value over a lookback period.",
      "description": "This indicator finds the lowest value of a specified column (e.g., Low, Close) over a given lookback period. This can be used for identifying potential support levels, calculating Donchian channels, or as components in other technical indicators.",
      "summary": "This indicator finds the lowest value of a specified column (e.g., Low, Close) over a given lookback period",
      "typical_role": "extreme_reference",
      "behavior_axes": [
        "extremes",
        "indicator"
      ],
      "tags": [
        "extreme_reference",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Column name to use from the input DataFrame",
          "default": "Close"
        },
        "period": {
          "type": "integer",
          "description": "Lookback period for finding extreme values",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "output_format": {
          "type": "string",
          "description": "Output format",
          "default": "series",
          "enum": [
            "series",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "period",
        "column",
        "output_format"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "MACD": {
      "type": "MACD",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "MACD",
      "name": "Moving Average Convergence Divergence indicator primitive.",
      "description": "Calculates the MACD technical indicator which shows the relationship between two moving averages of a security's price. This is a multi-output indicator that produces three separate values: 1. MACD Line: The difference between fast and slow EMAs 2. Signal Line: An EMA of the MACD Line 3. Histogram: The difference between MACD Line and Signal Line",
      "summary": "Calculates the MACD technical indicator which shows the relationship between two moving averages of a security's price",
      "typical_role": "momentum_filter",
      "behavior_axes": [
        "indicator",
        "momentum"
      ],
      "tags": [
        "indicator",
        "momentum_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Dict",
        "names": [
          "macd",
          "signal",
          "histogram"
        ]
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "fast_period": {
          "type": "integer",
          "description": "The period for the fast EMA",
          "default": 12,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "slow_period": {
          "type": "integer",
          "description": "The period for the slow EMA",
          "default": 26,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "signal_period": {
          "type": "integer",
          "description": "The period for the signal EMA",
          "default": 9,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "column": {
          "type": "string",
          "description": "The column to calculate MACD on",
          "default": "Close"
        },
        "output_format": {
          "type": "string",
          "description": "Output format, either 'dict' or 'dataframe'",
          "default": "dict",
          "enum": [
            "dict",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "fast_period",
        "slow_period",
        "signal_period",
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "MovingAverageTransformer": {
      "type": "MovingAverageTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "MovingAverageTransformer",
      "name": "Transformer that calculates moving averages of market indicator data.",
      "description": "Supports simple, exponential, and weighted moving averages.",
      "summary": "Transforms a market indicator into a smoothed moving-average series for regime or filter logic.",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context",
        "smoothing",
        "trend"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "window": {
          "type": "integer",
          "description": "Size of the moving average window",
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "method": {
          "type": "string",
          "description": "Type of moving average",
          "default": "simple",
          "enum": [
            "simple",
            "exponential",
            "weighted"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "window",
        "indicator",
        "method",
        "field"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "Multiply": {
      "type": "Multiply",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Multiply",
      "name": "Signal that multiplies two time series and compares the result.",
      "description": "This signal multiplies two input series and compares the result against either a threshold value or a third input series.",
      "summary": "This signal multiplies two input series and compares the result against either a threshold value or a third input series",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal",
        "threshold"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "dynamic",
      "capabilities": {},
      "params": {
        "operation": {
          "type": "string",
          "description": "Type of operation to perform",
          "default": "multiply",
          "enum": [
            "add",
            "subtract",
            "multiply",
            "divide"
          ]
        },
        "comparison": {
          "type": "string",
          "description": "Type of comparison",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal",
            "greater_equal",
            "less_equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "threshold": {
          "type": "number",
          "description": "Value to compare result against (if not comparing against another series)",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "absolute": {
          "type": "boolean",
          "description": "Whether to take the absolute value of the result before comparison",
          "default": false
        },
        "return_calculation": {
          "type": "boolean",
          "description": "If True, return the numeric calculation result instead of boolean comparison result. Supports multiple operands.",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "comparison",
        "operation",
        "absolute"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Not": {
      "type": "Not",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Not",
      "name": "Logical NOT operator for signal primitives.",
      "description": "Signal is True when the input signal is False, and vice versa.",
      "summary": "Logical negation that flips a boolean condition.",
      "typical_role": "signal_inverter",
      "behavior_axes": [
        "filtering",
        "inversion",
        "logic",
        "signal"
      ],
      "tags": [
        "signal",
        "signal_inverter"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "fill_method": {
          "type": "string",
          "description": "Method to handle NaN values",
          "default": null,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        },
        "preserve_na": {
          "type": "boolean",
          "description": "If True, NaN values remain NaN in the output",
          "default": false,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "fill_method",
        "preserve_na"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "OperatingCashflow": {
      "type": "OperatingCashflow",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "OperatingCashflow",
      "name": "Operating cash flow fundamental indicator primitive.",
      "description": "Reads the operating cash flow series from the configured column.",
      "summary": "Reads the operating cash flow series from the configured column",
      "typical_role": "general_component",
      "behavior_axes": [
        "cash_management",
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Operating cash flow column",
          "default": "operating_cashflow"
        }
      },
      "key_params": [
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Or": {
      "type": "Or",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Or",
      "name": "Logical OR operator for signal primitives.",
      "description": "Signal is True when either input signal is True.",
      "summary": "Logical gate that fires when any input condition is true.",
      "typical_role": "signal_broadener",
      "behavior_axes": [
        "broadening",
        "filtering",
        "frequency",
        "logic",
        "signal"
      ],
      "tags": [
        "signal",
        "signal_broadener"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "fill_method": {
          "type": "string",
          "description": "Method to handle NaN values",
          "default": null,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "fill_method"
      ],
      "when_to_use": [
        "Use when you want several alternative paths to produce the same signal.",
        "Helpful when combining multiple entry styles or fallback exits."
      ],
      "tuning_hints": [
        "If false signals are a problem, review whether this broadener is letting too many branches fire."
      ],
      "common_pitfalls": [
        "OR can increase trade frequency quickly and make attribution harder when many branches are active."
      ]
    },
    "PE_TTM": {
      "type": "PE_TTM",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "PETTM",
      "name": "PE TTM fundamental indicator primitive.",
      "description": "Reads the trailing-twelve-month PE series from the configured column.",
      "summary": "Reads the trailing-twelve-month PE series from the configured column",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "PE TTM column",
          "default": "pe_ttm"
        }
      },
      "key_params": [
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "PercentCapitalStrategy": {
      "type": "PercentCapitalStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "PercentCapitalStrategy",
      "name": "百分比资金分配策略",
      "description": "",
      "summary": "Allocates a fixed percentage of total portfolio value to each position.",
      "typical_role": "capital_exposure",
      "behavior_axes": [
        "capital",
        "position_sizing",
        "risk"
      ],
      "tags": [
        "capital",
        "capital_exposure"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "cash_based",
        "target_weight_compatible": false,
        "atomic_complete_vector_compatible": false,
        "audited_target_weight_override": false
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "percents": {
          "type": "number",
          "description": "Percentage of total value to allocate per position",
          "default": 20.0,
          "impact_axes": [
            "capital_exposure",
            "drawdown",
            "return_magnitude"
          ],
          "effect_when_higher": "Raises capital exposure and amplifies both gains and losses without changing signal timing.",
          "effect_when_lower": "Reduces capital exposure and usually lowers drawdown and upside together."
        },
        "max_positions": {
          "type": "integer",
          "description": "Maximum number of positions allowed, None for unlimited",
          "default": null,
          "impact_axes": [
            "concentration",
            "diversification"
          ],
          "effect_when_higher": "Allows more positions and can reduce concentration if the strategy actually uses them.",
          "effect_when_lower": "Forces concentration into fewer holdings and can make each position matter more."
        }
      },
      "key_params": [
        "percents",
        "max_positions",
        "initial_capital"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use.",
        "Use when you want simple, repeatable position sizing per trade."
      ],
      "tuning_hints": [
        "Treat capital-exposure tuning as separate from signal-quality tuning so you can tell whether performance changes come from sizing or from better timing.",
        "This changes capital exposure, not signal timing. Tune it after you understand whether the signal logic is sound."
      ],
      "common_pitfalls": [
        "Very high percents can dominate the result and hide whether the signal logic itself is good."
      ]
    },
    "PercentChange": {
      "type": "PercentChange",
      "category": "signal",
      "source_category": "signals",
      "class_name": "PercentChange",
      "name": "Calculate percent change signal over specified period.",
      "description": "Signal is True when the percent change meets the threshold criteria.",
      "summary": "Momentum-style trigger based on percentage change over a configurable number of periods.",
      "typical_role": "momentum_trigger",
      "behavior_axes": [
        "momentum",
        "responsiveness",
        "signal",
        "threshold"
      ],
      "tags": [
        "momentum_trigger",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "periods": {
          "type": "integer",
          "description": "Number of periods to calculate change over",
          "default": 1,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "threshold": {
          "type": "number",
          "description": "Value to compare percent change against",
          "default": 0,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "comparison": {
          "type": "string",
          "description": "Comparison type",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "is_absolute": {
          "type": "boolean",
          "description": "Whether to use absolute value of percent change",
          "default": false,
          "impact_axes": [
            "edge_case_behavior"
          ],
          "effect_when_changed": "Changes handling details and can matter at the edges, but usually is not the first tuning lever."
        }
      },
      "key_params": [
        "periods",
        "threshold",
        "comparison",
        "is_absolute"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "PercentFromHighest": {
      "type": "PercentFromHighest",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "PercentFromHighest",
      "name": "Indicator that calculates the percent distance from the highest value.",
      "description": "This indicator computes how far the current price is from the highest value over a given lookback period, expressed as a percentage. This can be useful for identifying potential entry or exit points based on price pullbacks.",
      "summary": "This indicator computes how far the current price is from the highest value over a given lookback period, expressed as a percentage",
      "typical_role": "extreme_reference",
      "behavior_axes": [
        "extremes",
        "indicator"
      ],
      "tags": [
        "extreme_reference",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Column name to use from the input DataFrame",
          "default": "Close"
        },
        "period": {
          "type": "integer",
          "description": "Lookback period for finding extreme values",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "output_format": {
          "type": "string",
          "description": "Output format",
          "default": "series",
          "enum": [
            "series",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "period",
        "column",
        "output_format"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "PercentFromLowest": {
      "type": "PercentFromLowest",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "PercentFromLowest",
      "name": "Indicator that calculates the percent distance from the lowest value.",
      "description": "This indicator computes how far the current price is from the lowest value over a given lookback period, expressed as a percentage. This can be useful for identifying potential entry or exit points based on price rebounds.",
      "summary": "This indicator computes how far the current price is from the lowest value over a given lookback period, expressed as a percentage",
      "typical_role": "extreme_reference",
      "behavior_axes": [
        "extremes",
        "indicator"
      ],
      "tags": [
        "extreme_reference",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Column name to use from the input DataFrame",
          "default": "Close"
        },
        "period": {
          "type": "integer",
          "description": "Lookback period for finding extreme values",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "output_format": {
          "type": "string",
          "description": "Output format",
          "default": "series",
          "enum": [
            "series",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "period",
        "column",
        "output_format"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "PercentileRankTransformer": {
      "type": "PercentileRankTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "PercentileRankTransformer",
      "name": "Transformer that calculates the percentile rank of market indicator values.",
      "description": "This is useful for normalizing indicators to a 0-100 scale based on historical distributions.",
      "summary": "Transforms a market indicator into a percentile rank, useful for regime or extreme-state filters.",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context",
        "normalization",
        "threshold"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "lookback": {
          "type": "integer",
          "description": "Historical period for percentile calculation",
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "lookback",
        "indicator",
        "field"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "ProportionalCapitalStrategy": {
      "type": "ProportionalCapitalStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "ProportionalCapitalStrategy",
      "name": "Proportional Capital Allocation Strategy.",
      "description": "This strategy allows setting different maximum position sizes for different symbols, making it ideal for multi-asset portfolios where different assets have different risk profiles. Key features: - Set individual maximum allocation for each symbol - Perfect for combining stable and aggressive assets - Works with \"all-in/all-out\" trading strategies (buy when signal, sell when exit) - Does NOT perform rebalancing (use RebalancingCapitalStrategy for that) Use cases: 1. Multi-asset trend following: - Large cap ETF (SPY): max 30% - Tech ETF (QQQ): max 25% - 3x Leveraged (SOXL): max 15% 2. Global diversification with risk control: - US stocks: max 40% - Emerging markets: max 20% - Commodities: max 15% 3. Crypto portfolio with different volatility: - BTC: max 50% - ETH: max 30% - Altcoins: max 10% each Comparison with other strategies: - vs PercentCapitalStrategy: That uses same % for all symbols - vs RebalancingCapitalStrategy: That maintains exact weights with rebalancing - vs FixedInvestmentStrategy: That provides periodic cash injections Example: If total portfolio = $100,000: - SPY (weight 0.3): Buy up to $30,000 - SOXL (weight 0.15): Buy up to $15,000 When sell signal comes: Close entire position (all-out)",
      "summary": "This strategy allows setting different maximum position sizes for different symbols, making it ideal for multi-asset portfolios where different assets have different risk profiles",
      "typical_role": "capital_exposure",
      "behavior_axes": [
        "capital",
        "cash_management",
        "position_sizing"
      ],
      "tags": [
        "capital",
        "capital_exposure"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "cash_based",
        "target_weight_compatible": false,
        "atomic_complete_vector_compatible": false,
        "audited_target_weight_override": false
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "weights": {
          "type": "object",
          "description": "Maximum allocation weight for each symbol (e.g., {'SPY': 0.3, 'QQQ': 0.25})"
        }
      },
      "key_params": [
        "initial_capital",
        "weights"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use."
      ],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "ROE": {
      "type": "ROE",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "ROE",
      "name": "Return on equity fundamental indicator primitive.",
      "description": "Reads the ROE series from the configured column as a numeric time series.",
      "summary": "Reads the ROE series from the configured column as a numeric time series",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Return on Equity column",
          "default": "roe"
        }
      },
      "key_params": [
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "RSI": {
      "type": "RSI",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "RSI",
      "name": "Relative Strength Index indicator primitive.",
      "description": "Calculates the RSI technical indicator which measures the magnitude of recent price changes to evaluate overbought or oversold conditions.",
      "summary": "Momentum oscillator commonly used to detect overbought, oversold, or mean-reversion conditions.",
      "typical_role": "momentum_filter",
      "behavior_axes": [
        "indicator",
        "momentum",
        "oscillator",
        "threshold"
      ],
      "tags": [
        "indicator",
        "momentum_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for the RSI calculation",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "column": {
          "type": "string",
          "description": "The column to calculate RSI on",
          "default": "Close"
        }
      },
      "key_params": [
        "period",
        "column"
      ],
      "when_to_use": [
        "Use when you need momentum context or threshold-based entries and exits.",
        "Common for oversold rebound or overbought risk-control logic."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea.",
        "Thresholds often matter more than small period changes when the strategy is threshold-driven.",
        "Keep threshold changes small so the signal meaning stays comparable."
      ],
      "common_pitfalls": []
    },
    "RSITransformer": {
      "type": "RSITransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "RSITransformer",
      "name": "Transformer that calculates the Relative Strength Index (RSI) of market indicator values.",
      "description": "RSI is a momentum oscillator that measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.",
      "summary": "Calculates RSI over a market indicator so it can be used as a market regime or filter input.",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context",
        "momentum",
        "oscillator"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "window": {
          "type": "integer",
          "description": "Period for RSI calculation",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "window",
        "indicator",
        "field"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "RateOfChange": {
      "type": "RateOfChange",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "RateOfChange",
      "name": "Numeric decimal return over a fixed number of prior observations.",
      "description": "",
      "summary": "Numeric decimal return over a fixed observation lookback for per-symbol ranking features.",
      "typical_role": "cross_sectional_feature",
      "behavior_axes": [
        "indicator",
        "lookback",
        "momentum",
        "ranking"
      ],
      "tags": [
        "cross_sectional_feature",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "periods": {
          "type": "integer",
          "description": "Positive lookback in trading observations",
          "default": 1,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "column": {
          "type": "string",
          "description": "Input data column used to calculate decimal returns",
          "default": "Close"
        }
      },
      "key_params": [
        "periods",
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": [
        "Returns are decimals rather than percentages, and the warm-up window is NaN."
      ]
    },
    "RebalanceCadence": {
      "type": "RebalanceCadence",
      "category": "signal",
      "source_category": "signals",
      "class_name": "RebalanceCadence",
      "name": "Portfolio-calendar cadence with first-observed-period bootstrap control.",
      "description": "",
      "summary": "Marks daily sessions or the first actual Portfolio trading session of each ISO week or month, with first-observed-period bootstrap control.",
      "typical_role": "rebalance_cadence",
      "behavior_axes": [
        "bootstrap",
        "calendar",
        "daily",
        "monthly",
        "signal",
        "weekly"
      ],
      "tags": [
        "rebalance_cadence",
        "signal"
      ],
      "inputs": {
        "count": 0,
        "types": []
      },
      "outputs": {
        "type": "cadence"
      },
      "evaluation_domain": "cross_sectional",
      "output_type": "cadence",
      "capabilities": {},
      "params": {
        "frequency": {
          "type": "string",
          "description": "Portfolio decision-session cadence",
          "default": "monthly_first_session",
          "enum": [
            "daily",
            "weekly_first_session",
            "monthly_first_session"
          ]
        },
        "bootstrap_on_first_session": {
          "type": "boolean",
          "description": "For weekly/monthly cadence, act on the first observed Portfolio session when true; when false, wait for the next calendar period. Daily cadence is unaffected.",
          "default": true
        }
      },
      "key_params": [
        "frequency",
        "bootstrap_on_first_session"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": [
        "Weekly and monthly cadence use the aligned Portfolio trading calendar, not wall-clock Mondays or month starts.",
        "When bootstrap is false, the entire first observed weekly/monthly period is suppressed, even if the Portfolio starts on that period's first session."
      ]
    },
    "RebalancingCapitalStrategy": {
      "type": "RebalancingCapitalStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "RebalancingCapitalStrategy",
      "name": "Rebalancing Capital Allocation Strategy.",
      "description": "This strategy is specifically designed to work with rebalancing strategies (like TargetWeightStrategy) that include target_weight information in their signals. Key features: - Reads target weights directly from trading signals - Projects normalized Cross-sectional model weights to configured gross exposure - Calculates capital needed to reach target allocation - Preserves final executable target weights in signal feeds and persistence Design philosophy: - Trading strategy (signal) decides WHAT and WHEN to trade - Capital strategy (this) decides HOW MUCH to allocate - Trade construction decides relative weights - Capital strategy decides invested exposure and retained cash Example usage: For HFEA strategy (55% UPRO + 45% TMF): - TargetWeightStrategy generates final target_weight signals - RebalancingCapitalStrategy reads target_weight from signal - Calculates how much capital needed to reach that weight Comparison with other strategies: - PercentCapitalStrategy: Uses fixed % of cash, no target weight - FixedInvestmentStrategy: Periodic cash injection, signals have weights - RebalancingCapitalStrategy: Reads weights from signals dynamically",
      "summary": "Capital strategy that atomically maintains target weights and scales normalized Cross-sectional vectors to an explicit gross exposure.",
      "typical_role": "rebalancing",
      "behavior_axes": [
        "capital",
        "cash_buffer",
        "cash_management",
        "gross_exposure",
        "position_sizing",
        "rebalancing",
        "weighting"
      ],
      "tags": [
        "capital",
        "rebalancing"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "target_weight",
        "target_weight_compatible": true,
        "atomic_complete_vector_compatible": true,
        "audited_target_weight_override": false
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "gross_exposure": {
          "type": "number",
          "description": "Fraction of portfolio value invested by normalized Cross-sectional complete target vectors",
          "default": 1.0
        }
      },
      "key_params": [
        "initial_capital",
        "gross_exposure"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use."
      ],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "RelativeStrengthTransformer": {
      "type": "RelativeStrengthTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "RelativeStrengthTransformer",
      "name": "Transformer that calculates the relative strength of a market indicator.",
      "description": "Relative strength is calculated as the ratio of current value to a reference value (typically a moving average or historical value).",
      "summary": "Transforms market indicator data into a relative-strength style series against a chosen reference.",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context",
        "relative_strength",
        "trend"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "reference": {
          "type": "string",
          "description": "Reference method: 'ma', 'value', or 'lookback'",
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "window": {
          "type": "integer",
          "description": "Window size for 'ma' or 'lookback' reference",
          "default": null,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "value": {
          "type": "number",
          "description": "Fixed value for 'value' reference",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the value shifts when the condition becomes true; test small changes because direction depends on the surrounding comparison or reference logic."
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "window",
        "indicator",
        "reference",
        "value"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    },
    "RevenueYoY": {
      "type": "RevenueYoY",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "RevenueYoY",
      "name": "Revenue YoY fundamental indicator primitive.",
      "description": "Reads the year-over-year revenue growth series from the configured column.",
      "summary": "Reads the year-over-year revenue growth series from the configured column",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "column": {
          "type": "string",
          "description": "Revenue YoY column",
          "default": "revenue_yoy"
        }
      },
      "key_params": [
        "column"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "SMA": {
      "type": "SMA",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "SMA",
      "name": "Simple Moving Average indicator primitive.",
      "description": "Uses pandas' rolling window calculation for Simple Moving Average computation.",
      "summary": "Simple moving average used as a smoothing baseline and long/medium-term trend filter.",
      "typical_role": "trend_filter",
      "behavior_axes": [
        "indicator",
        "lagging",
        "smoothing",
        "trend"
      ],
      "tags": [
        "indicator",
        "trend_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "period": {
          "type": "integer",
          "description": "The lookback period for the moving average",
          "default": 20,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "column": {
          "type": "string",
          "description": "The column to calculate SMA on",
          "default": "Close"
        }
      },
      "key_params": [
        "period",
        "column"
      ],
      "when_to_use": [
        "Use when you want a slower baseline to judge whether price is aligned with the prevailing trend.",
        "Use when you want a simple trend baseline or a crossover component.",
        "Useful for reducing noise before building entry or exit signals."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea.",
        "Period is usually the first parameter to test because it changes signal speed without changing the overall strategy idea.",
        "Longer periods usually reduce trade frequency; shorter periods usually react faster but add noise."
      ],
      "common_pitfalls": [
        "Very long periods can make entries late and reduce participation in early trend phases.",
        "Very short periods can overreact in choppy markets."
      ]
    },
    "SimplePercentCapitalStrategy": {
      "type": "SimplePercentCapitalStrategy",
      "category": "capital_strategy",
      "source_category": "capital_strategies",
      "class_name": "SimplePercentCapitalStrategy",
      "name": "使用可用现金百分比进行资金分配的策略",
      "description": "与PercentCapitalStrategy不同，本策略基于当前可用现金而非总资产计算分配金额",
      "summary": "与PercentCapitalStrategy不同，本策略基于当前可用现金而非总资产计算分配金额",
      "typical_role": "capital_exposure",
      "behavior_axes": [
        "capital",
        "position_sizing"
      ],
      "tags": [
        "capital",
        "capital_exposure"
      ],
      "inputs": {
        "count": 4,
        "types": [
          {
            "name": "cash",
            "type": "float"
          },
          {
            "name": "total_value",
            "type": "float"
          },
          {
            "name": "data",
            "type": "Any"
          },
          {
            "name": "broker",
            "type": "Any"
          }
        ]
      },
      "outputs": {
        "type": "float"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {
        "allocation_mode": "cash_based",
        "target_weight_compatible": false,
        "atomic_complete_vector_compatible": false,
        "audited_target_weight_override": false
      },
      "params": {
        "initial_capital": {
          "type": "number",
          "description": "Initial capital amount",
          "default": 100000.0
        },
        "percents": {
          "type": "number",
          "description": "Percentage of available cash to allocate per position",
          "default": 20.0,
          "impact_axes": [
            "capital_exposure",
            "drawdown",
            "return_magnitude"
          ],
          "effect_when_higher": "Raises capital exposure and amplifies both gains and losses without changing signal timing.",
          "effect_when_lower": "Reduces capital exposure and usually lowers drawdown and upside together."
        },
        "max_positions": {
          "type": "integer",
          "description": "Maximum number of positions allowed, None for unlimited",
          "default": null,
          "impact_axes": [
            "concentration",
            "diversification"
          ],
          "effect_when_higher": "Allows more positions and can reduce concentration if the strategy actually uses them.",
          "effect_when_lower": "Forces concentration into fewer holdings and can make each position matter more."
        }
      },
      "key_params": [
        "percents",
        "max_positions",
        "initial_capital"
      ],
      "when_to_use": [
        "Use when signal logic is already defined and you need to control how much capital each trade can use."
      ],
      "tuning_hints": [
        "Treat capital-exposure tuning as separate from signal-quality tuning so you can tell whether performance changes come from sizing or from better timing."
      ],
      "common_pitfalls": []
    },
    "Stochastic": {
      "type": "Stochastic",
      "category": "indicator",
      "source_category": "indicators",
      "class_name": "Stochastic",
      "name": "Stochastic Oscillator indicator primitive.",
      "description": "Calculates the Stochastic Oscillator which compares a security's closing price to its price range over a given time period. The indicator consists of two lines: %K (the main line) and %D (the signal line).",
      "summary": "Calculates the Stochastic Oscillator which compares a security's closing price to its price range over a given time period",
      "typical_role": "general_component",
      "behavior_axes": [
        "indicator",
        "oscillator",
        "range"
      ],
      "tags": [
        "general_component",
        "indicator"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Union",
        "names": [
          "k",
          "d"
        ]
      },
      "evaluation_domain": "per_symbol",
      "output_type": "numeric",
      "capabilities": {},
      "params": {
        "k_period": {
          "type": "integer",
          "description": "The lookback period for %K calculation",
          "default": 14,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "d_period": {
          "type": "integer",
          "description": "The period for the %D moving average",
          "default": 3,
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "d_method": {
          "type": "string",
          "description": "The method for calculating %D",
          "default": "simple",
          "enum": [
            "simple",
            "exponential"
          ]
        },
        "output_format": {
          "type": "string",
          "description": "Output format",
          "default": "dict",
          "enum": [
            "dict",
            "dataframe"
          ]
        }
      },
      "key_params": [
        "k_period",
        "d_period",
        "d_method",
        "output_format"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "StockBondSwitch": {
      "type": "StockBondSwitch",
      "category": "signal",
      "source_category": "signals",
      "class_name": "StockBondSwitch",
      "name": "Stock-Bond switching signal primitive.",
      "description": "This primitive implements a stock-bond switching strategy where: - When condition is True: hold stock ETF (symbols[0]) - When condition is False: hold bond ETF (symbols[1]) The primitive returns appropriate signals based on the current symbol being evaluated and the market condition. Convention: - symbols[0] should be the stock ETF - symbols[1] should be the bond ETF",
      "summary": "This primitive implements a stock-bond switching strategy where: - When condition is True: hold stock ETF (symbols[0]) - When condition is False: hold bond ETF (symbols[1]) The primitive returns appropriate signals based on the current symbol being evaluated and the market condition",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "condition_signal",
            "type": "Series"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "default_to_stock": {
          "type": "boolean",
          "description": "Whether to default to stock when condition is unavailable",
          "default": true
        }
      },
      "key_params": [
        "default_to_stock"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Streak": {
      "type": "Streak",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Streak",
      "name": "Streak pattern detection signal primitive.",
      "description": "Detects when a signal has occurred consecutively for a specified number of periods.",
      "summary": "Detects when a signal has occurred consecutively for a specified number of periods",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "boolean",
      "capabilities": {},
      "params": {
        "min_length": {
          "type": "integer",
          "description": "Minimum number of consecutive occurrences required",
          "default": 2
        },
        "max_length": {
          "type": "integer",
          "description": "Maximum number of consecutive occurrences to consider",
          "default": null
        },
        "match_type": {
          "type": "string",
          "description": "Type of streak to detect",
          "default": "true",
          "enum": [
            "true",
            "false",
            "any"
          ]
        }
      },
      "key_params": [
        "min_length",
        "max_length",
        "match_type"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "Subtract": {
      "type": "Subtract",
      "category": "signal",
      "source_category": "signals",
      "class_name": "Subtract",
      "name": "Signal that subtracts one time series from another and compares the result.",
      "description": "This signal subtracts the second input series from the first and compares the result against either a threshold value or a third input series.",
      "summary": "This signal subtracts the second input series from the first and compares the result against either a threshold value or a third input series",
      "typical_role": "general_component",
      "behavior_axes": [
        "signal",
        "threshold"
      ],
      "tags": [
        "general_component",
        "signal"
      ],
      "inputs": {
        "count": "variable",
        "types": []
      },
      "outputs": {
        "type": "Union"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "dynamic",
      "capabilities": {},
      "params": {
        "operation": {
          "type": "string",
          "description": "Type of operation to perform",
          "default": "subtract",
          "enum": [
            "add",
            "subtract",
            "multiply",
            "divide"
          ]
        },
        "comparison": {
          "type": "string",
          "description": "Type of comparison",
          "default": "greater",
          "enum": [
            "greater",
            "less",
            "equal",
            "greater_equal",
            "less_equal"
          ],
          "impact_axes": [
            "signal_definition"
          ],
          "effect_when_changed": "Changes the interpretation or transformation rule rather than just nudging sensitivity."
        },
        "threshold": {
          "type": "number",
          "description": "Value to compare result against (if not comparing against another series)",
          "default": null,
          "impact_axes": [
            "threshold_strictness"
          ],
          "effect_when_changed": "Changing the threshold shifts when the condition becomes true; test small changes because direction depends on the comparison logic around it."
        },
        "absolute": {
          "type": "boolean",
          "description": "Whether to take the absolute value of the result before comparison",
          "default": false
        },
        "return_calculation": {
          "type": "boolean",
          "description": "If True, return the numeric calculation result instead of boolean comparison result. Supports multiple operands.",
          "default": false
        }
      },
      "key_params": [
        "threshold",
        "comparison",
        "operation",
        "absolute"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "TopN": {
      "type": "TopN",
      "category": "signal",
      "source_category": "signals",
      "class_name": "TopN",
      "name": "Select the lowest N finite ordinal ranks and carry membership between decisions.",
      "description": "",
      "summary": "Selects at most N lowest ordinal ranks and carries reporting membership until rebalance.",
      "typical_role": "cross_sectional_selection",
      "behavior_axes": [
        "membership",
        "selection",
        "signal",
        "top_n"
      ],
      "tags": [
        "cross_sectional_selection",
        "signal"
      ],
      "inputs": {
        "count": 2,
        "types": [
          "rank",
          "cadence"
        ]
      },
      "outputs": {
        "type": "selection"
      },
      "evaluation_domain": "cross_sectional",
      "output_type": "selection",
      "capabilities": {},
      "params": {
        "n": {
          "type": "integer",
          "description": "Maximum selected symbol count",
          "default": 1
        }
      },
      "key_params": [
        "n"
      ],
      "when_to_use": [],
      "tuning_hints": [],
      "common_pitfalls": []
    },
    "ZScoreTransformer": {
      "type": "ZScoreTransformer",
      "category": "market_transformer",
      "source_category": "market_transformers",
      "class_name": "ZScoreTransformer",
      "name": "Transformer that calculates Z-scores of market indicator values.",
      "description": "Z-score represents how many standard deviations a value is from the mean, useful for identifying extreme values.",
      "summary": "Z-score represents how many standard deviations a value is from the mean, useful for identifying extreme values",
      "typical_role": "market_filter",
      "behavior_axes": [
        "market_context"
      ],
      "tags": [
        "market_context",
        "market_filter"
      ],
      "inputs": {
        "count": 1,
        "types": [
          {
            "name": "data",
            "type": "DataFrame"
          }
        ]
      },
      "outputs": {
        "type": "Series"
      },
      "evaluation_domain": "per_symbol",
      "output_type": "unknown",
      "capabilities": {},
      "params": {
        "indicator": {
          "type": "string",
          "description": "Market indicator code"
        },
        "window": {
          "type": "integer",
          "description": "Historical period for mean/std calculation",
          "impact_axes": [
            "responsiveness",
            "trade_frequency"
          ],
          "effect_when_higher": "Usually smoother and slower, which often reduces trigger frequency and reacts later to new moves.",
          "effect_when_lower": "Usually faster and more sensitive, which often increases trigger frequency but also increases noise."
        },
        "field": {
          "type": "string",
          "description": "Field to use from the indicator data",
          "default": "Close"
        }
      },
      "key_params": [
        "window",
        "indicator",
        "field"
      ],
      "when_to_use": [
        "Use when market-wide context should filter or reshape the strategy instead of directly generating trades."
      ],
      "tuning_hints": [
        "Period-style parameters are often the cleanest first test because they change speed without changing the whole strategy idea."
      ],
      "common_pitfalls": []
    }
  }
}
