# How to Calculate Profit and Loss for Long/Short Positions in AI-Trader

> Learn how to calculate profit and loss for long and short positions in AI-Trader. Understand P&L aggregation for open positions and cash balance adjustments.

- Repository: [✨Data Intelligence Lab@HKU✨/AI-Trader](https://github.com/HKUDS/AI-Trader)
- Tags: how-to-guide
- Published: 2026-05-09

---

**AI-Trader calculates P&L by aggregating the market value of open positions—using `current_price × quantity` for longs and `(2 × entry_price − current_price) × |quantity|` for shorts—then adding cash balance and subtracting the total invested capital (initial $100,000 plus any deposits) in the periodic background task `record_profit_history`.**

AI-Trader is an open-source algorithmic trading framework that tracks agent performance through deterministic profit and loss calculations. To calculate profit and loss for long/short positions in AI-Trader, the system combines SQL aggregation logic with cash balance tracking, evaluating both realized and unrealized gains every 15 minutes or at the interval defined by `PROFIT_HISTORY_RECORD_INTERVAL`.

## The Core P&L Formula

The profit calculation follows a straightforward equity-based formula implemented in [`service/server/tasks.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/tasks.py) (lines 70-74):

```python
total_value = cash + position_value
profit = total_value - (INITIAL_CAPITAL + deposited)

```

**Key components:**
- **Cash**: The agent's current liquid cash balance
- **Position value**: Aggregated market value of all open long and short positions
- **INITIAL_CAPITAL**: Fixed at `$100,000` (defined in [`routes_trading.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_trading.py))
- **Deposited**: Additional capital injected beyond the initial amount

## How Position Values Are Calculated

The system uses a SQL `CASE` statement (lines 35-51 in [`tasks.py`](https://github.com/HKUDS/AI-Trader/blob/main/tasks.py)) to handle the asymmetric payoff structure of long versus short positions:

```sql
CASE
    WHEN p.current_price IS NULL THEN p.entry_price * ABS(p.quantity)
    WHEN p.side = 'long' THEN p.current_price * ABS(p.quantity)
    ELSE (2 * p.entry_price - p.current_price) * ABS(p.quantity)  -- short side
END

```

### Long Position Valuation

For **long** positions, the valuation is intuitive:

- **Formula**: `current_price × |quantity|`
- **Logic**: The position value increases linearly as the market price rises above the entry price

### Short Position Valuation

For **short** positions, AI-Trader implements the classic short-sell payoff:

- **Formula**: `(2 × entry_price − current_price) × |quantity|`
- **Logic**: This reflects the inverse relationship where profit accrues when the price falls below entry. If you short at `$100` and the price drops to `$90`, the position value becomes `(200 − 90) × 1 = $110`, capturing the `$10` profit while maintaining the initial `$100` value.

## The Background Calculation Process

The `record_profit_history` task in [`service/server/tasks.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/tasks.py) orchestrates the P&L computation:

1. **Aggregate position values**: Joins the `agents` and `positions` tables, applying the SQL `CASE` logic to sum current market values
2. **Calculate total equity**: Adds cash balance to the aggregated position value
3. **Determine profit**: Subtracts the cost basis (`INITIAL_CAPITAL + deposited`) from total equity
4. **Clamp extremes**: Applies `clamp_profit_for_display` (from [`routes_shared.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_shared.py)) to bound anomalous values before storage
5. **Persist results**: Inserts the calculated profit into the `profit_history` table with a timestamp

This process captures **unrealized P&L** from open positions combined with realized gains from closed trades and cash holdings.

## Position Management and Side Tracking

Before profit calculation can occur, positions must be created with correct directional metadata. The `_update_position_from_signal` function in [`service/server/services.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/services.py) handles four actions: `buy`, `sell`, `short`, and `cover`.

**Critical implementation details:**
- **Long positions**: Stored with `side='long'` and positive `quantity` values
- **Short positions**: Stored with `side='short'` and negative `quantity` values

The sign convention ensures the SQL aggregation logic correctly distinguishes between long and short payoff structures when calculating total position value.

## Retrieving Profit History via API

Once calculated, profit data is exposed through the REST API endpoint `/api/profit/history` defined in [`service/server/routes_trading.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/routes_trading.py). The endpoint returns both absolute profit values and percentages.

**Percentage calculation** (lines 34-38 in [`routes_trading.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_trading.py)):

```python
profit_percent = (profit / (INITIAL_CAPITAL + deposited)) * 100

```

The raw profit value is first processed through `clamp_profit_for_display` in [`routes_shared.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_shared.py) to prevent extreme outliers from distorting the display values, ensuring the API returns sensible, bounded percentages.

## Practical Implementation Examples

### Opening a Long Position

```python
from service.server.services import _update_position_from_signal

# Buy 0.5 BTC at $25,000

_update_position_from_signal(
    agent_id=42,
    symbol="BTC",
    market="crypto",
    action="buy",
    quantity=0.5,
    price=25000.0,
    executed_at="2026-05-09T12:00:00Z"
)

```

### Opening a Short Position

```python

# Short 2 ETH at $1,800

_update_position_from_signal(
    agent_id=42,
    symbol="ETH",
    market="crypto",
    action="short",
    quantity=2.0,
    price=1800.0,
    executed_at="2026-05-09T12:01:00Z"
)

```

### Querying Profit via REST API

```bash
curl "https://<host>/api/profit/history?limit=5&days=30"

```

**Sample response:**

```json
{
  "agents": [
    {
      "agent_id": 42,
      "name": "Trader42",
      "profit": 12034.5,
      "profit_percent": 12.03,
      "recorded_at": "2026-05-09T12:30:00Z"
    }
  ]
}

```

### Manual Profit Calculation

To replicate the SQL logic in Python:

```python
def calculate_profit(cash, deposited, positions, initial_capital=100000.0):
    """
    positions: list of dicts with keys:
    side: 'long' or 'short'
    quantity: float (absolute value)
    entry_price: float
    current_price: float or None
    """
    position_value = 0.0
    for p in positions:
        if p["current_price"] is None:
            value = p["entry_price"] * abs(p["quantity"])
        elif p["side"] == "long":
            value = p["current_price"] * abs(p["quantity"])
        else:  # short

            value = (2 * p["entry_price"] - p["current_price"]) * abs(p["quantity"])
        position_value += value
    
    total = cash + position_value
    profit = total - (initial_capital + deposited)
    return profit

```

## Summary

- **Long positions** are valued at `current_price × quantity`, while **short positions** use `(2 × entry_price − current_price) × quantity` to capture inverse payoff
- Profit equals `(cash + position_value) − (INITIAL_CAPITAL + deposited)`, calculated in [`service/server/tasks.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/tasks.py)
- The `record_profit_history` background task runs every ~15 minutes (configurable via `PROFIT_HISTORY_RECORD_INTERVAL`)
- Position signs are critical: positive quantities indicate longs, negative quantities indicate shorts (managed in [`services.py`](https://github.com/HKUDS/AI-Trader/blob/main/services.py))
- Historical data is stored in the `profit_history` table and served via `/api/profit/history` with percentage formatting and clamping

## Frequently Asked Questions

### How does AI-Trader calculate P&L for short selling differently from long positions?

AI-Trader uses an asymmetric valuation formula where short positions are calculated as `(2 × entry_price − current_price) × |quantity|`. This mirrors the short-sell payoff structure: when the current price drops below the entry price, the position value increases, reflecting profit. In contrast, long positions simply multiply current price by quantity. The `side` column in the `positions` table and the sign of the `quantity` field determine which branch of the SQL `CASE` statement applies.

### What is the default initial capital for AI-Trader agents?

According to the source code in [`service/server/routes_trading.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/routes_trading.py), the `INITIAL_CAPITAL` is fixed at **$100,000** per agent. This base amount is combined with any additional `deposited` funds to establish the cost basis against which profit is calculated. The denominator for percentage returns is always this total invested capital (`INITIAL_CAPITAL + deposited`).

### How frequently does AI-Trader update profit calculations?

The system updates profit history every **15 minutes** by default, though this interval is configurable through the `PROFIT_HISTORY_RECORD_INTERVAL` environment variable or setting. The `record_profit_history` task in [`service/server/tasks.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/tasks.py) performs this calculation asynchronously, aggregating current positions, updating market prices, and writing results to the `profit_history` table.

### Where does AI-Trader store historical profit and loss data?

Calculated profit values are persisted in the **`profit_history`** table (defined in the database schema), which stores the agent ID, profit amount, timestamp, and associated metadata. The REST endpoint `/api/profit/history` queries this table to provide time-series data for performance tracking, applying `clamp_profit_for_display` from [`routes_shared.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_shared.py) to ensure values remain within displayable bounds.