# PerpsHub TP/SL Implementation

### Overview

The stop-loss/take-profit (SL/TP) system allows users to automatically close positions when the market price reaches specified trigger levels. The system continuously monitors mark prices and executes closes when conditions are met.

**Base URL Pattern:** `/v1/:symmId/:multiaccount`

All endpoints require the following path parameters:

* `symmId`: Network/chain identifier (e.g., '56a', '5000a', '42161a', '8453a', etc.)
* `multiaccount`: Ethereum address of the multi-account contract

***

### Endpoints

#### 1. Create Stop Loss / Take Profit

Create or update a stop-loss and/or take-profit order for a specific position.

**Endpoint:**

* `POST /v1/:symmId/:multiaccount/stop_loss`
* `POST /v1/:symmId/:multiaccount/stop-loss` (kebab-case alternative)

**Authentication:** Required (Bearer token)

**Request Body:**

```typescript
{
  userAddress: string        // Ethereum address of the user (0x...)
  accountAddress: string     // Ethereum address of the account/partyA (0x...)
  positionSide: 0 | 1       // 0 = LONG, 1 = SHORT
  symbolId: number          // Numeric symbol identifier
  requestedPrice: string    // Requested open price (decimal string)
  quoteId: number           // Quote ID (positive for filled trades, negative for pending instant opens)
  tpPrice: string           // Take profit trigger price (decimal string, can be empty)
  slPrice: string           // Stop loss trigger price (decimal string, can be empty)
  timestamp: number         // Request timestamp in milliseconds
}
```

**Validation Rules:**

* At least one of `tpPrice` or `slPrice` must be provided (non-empty)
* `tpPrice` and `slPrice` cannot be equal
* For LONG positions:
  * `tpPrice` must be ABOVE the current/estimated mark price
  * `slPrice` must be BELOW the current/estimated mark price
* For SHORT positions:
  * `tpPrice` must be BELOW the current/estimated mark price
  * `slPrice` must be ABOVE the current/estimated mark price

**Success Response (200 OK):**

```json
{
  "success": true,
  "statusMessage": "Stoploss created",
  "data": {
    "success": true,
    "statusMessage": "Stoploss created"
  }
}
```

**Error Responses:**

**400 Bad Request:**

```json
{
  "error": "Stoploss request must have either TP or SL"
}
```

```json
{
  "error": "Stoploss request cannot have TP and SL equal"
}
```

```json
{
  "error": "Take profit price is invalid"
}
```

```json
{
  "error": "Stop loss price is invalid"
}
```

```json
{
  "error": "Stop loss already exists for this trade"
}
```

```json
{
  "error": "Unsupported symbol"
}
```

**404 Not Found:**

```json
{
  "error": "Trade not found"
}
```

**Notes:**

* If a stop-loss already exists for the trade in PENDING status, the request will be rejected
* The system validates prices against the current mark price (for opened positions) or estimated fill price (for pending instant opens)
* Prices are validated with slippage considerations

**Example:**

```bash
curl -X POST \
  https://api.example.com/v1/42161a/0x1234...5678/stop-loss \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0xabcd...ef01",
    "accountAddress": "0x1234...5678",
    "positionSide": 0,
    "symbolId": 123,
    "requestedPrice": "50000.5",
    "quoteId": 12345,
    "tpPrice": "55000",
    "slPrice": "48000",
    "timestamp": 1234567890000
  }'
```

***

#### 2. List Stop Losses / Take Profits

Retrieve stop-loss/take-profit orders for multiple quote IDs.

**Endpoint:**

* `POST /v1/:symmId/:multiaccount/list_sl`
* `POST /v1/:symmId/:multiaccount/list-sl-tp` (kebab-case alternative)

**Authentication:** Not required

**Request Body:**

```typescript
{
  quoteIds: number[]  // Array of quote IDs (minimum 1 item)
}
```

**Success Response (200 OK):**

```json
{
  "success": true,
  "statusMessage": "Stoplosses listed",
  "data": {
    "statusMessage": "Stoplosses listed",
    "success": true,
    "data": {
      "12345": {
        "symmId": "42161a",
        "quoteId": 12345,
        "userAddress": "0xabcd...ef01",
        "affiliate": "0x1234...5678",
        "partyA": "0x9876...5432",
        "tpPrice": "55000",
        "slPrice": "48000",
        "timestamp": 1234567890000,
        "status": "pending",
        "positionSide": 0,
        "lastCheckedAt": 1234567890000,
        "symbolName": "BTCUSDT"
      },
      "67890": {
        "symmId": "42161a",
        "quoteId": 67890,
        "userAddress": "0xabcd...ef01",
        "affiliate": "0x1234...5678",
        "partyA": "0x9876...5432",
        "tpPrice": null,
        "slPrice": "3200",
        "timestamp": 1234567891000,
        "status": "pending",
        "positionSide": 0,
        "lastCheckedAt": 1234567891000,
        "symbolName": "ETHUSDT"
      }
    }
  }
}
```

**Response Fields:**

* `symmId`: Network identifier
* `quoteId`: Quote/trade identifier
* `userAddress`: User's address
* `affiliate`: Affiliate/multiaccount address
* `partyA`: Account address (partyA)
* `tpPrice`: Take profit trigger price (null if not set)
* `slPrice`: Stop loss trigger price (null if not set)
* `timestamp`: Creation timestamp
* `status`: Order status (`"pending"`, `"processing"`, `"executed"`, `"cancelled"`)
* `positionSide`: 0 = LONG, 1 = SHORT
* `lastCheckedAt`: Last time the order was checked by the system
* `symbolName`: Trading symbol (e.g., "BTCUSDT")

**Error Response (400 Bad Request):**

```json
{
  "error": "Error message"
}
```

**Example:**

```bash
curl -X POST \
  https://api.example.com/v1/42161a/0x1234...5678/list-sl-tp \
  -H "Content-Type: application/json" \
  -d '{
    "quoteIds": [12345, 67890, 11111]
  }'
```

***

#### 3. Cancel Stop Loss / Take Profit

Cancel the stop-loss and/or take-profit for a specific quote.

**Endpoint:**

* `POST /v1/:symmId/:multiaccount/cancel_sl`
* `POST /v1/:symmId/:multiaccount/cancel-sl-tp` (kebab-case alternative)

**Authentication:** Required (Bearer token)

**Request Body:**

```typescript
{
  quoteId: number         // Quote ID to cancel
  cancelSl?: boolean     // Whether to cancel stop loss (optional, default: false)
  cancelTp?: boolean     // Whether to cancel take profit (optional, default: false)
}
```

**Validation Rules:**

* At least one of `cancelSl` or `cancelTp` must be `true`
* The stop-loss order must exist for the given quote
* The stop-loss order must be in `PENDING` status (cannot cancel processing/executed orders)

**Success Response (200 OK):**

When canceling both:

```json
{
  "success": true,
  "statusMessage": "Stop loss and take profit cancelled",
  "data": {
    "success": true,
    "statusMessage": "Stop loss and take profit cancelled"
  }
}
```

When canceling only stop loss:

```json
{
  "success": true,
  "statusMessage": "Stop loss cancelled",
  "data": {
    "success": true,
    "statusMessage": "Stop loss cancelled"
  }
}
```

When canceling only take profit:

```json
{
  "success": true,
  "statusMessage": "Take profit cancelled",
  "data": {
    "success": true,
    "statusMessage": "Take profit cancelled"
  }
}
```

**Error Responses:**

**400 Bad Request:**

```json
{
  "error": "Must cancel at least one of SL or TP"
}
```

```json
{
  "error": "Can only cancel pending stop loss orders"
}
```

```json
{
  "error": "Error while cancelling stop loss"
}
```

**404 Not Found:**

```json
{
  "error": "Stop loss not found for this quote"
}
```

**Notes:**

* If both `cancelSl` and `cancelTp` are true (or if canceling the only remaining one), the entire stop-loss document is removed
* If only one is canceled and the other remains, the document is updated with the remaining value
* Partial cancellation is supported: you can cancel just SL or just TP while keeping the other active

**Example:**

Cancel both SL and TP:

```bash
curl -X POST \
  https://api.example.com/v1/42161a/0x1234...5678/cancel-sl-tp \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": 12345,
    "cancelSl": true,
    "cancelTp": true
  }'
```

Cancel only stop loss:

```bash
curl -X POST \
  https://api.example.com/v1/42161a/0x1234...5678/cancel-sl-tp \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": 12345,
    "cancelSl": true,
    "cancelTp": false
  }'
```

***

#### Stop Loss States

| Status       | Description                                   |
| ------------ | --------------------------------------------- |
| `PENDING`    | Order is active and being monitored           |
| `PROCESSING` | Trigger conditions met, execution in progress |
| `EXECUTED`   | Order successfully executed                   |
| `CANCELLED`  | Order cancelled by user                       |

### Authentication

Endpoints marked as requiring authentication must include a Bearer token in the Authorization header:

```
Authorization: Bearer YOUR_ACCESS_TOKEN
```

To obtain an access token, use the login endpoints:

* `POST /v1/:symmId/:multiaccount/login` (for EVM wallets)
* `POST /v1/:symmId/:multiaccount/tac-login` (for TON wallets)

***

### Common Error Codes

| HTTP Status | Error Message                  | Description                                |
| ----------- | ------------------------------ | ------------------------------------------ |
| 400         | Invalid symmId                 | The symmId path parameter is not valid     |
| 400         | Validation error               | Request body validation failed             |
| 401         | Invalid or missing credentials | Authentication token is missing or invalid |
| 404         | Trade not found                | The specified quote ID does not exist      |
| 404         | Stop loss not found            | No stop-loss order exists for the quote    |
| 500         | Internal server error          | Unexpected server error                    |
