Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/najmulhossainnj/Hedge-fund-backend/llms.txt

Use this file to discover all available pages before exploring further.

Raw model predictions — floating-point forward-return estimates — are not directly tradeable. The Signal Engine bridges that gap by applying a configurable rule tree or a signal generator plugin to turn predictions into discrete BUY, SELL, or HOLD directives (or their numeric equivalents +1, 0, -1). Signal logic is stored as a versioned SignalLogic record, making every signal regime auditable and reproducible.

SignalLogic Entity

A SignalLogic row stores the complete rule tree together with output and position mode settings.
{
  "id": "s1000000-0000-0000-0000-000000000001",
  "name": "Momentum Threshold v2",
  "description": "BUY when prediction > 0.6 AND sentiment > 0.4, SELL when prediction < -0.4",
  "rule_tree": [
    {
      "action": "BUY",
      "combinator": "AND",
      "rules": [
        { "field": "prediction", "operator": ">",  "value": 0.6 },
        { "field": "sentiment",  "operator": ">",  "value": 0.4 }
      ]
    },
    {
      "action": "SELL",
      "combinator": "AND",
      "rules": [
        { "field": "prediction", "operator": "<", "value": -0.4 }
      ]
    }
  ],
  "output_mode": "discrete",
  "position_mode": "long_short",
  "strategy_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "version": 1,
  "created_at": "2024-01-18T11:00:00Z",
  "updated_at": "2024-01-18T11:00:00Z"
}

SignalLogic Fields

FieldTypeDescription
rule_treeRuleGroup[]Ordered list of rule groups — first match wins
output_modestring"discrete" (BUY/SELL/HOLD), "numeric" (+1/0/-1), or "score" (raw prediction pass-through)
position_modestring"long_only", "long_short", or "portfolio"
strategy_idUUID | nullOptional back-reference to the owning strategy

Two Signal Generation Modes

Rule-Tree Mode

A JSON condition tree is evaluated bar-by-bar against the prediction and feature columns. The rule tree is serialised in SignalLogic.rule_tree and can be built by the Signal Builder UI or posted as raw JSON. No code required.

Plugin Mode

A BaseSignalGenerator subclass is resolved from the signal plugin registry via plugin_key. Plugins have access to the full predictions DataFrame and can implement arbitrarily complex logic (ranking, portfolio construction, etc.).
The generation request accepts either a persisted signal_logic_id or a plugin_key — the two modes are mutually exclusive:
// POST /api/signals/generate
{
  "model_id":   "m1000000-0000-0000-0000-000000000001",
  "feature_ids": ["f1000000-0000-0000-0000-000000000001"],
  "signal_logic_id": "s1000000-0000-0000-0000-000000000001",
  "symbol":    "AAPL",
  "timeframe": "1d",
  "start_date": "2023-01-01T00:00:00Z",
  "end_date":   "2024-01-01T00:00:00Z",
  "target_horizon": 1
}

Rule Tree Structure

The rule tree is a JSON array of RuleGroup objects. Each group combines a set of leaf conditions with a combinator and maps the outcome to an action:
[
  {
    "action": "BUY",
    "combinator": "AND",
    "rules": [
      { "field": "prediction", "operator": ">",  "value": 0.7 },
      { "field": "rsi_14",     "operator": "<",  "value": 70  }
    ]
  },
  {
    "action": "SELL",
    "combinator": "OR",
    "rules": [
      { "field": "prediction", "operator": "<",  "value": -0.5 },
      { "field": "rsi_14",     "operator": ">",  "value": 80   }
    ]
  }
]
Rules are evaluated in declaration order — the first group whose conditions are satisfied determines the signal for that bar. Any bar that matches no group gets a HOLD / 0 signal.

Leaf Rule Operators

OperatorMeaning
>Strictly greater than
<Strictly less than
>=Greater than or equal
<=Less than or equal
==Equal (useful for categorical flags)
!=Not equal

Branch Rules (Nested Combinators)

Rules within a group can themselves be BranchRule objects, enabling nested AND/OR logic:
{
  "action": "BUY",
  "combinator": "AND",
  "rules": [
    {
      "combinator": "OR",
      "rules": [
        { "field": "prediction", "operator": ">", "value": 0.7 },
        { "field": "sentiment",  "operator": ">", "value": 0.8 }
      ]
    },
    { "field": "atr_14", "operator": "<", "value": 2.5 }
  ]
}
All field references in leaf rules must match column names present in the predictions DataFrame produced by the model. Use POST /api/signals/validate-rule-tree to dry-run your rule tree before saving it.

Available Signal Plugins

Plugin KeyDescription
signal.thresholdApplies fixed upper/lower thresholds to the raw prediction score
signal.long_shortMaps positive predictions to BUY, negative to SELL, near-zero to HOLD
signal.rankingRanks predictions across a universe and signals top-N long, bottom-N short
signal.portfolioConverts predictions to continuous position weights for portfolio construction
Implement your own signal logic by subclassing BaseSignalGenerator from app/plugins/base.py, assigning a unique key, and registering it in the signal plugin registry. The generate() method receives the full predictions DataFrame and must return a DataFrame with a signal column.

Validating a Rule Tree

Before wiring a rule tree to a strategy you can perform a dry run:
POST /api/signals/validate-rule-tree
The endpoint evaluates the rule tree against a sample prediction DataFrame and returns the signal distribution without persisting any records. This lets you iterate on thresholds quickly.

Signal Output

Discrete Mode (output_mode: "discrete")

Output is a signal column containing the strings "BUY", "SELL", or "HOLD".

Numeric Mode (output_mode: "numeric")

Output is a signal column containing +1 (long), -1 (short), or 0 (flat). This mode is required when feeding signals directly to portfolio optimisers or to the vectorbt backtest engine’s signal-weight sizing mode.

Score Mode (output_mode: "score")

Output is a signal column containing the raw floating-point prediction value with no thresholding applied. Useful when passing signals to downstream portfolio construction layers that perform their own ranking or weight optimisation.

Signal Summary

Every generation response includes a summary of the produced signal series:
{
  "signal_logic_id": "s1000000-0000-0000-0000-000000000001",
  "plugin_key": null,
  "model_id": "m1000000-0000-0000-0000-000000000001",
  "symbol": "AAPL",
  "metadata": { "timeframe": "1d", "start_date": "2023-01-01", "end_date": "2024-01-01" },
  "summary": {
    "total_bars": 252,
    "buy_count":  87,
    "sell_count": 64,
    "hold_count": 101,
    "signal_rate": 0.599
  },
  "preview": [
    { "timestamp": "2023-01-03", "prediction": 0.732, "signal": "BUY" },
    { "timestamp": "2023-01-04", "prediction": 0.218, "signal": "HOLD" }
  ]
}
signal_rate is the fraction of bars with a non-HOLD signal — a useful sanity check before backtesting. Very low signal rates (< 0.05) or very high rates (> 0.95) typically indicate misconfigured thresholds.

Async Generation

For large date ranges or multi-symbol universes, pass async_mode: true in the generation request. The engine dispatches the computation to a Celery worker and immediately returns a task_id. Poll GET /api/signals/tasks/{task_id} to check status and retrieve results when complete.

API Reference

For the complete endpoint reference — including rule tree CRUD, bulk generation, task polling, and signal export — see the Signals API.

Build docs developers (and LLMs) love