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

# Stream de PNL da Calculadora

> SSE que emite o PNL atualizado da posição aberta em tempo real, recalculado a cada segundo com dados de mercado frescos

## 📖 Descrição

Endpoint de **Server-Sent Events (SSE)** que transmite o **PNL (Profit and Loss) em tempo real** de uma posição de arbitragem spot-futuro aberta na Calculadora. A cada segundo, o servidor busca os preços de mercado atuais (spot + futuro), recalcula o spread e o PNL com base nos valores de entrada registrados, e emite o resultado.

<Callout title="Substitui o polling" icon="zap" color="green">
  Este endpoint substitui o mecanismo de polling manual do frontend (chamadas `POST /v1/calculators/process` a cada 5 segundos). Com SSE, a atualização é server-driven e mais eficiente — sem overfetching.
</Callout>

***

## 🛠️ Requisição

### Método

`GET`

### URL

```plaintext theme={null}
/v1/calculators/stream
```

### Query Parameters

| Parâmetro  | Tipo   | Obrigatório | Descrição                           |
| ---------- | ------ | ----------- | ----------------------------------- |
| `email`    | string | Sim         | Email do usuário dono da posição    |
| `ticker`   | string | Sim         | Par de trading (ex: `BTC-USDT`)     |
| `exchange` | string | Sim         | Exchange da posição (ex: `binance`) |

### Headers Necessários

| Header          | Valor               |
| --------------- | ------------------- |
| `Accept`        | `text/event-stream` |
| `Cache-Control` | `no-cache`          |

### Exemplo de Requisição (curl)

```bash theme={null}
curl --location --no-buffer \
  -H "Accept: text/event-stream" \
  -H "Cache-Control: no-cache" \
  'localhost:8080/v1/calculators/stream?email=user@example.com&ticker=BTC-USDT&exchange=binance'
```

### Exemplo de Requisição (JavaScript)

```javascript theme={null}
const params = new URLSearchParams({
    email: 'user@example.com',
    ticker: 'BTC-USDT',
    exchange: 'binance'
});

const source = new EventSource(`/v1/calculators/stream?${params}`);

source.onmessage = (event) => {
    const pnl = JSON.parse(event.data);
    console.log(`PNL: $${pnl.pnl.toFixed(2)} (${pnl.pnlPercentage.toFixed(2)}%) - Lucrativo: ${pnl.isProfitable}`);
};

source.onerror = () => {
    // EventSource reconecta automaticamente
};

// Ao desmontar:
// source.close();
```

***

## 📤 Resposta

### Headers da Resposta

| Header              | Valor               |
| ------------------- | ------------------- |
| `Content-Type`      | `text/event-stream` |
| `Cache-Control`     | `no-cache`          |
| `Connection`        | `keep-alive`        |
| `X-Accel-Buffering` | `no`                |

### Frequência de Emissão

Um evento é emitido a cada **1 segundo**. Internamente, os dados de mercado (spot + futuro) são buscados do MongoDB com cache de **5 segundos** para evitar sobrecarga no banco.

### Formato dos Eventos SSE

```
data: {"email":"user@example.com","ticker":"BTC-USDT","exchange":"binance","entrySpotValue":83000,"entryShortValue":83150,...,"pnl":42.50,"pnlPercentage":0.026,"isProfitable":true}

data: {"email":"user@example.com","ticker":"BTC-USDT","exchange":"binance","entrySpotValue":83000,"entryShortValue":83150,...,"pnl":51.30,"pnlPercentage":0.031,"isProfitable":true}
```

### Estrutura Completa do Payload JSON

```json theme={null}
{
  "email": "user@example.com",
  "ticker": "BTC-USDT",
  "exchange": "binance",
  "entrySpotValue": 83000.00,
  "entryShortValue": 83150.00,
  "entrySpotQty": 0.1,
  "entryFuturesQty": 0.1,
  "entrySpread": 150.00,
  "entrySpreadPercentage": 0.18,
  "currentSpotPrice": 85400.00,
  "currentFuturePrice": 85450.00,
  "currentSpread": 50.00,
  "currentSpreadPercentage": 0.058,
  "fundingRate": 0.0001,
  "pnl": 42.50,
  "pnlPercentage": 0.026,
  "isProfitable": true
}
```

### Campos do Payload

| Campo                     | Tipo    | Descrição                                       |
| ------------------------- | ------- | ----------------------------------------------- |
| `email`                   | string  | Email do usuário                                |
| `ticker`                  | string  | Par de trading                                  |
| `exchange`                | string  | Exchange da posição                             |
| `entrySpotValue`          | float   | Preço spot de entrada (registrado na posição)   |
| `entryShortValue`         | float   | Preço futuro de entrada (registrado na posição) |
| `entrySpotQty`            | float   | Quantidade spot de entrada                      |
| `entryFuturesQty`         | float   | Quantidade futuros de entrada                   |
| `entrySpread`             | float   | Spread no momento da abertura (futuro − spot)   |
| `entrySpreadPercentage`   | float   | Spread de entrada em %                          |
| `currentSpotPrice`        | float   | Preço spot atual                                |
| `currentFuturePrice`      | float   | Preço futuro atual                              |
| `currentSpread`           | float   | Spread atual (futuro − spot)                    |
| `currentSpreadPercentage` | float   | Spread atual em %                               |
| `fundingRate`             | float   | Taxa de funding atual                           |
| `pnl`                     | float   | Lucro/prejuízo total em USDT                    |
| `pnlPercentage`           | float   | Lucro/prejuízo em % sobre o capital empregado   |
| `isProfitable`            | boolean | `true` se `pnl > 0`                             |

### Como o PNL é Calculado

```
spotValueChange    = (currentSpotPrice − entrySpotValue) × entrySpotQty
futureValueChange  = (entryShortValue − currentFuturePrice) × entryFuturesQty
PNL                = spotValueChange + futureValueChange

entryTotalValue    = (entrySpotValue × entrySpotQty) + (entryShortValue × entryFuturesQty)
PNLPercentage      = (PNL / entryTotalValue) × 100
```

***

## 📝 Códigos de Resposta

<Callout type="success">
  **200 OK** + `Content-Type: text/event-stream`: Stream aberto com sucesso.
</Callout>

<Callout type="warning">
  **400 Bad Request**: Parâmetros obrigatórios ausentes:

  * `email is required`
  * `ticker is required`
  * `exchange is required`
</Callout>

<Callout type="error">
  **404 Not Found**: `calculator not found` — Não há posição salva para o par `email + ticker` fornecido. Salve os dados de entrada via `POST /v1/calculators` primeiro.
</Callout>

***

## 💡 Integração no Frontend (hook React)

O projeto já fornece o hook `useCalculatorStream` em `src/hooks/useCalculatorStream.js`:

```jsx theme={null}
import { useCalculatorStream } from 'src/hooks/useCalculatorStream';

function CalculatorPage({ ticker }) {
    const { user } = useAuth();
    const [entryData, setEntryData] = useState(null);
    const [exitOpen, setExitOpen] = useState(true);

    // Stream só é aberto quando a posição está salva e "exitOpen" está ativo
    const streamActive = exitOpen && !!entryData && !!user?.email;
    const { result: processData, connected } = useCalculatorStream(
        user?.email,
        ticker,
        'binance',
        streamActive
    );

    return (
        <div>
            <span>{connected ? '🟢 Live' : '⚪ Aguardando'}</span>
            {processData && (
                <div style={{ color: processData.isProfitable ? 'green' : 'red' }}>
                    PNL: ${processData.pnl?.toFixed(2)} ({processData.pnlPercentage?.toFixed(2)}%)
                </div>
            )}
        </div>
    );
}
```

### Parâmetros do Hook

| Parâmetro  | Tipo    | Descrição                                                          |
| ---------- | ------- | ------------------------------------------------------------------ |
| `email`    | string  | Email do usuário                                                   |
| `ticker`   | string  | Par de trading                                                     |
| `exchange` | string  | Exchange selecionada                                               |
| `active`   | boolean | Liga/desliga o stream. `false` fecha o `EventSource` imediatamente |

### Retorno do Hook

| Propriedade | Tipo           | Descrição                                      |
| ----------- | -------------- | ---------------------------------------------- |
| `result`    | object \| null | Último payload recebido do servidor            |
| `connected` | boolean        | `true` enquanto o `EventSource` estiver aberto |

***

## ⚠️ Considerações

<Callout title="Pré-requisito: posição salva" icon="info-circle" color="blue">
  O stream retorna 404 se não houver uma entrada no banco para `email + ticker`. Sempre salve os dados de entrada via `POST /v1/calculators` antes de abrir o stream.
</Callout>

<Callout title="Cache de mercado de 5s" icon="clock" color="yellow">
  Os dados de mercado (spot + futuro) são buscados do MongoDB com cache interno de **5 segundos**. Isso significa que o PNL pode ter até 5s de defasagem em relação ao mercado real — aceitável para monitoramento de posições de arbitragem, que não requerem latência de milissegundos.
</Callout>

<Callout title="Dados de mercado do MongoDB" icon="database" color="purple">
  Diferente do stream do Looker (que usa o `RealtimePriceCache` WebSocket), o stream da Calculadora usa a coleção `operations_future` do MongoDB para obter o par de preços spot + futuro. Essa coleção é populada continuamente pelo motor de arbitragem.
</Callout>
