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

# Strategy Configuration

> Configure target profit, stop loss, trailing stop, and sell on graduation thresholds for the Exit Intelligence Stream.

## `StrategyConfigMsg` Schema

| Field                 | Type                   | Required | Description                                                                                                                                                                                            |
| --------------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `target_profit_pct`   | `number`               | Yes      | Take profit threshold as a percentage (e.g., `5` = 5%).                                                                                                                                                |
| `stop_loss_pct`       | `number`               | Yes      | Stop loss threshold as a percentage (e.g., `1.5` = 1.5%).                                                                                                                                              |
| `trailing_stop_pct`   | `number`               | No       | Trailing stop percentage from the peak. Activates only after the position is in profit.                                                                                                                |
| `sell_on_graduation`  | `boolean`              | No       | If `true`, auto sells when a bonding curve token graduates to an AMM pool.                                                                                                                             |
| `take_profit_levels`  | `TakeProfitLevelMsg[]` | No       | Exit ladder: sell partial amounts at multiple profit thresholds. Each level has `profit_pct` (trigger), `sell_pct` (portion to sell), and `trailing_stop_pct` (optional trailing stop for that level). |
| `liquidity_guard`     | `boolean`              | No       | When enabled, the stream checks available pool liquidity before generating an exit signal. Prevents exits into thin liquidity. Default: `false`.                                                       |
| `breakeven_trail_pct` | `number`               | No       | A trailing stop that activates once the position breaks even, trailing from the breakeven point rather than the peak.                                                                                  |

At least one of `target_profit_pct`, `stop_loss_pct`, `trailing_stop_pct`, or `deadline_timeout_sec` (on the configure message) must be greater than zero.

### `TakeProfitLevelMsg` Schema

| Field               | Type     | Required | Description                                                                                                                                                              |
| ------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `profit_pct`        | `number` | Yes      | Profit percentage at which this level triggers (e.g., `20` = 20%).                                                                                                       |
| `sell_pct`          | `number` | Yes      | Percentage of the position to sell at this level (e.g., `50` = sell 50%).                                                                                                |
| `trailing_stop_pct` | `number` | No       | Optional trailing stop for this level. If set, instead of selling immediately at `profit_pct`, a trailing stop activates at this level. Default: `0` (sell immediately). |

## Setting Strategy at Connection Time

Strategy is provided in the `configure` message when you first connect:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const session = await StreamSession.connect(client, {
    wallet_pubkeys: ["WALLET_PUBKEY"],
    strategy: {
      target_profit_pct: 10,
      stop_loss_pct: 2,
      trailing_stop_pct: 5,
      sell_on_graduation: true,
    },
    deadline_timeout_sec: 60,
    send_mode: "helius_sender",
    tip_lamports: 1000,
  });
  ```

  ```python Python theme={null}
  session = await StreamSession.connect(
      client,
      StreamConfigure(
          wallet_pubkeys=["WALLET_PUBKEY"],
          strategy={
              "target_profit_pct": 10.0,
              "stop_loss_pct": 2.0,
              "trailing_stop_pct": 5.0,
              "sell_on_graduation": True,
          },
          deadline_timeout_sec=60,
          send_mode="helius_sender",
          tip_lamports=1000,
      ),
  )
  ```

  ```rust Rust theme={null}
  let session = StreamSession::connect(&client, StreamConfigure {
      wallet_pubkeys: vec!["WALLET_PUBKEY".into()],
      strategy: StrategyConfigMsg {
          target_profit_pct: 10.0,
          stop_loss_pct: 2.0,
          trailing_stop_pct: Some(5.0),
          sell_on_graduation: Some(true),
      },
      deadline_timeout_sec: Some(60),
      send_mode: Some("helius_sender".to_string()),
      tip_lamports: Some(1000),
  }).await?;
  ```

  ```go Go theme={null}
  sendMode := "helius_sender"
  tipLamports := uint64(1000)
  session, err := stream.ConnectSession(ctx, client, stream.StreamConfigure{
      WalletPubkeys: []string{"WALLET_PUBKEY"},
      Strategy: stream.StrategyConfigMsg{
          TargetProfitPct:    10.0,
          StopLossPct:        2.0,
          TrailingStopPct:    5.0,
          SellOnGraduation:   true,
      },
      DeadlineTimeoutSec: 60,
      SendMode:           &sendMode,
      TipLamports:        &tipLamports,
  })
  ```
</CodeGroup>

## Using StrategyConfigBuilder

All 4 SDKs provide a fluent builder for strategy configuration. The builder validates that at least one exit condition is set when you call `build()`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { StrategyConfigBuilder } from "@lasersell/lasersell-sdk";

  const strategy = new StrategyConfigBuilder()
    .targetProfitPct(50)
    .stopLossPct(10)
    .trailingStopPct(5)
    .takeProfitLevels([
      { profit_pct: 20, sell_pct: 25, trailing_stop_pct: 0 },
      { profit_pct: 50, sell_pct: 50, trailing_stop_pct: 3 },
      { profit_pct: 100, sell_pct: 100, trailing_stop_pct: 5 },
    ])
    .liquidityGuard(true)
    .breakevenTrailPct(2)
    .build();
  ```

  ```python Python theme={null}
  from lasersell_sdk.stream.client import StrategyConfigBuilder

  strategy = (
      StrategyConfigBuilder()
      .target_profit_pct(50)
      .stop_loss_pct(10)
      .trailing_stop_pct(5)
      .take_profit_levels([
          {"profit_pct": 20, "sell_pct": 25, "trailing_stop_pct": 0},
          {"profit_pct": 50, "sell_pct": 50, "trailing_stop_pct": 3},
          {"profit_pct": 100, "sell_pct": 100, "trailing_stop_pct": 5},
      ])
      .liquidity_guard(True)
      .breakeven_trail_pct(2.0)
      .build()
  )
  ```

  ```rust Rust theme={null}
  use lasersell_sdk::stream::client::StrategyConfigBuilder;
  use lasersell_sdk::stream::proto::TakeProfitLevelMsg;

  let strategy = StrategyConfigBuilder::new()
      .target_profit_pct(50.0)
      .stop_loss_pct(10.0)
      .trailing_stop_pct(5.0)
      .take_profit_levels(vec![
          TakeProfitLevelMsg { profit_pct: 20.0, sell_pct: 25.0, trailing_stop_pct: 0.0 },
          TakeProfitLevelMsg { profit_pct: 50.0, sell_pct: 50.0, trailing_stop_pct: 3.0 },
          TakeProfitLevelMsg { profit_pct: 100.0, sell_pct: 100.0, trailing_stop_pct: 5.0 },
      ])
      .liquidity_guard(true)
      .breakeven_trail_pct(2.0)
      .build();
  ```

  ```go Go theme={null}
  import "github.com/lasersell/lasersell-sdk/go/stream"

  strategy := stream.NewStrategyConfigBuilder().
      TargetProfitPct(50.0).
      StopLossPct(10.0).
      TrailingStopPct(5.0).
      TakeProfitLevels([]stream.TakeProfitLevelMsg{
          {ProfitPct: 20.0, SellPct: 25.0, TrailingStopPct: 0.0},
          {ProfitPct: 50.0, SellPct: 50.0, TrailingStopPct: 3.0},
          {ProfitPct: 100.0, SellPct: 100.0, TrailingStopPct: 5.0},
      }).
      LiquidityGuard(true).
      BreakevenTrailPct(2.0).
      Build()
  ```
</CodeGroup>

## Exit Ladder (Take Profit Levels)

An exit ladder lets you take partial profits at multiple thresholds instead of exiting your entire position at a single price. Each level specifies a profit target and the percentage of the remaining position to sell.

### How it works

1. When profit reaches a level's `profit_pct`, the stream sells `sell_pct` of the current position.
2. If a level has `trailing_stop_pct > 0`, a trailing stop activates at that level instead of selling immediately.
3. Levels are evaluated in order from lowest to highest `profit_pct`.
4. The global `target_profit_pct` still applies as a hard cap; if reached before all levels fire, the remaining position is sold.

### Example

With this exit ladder and an entry of 1 SOL:

```json theme={null}
{
  "take_profit_levels": [
    { "profit_pct": 20, "sell_pct": 25, "trailing_stop_pct": 0 },
    { "profit_pct": 50, "sell_pct": 50, "trailing_stop_pct": 3 },
    { "profit_pct": 100, "sell_pct": 100, "trailing_stop_pct": 5 }
  ],
  "stop_loss_pct": 15
}
```

* At 20% profit (position worth 1.2 SOL): sell 25% immediately.
* At 50% profit (position worth 1.5 SOL): activate a 3% trailing stop on 50% of the remaining position.
* At 100% profit (position worth 2 SOL): activate a 5% trailing stop on the remaining position.

## Liquidity Guard

When `liquidity_guard` is enabled, the stream checks pool liquidity before generating an exit signal. If the pool cannot absorb the sell at a reasonable slippage, the signal is deferred until liquidity improves or a timeout forces the exit.

This is useful for large positions in thin pools where an immediate sell would result in excessive slippage.

```json theme={null}
{
  "target_profit_pct": 50,
  "stop_loss_pct": 10,
  "liquidity_guard": true
}
```

## Breakeven Trail

The `breakeven_trail_pct` field enables a trailing stop that activates once a position breaks even (profit >= 0), trailing from the breakeven point rather than the peak.

Unlike the standard `trailing_stop_pct` which trails from the highest observed profit, the breakeven trail protects you from giving back all gains on a position that briefly went profitable.

### Example

With `breakeven_trail_pct: 2` and an entry of 1 SOL:

| Time | Profit (%) | Breakeven Trail Active | Trigger                                   |
| ---- | ---------- | ---------------------- | ----------------------------------------- |
| t=0  | -5         | No (not at breakeven)  | —                                         |
| t=1  | 0          | Yes (at breakeven)     | —                                         |
| t=2  | 3          | Yes                    | No trigger (profit > 0)                   |
| t=3  | -1         | Yes                    | No trigger (drop \< 2% from breakeven)    |
| t=4  | -2.5       | Yes                    | **Triggered** (drop >= 2% from breakeven) |

Combine with a standard `trailing_stop_pct` to protect both the breakeven point and the profit peak.

## Trailing Stop Explained

The trailing stop tracks the highest profit observed since the position opened. When profit drops from that peak by `trailing_stop_pct`, an exit signal fires.

### Numerical Example

| Time | Profit (%) | Peak (%) | Drop from Peak (%) | Trailing Stop (5%)         |
| ---- | ---------- | -------- | ------------------ | -------------------------- |
| t=0  | 0          | 0        | 0                  | Not active                 |
| t=1  | 3          | 3        | 0                  | Not active                 |
| t=2  | 8          | 8        | 0                  | Active, no trigger         |
| t=3  | 6          | 8        | 2                  | Active, no trigger         |
| t=4  | 4          | 8        | 4                  | Active, no trigger         |
| t=5  | 2.5        | 8        | 5.5                | **Triggered** (drop >= 5%) |

The trailing stop only activates once the position is in profit. If the position goes negative before ever being in profit, the stop loss triggers instead.

## Updating Strategy Mid Session

You can change the strategy at any time without reconnecting by sending an `update_strategy` message:

```json theme={null}
{
  "type": "update_strategy",
  "strategy": {
    "target_profit_pct": 15,
    "stop_loss_pct": 3,
    "trailing_stop_pct": 7
  }
}
```

Using the SDK:

<CodeGroup>
  ```typescript TypeScript theme={null}
  session.updateStrategy({
    target_profit_pct: 15,
    stop_loss_pct: 3,
    trailing_stop_pct: 7,
  });
  ```

  ```python Python theme={null}
  session.sender().update_strategy({
      "target_profit_pct": 15.0,
      "stop_loss_pct": 3.0,
      "trailing_stop_pct": 7.0,
  })
  ```

  ```rust Rust theme={null}
  session.update_strategy(StrategyConfigMsg {
      target_profit_pct: 15.0,
      stop_loss_pct: 3.0,
      trailing_stop_pct: Some(7.0),
      sell_on_graduation: None,
  }, None);
  ```

  ```go Go theme={null}
  session.Sender().UpdateStrategy(stream.StrategyConfigMsg{
      TargetProfitPct: 15.0,
      StopLossPct:     3.0,
      TrailingStopPct: 7.0,
  })
  ```
</CodeGroup>

The new strategy takes effect immediately for all tracked positions. No positions are closed or reopened; the server simply re evaluates existing positions against the new thresholds.

## Deadline Timeout

The `deadline_timeout_sec` is set on the `configure` message (not on the strategy object itself). It defines the maximum time in seconds to hold a position before requesting an exit signal, regardless of profit or loss.

Setting `deadline_timeout_sec: 60` means the Exit Intelligence Stream will attempt to exit any position held for more than 60 seconds.

When using `StreamSession`, the deadline timer runs client side and automatically calls `requestExitSignal` when it fires. If you are using `StreamClient` directly, you must implement deadline logic yourself.

## Validation Rules

* `target_profit_pct` must be >= 0.
* `stop_loss_pct` must be >= 0.
* `trailing_stop_pct` (if provided) must be >= 0.
* `deadline_timeout_sec` must be >= 0.
* At least one of these values must be > 0.

Violating these rules raises a `StreamClientError` with kind `"protocol"`.

## Per-Position Strategy Override

You can override the global strategy for individual positions without affecting other tracked positions. This is useful when you want different exit rules for specific tokens.

```json theme={null}
{
  "type": "update_position_strategy",
  "position_id": 1,
  "strategy": {
    "target_profit_pct": 200,
    "stop_loss_pct": 5,
    "trailing_stop_pct": 10
  }
}
```

Using the SDK:

<CodeGroup>
  ```typescript TypeScript theme={null}
  session.sender().updatePositionStrategy(positionId, {
    target_profit_pct: 200,
    stop_loss_pct: 5,
    trailing_stop_pct: 10,
  });
  ```

  ```python Python theme={null}
  session.sender().update_position_strategy(position_id, {
      "target_profit_pct": 200.0,
      "stop_loss_pct": 5.0,
      "trailing_stop_pct": 10.0,
  })
  ```

  ```rust Rust theme={null}
  session.sender().update_position_strategy(position_id, StrategyConfigMsg {
      target_profit_pct: 200.0,
      stop_loss_pct: 5.0,
      trailing_stop_pct: 10.0,
      ..Default::default()
  })?;
  ```

  ```go Go theme={null}
  sender.UpdatePositionStrategy(positionID, stream.StrategyConfigMsg{
      TargetProfitPct: 200.0,
      StopLossPct:     5.0,
      TrailingStopPct: 10.0,
  })
  ```
</CodeGroup>

The override applies only to the specified position. All other positions continue using the global strategy. To revert a position back to the global strategy, send another `update_position_strategy` with the current global strategy values.

Per-position overrides are ephemeral and are not persisted across reconnections.
