> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quanux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Write and generate trading strategies in QuanuX

> Build strategies with QuanuX's Python component model, then use the AI Foundry to generate Cython or C++ equivalents for backtesting and live deployment.

QuanuX strategies are built from modular components. You start in Python — where iteration is fast and the data science ecosystem is available — then graduate the logic to compiled code once you're confident in the approach. This page covers how to write a strategy from scratch and how to use the Foundry API to generate higher-performance equivalents.

## Strategy architecture

Every strategy in `server/strategies/` is assembled from four component types, each inheriting from `StrategyComponent` defined in `server/strategies/base.py`.

| Component | Base class     | Responsibility                                             |
| --------- | -------------- | ---------------------------------------------------------- |
| Signal    | `SignalModule` | Analyze market data; return `LONG`, `SHORT`, or `NEUTRAL`  |
| Entry     | `EntryModule`  | Generate an order dict when a signal is present            |
| Risk      | `RiskModule`   | Calculate stop loss, take profit, and trailing stop levels |
| Exit      | `ExitModule`   | Decide whether an open position should close               |

A `CompositeStrategy` wires these components together and drives the `on_bar` loop.

## Writing a strategy in Python

The example below is drawn from the `SmaCrossover` strategy in `server/strategies/full/SmaCrossover/`.

<CodeGroup>
  ```python signal.py theme={null}
  from server.strategies.base import SignalModule, SignalType, BaseParameters
  from typing import Type
  import pandas as pd

  class SmaSignal(SignalModule):
      """Simple Moving Average Crossover Signal.
      Long when Fast SMA > Slow SMA. Short when Fast SMA < Slow SMA.
      """
      def define_parameters(self) -> Type[BaseParameters]:
          class Params(BaseParameters):
              fast_period: int = 10
              slow_period: int = 20
          return Params

      def on_bar(self, bar_data: dict) -> SignalType:
          if not hasattr(self, '_history'):
              self._history = []

          close = bar_data.get('close')
          if close is None:
              return SignalType.NEUTRAL

          self._history.append(close)
          slow = self.params['slow_period']
          if len(self._history) > slow + 20:
              self._history.pop(0)
          if len(self._history) < slow:
              return SignalType.NEUTRAL

          series = pd.Series(self._history)
          fast_ma = series.rolling(window=self.params['fast_period']).mean().iloc[-1]
          slow_ma = series.rolling(window=self.params['slow_period']).mean().iloc[-1]

          if fast_ma > slow_ma:
              return SignalType.LONG
          elif fast_ma < slow_ma:
              return SignalType.SHORT
          return SignalType.NEUTRAL
  ```

  ```python entry.py theme={null}
  from typing import Dict, Any, Optional, Type
  from server.strategies.base import EntryModule, SignalType, BaseParameters
  from server.app.domain.contracts import Side

  class MarketEntry(EntryModule):
      def define_parameters(self) -> Type[BaseParameters]:
          return BaseParameters

      def generate_entry(self, signal: SignalType, bar_data: Any) -> Optional[Dict[str, Any]]:
          if signal == SignalType.NEUTRAL:
              return None
          side = Side.BUY if signal == SignalType.LONG else Side.SELL
          return {
              "type": "MARKET",
              "side": side,
              "time_in_force": "DAY"
          }
  ```
</CodeGroup>

<Tip>
  Define parameters using a nested Pydantic model inside `define_parameters`. QuanuX validates and coerces all values at runtime when you call `update_parameters`.
</Tip>

## Using the Foundry to generate code

Once your Python prototype is working, submit it to the AI Foundry to generate a faster equivalent. The Foundry dispatches your request as a NATS payload and returns a job ID immediately.

### POST /api/foundry/forge

Submit a generation job for a strategy component.

**Request body**

<ParamField body="component_type" type="string" required>
  The type of component to generate. Accepted values: `indicator`, `entry`, `exit`, `strategy`.
</ParamField>

<ParamField body="name" type="string" required>
  The name for the generated component, used as the file and class identifier.
</ParamField>

<ParamField body="target_lang" type="string" required>
  The output language. Accepted values: `python`, `cython`, `cpp`.
</ParamField>

<ParamField body="target_version" type="string">
  Semantic version string for the generated artifact, e.g. `1.0.0`. Optional; defaults to `null`.
</ParamField>

<ParamField body="prompt" type="string">
  Natural-language description of the strategy logic. The Foundry uses this alongside the component type and name to guide code generation.
</ParamField>

**Response**

<ResponseField name="status" type="string" required>
  Always `"accepted"` on success. The job runs asynchronously.
</ResponseField>

<ResponseField name="job_id" type="string" required>
  Unique job identifier in the format `job_xxxxxxxx`. Use this to correlate NATS telemetry events back to your request.
</ResponseField>

**Example: generate a Cython signal component**

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST http://localhost:8080/api/foundry/forge \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "component_type": "strategy",
      "name": "SmaCrossover",
      "target_lang": "cython",
      "target_version": "1.0.0",
      "prompt": "SMA crossover: long when 10-period SMA crosses above 20-period SMA, short on the reverse"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "http://localhost:8080/api/foundry/forge",
      json={
          "component_type": "strategy",
          "name": "SmaCrossover",
          "target_lang": "cython",
          "target_version": "1.0.0",
          "prompt": "SMA crossover: long when 10-period SMA crosses above 20-period SMA, short on the reverse"
      }
  )
  print(response.json())
  ```
</CodeGroup>

```json Response theme={null}
{
  "status": "accepted",
  "job_id": "job_3f8a1c2d"
}
```

### POST /api/foundry/verify

After the Foundry generates code, run equivalence testing to confirm the generated version produces mathematically identical output to your Python prototype.

**Request body**

<ParamField body="strategy_name" type="string" required>
  The name of the strategy to verify. Must match the `name` used in the forge request.
</ParamField>

**Response**

<ResponseField name="status" type="string" required>
  Always `"accepted"`. Verification runs asynchronously in a deterministic sandbox.
</ResponseField>

<ResponseField name="job_id" type="string" required>
  Unique job identifier for this verification run.
</ResponseField>

**Example**

```bash cURL theme={null}
curl -X POST http://localhost:8080/api/foundry/verify \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"strategy_name": "SmaCrossover"}'
```

```json Response theme={null}
{
  "status": "accepted",
  "job_id": "job_9b4e7a01"
}
```

## Git-as-Governance

Before a generated strategy can be promoted to C++ and deployed to the live spreader, it must be committed to your QuanuX repository. The spreader verifies the SHA-256 of every strategy artifact against a signed commit. An uncommitted or unsigned strategy will be rejected at deploy time.

<Warning>
  Do not modify generated Cython `.pyx` files by hand after committing. Any change invalidates the SHA-256 signature and will block deployment. If you need to adjust the logic, submit a new forge request and re-verify.
</Warning>

Commit your strategy files before proceeding to backtesting:

```bash theme={null}
git add server/strategies/full/SmaCrossover/
git commit -S -m "feat(strategy): add SmaCrossover v1.0.0"
```
