# Managing a Position

Adjust an open position without closing it: change leverage by moving margin, or move the
take-profit and stop-loss.

## Add margin

Deposit more collateral into an open position, lowering its leverage and moving the liquidation
price away.

```solidity
function addMargin(bytes32 tradeHash, address tokenIn, uint96 amount) external payable;
```

No oracle data required.

```ts
import { parseUnits } from 'viem'
import { publicClient, walletClient, DIAMOND, USDC } from './config'

const ADD_MARGIN_ABI = [{
  type: 'function',
  name: 'addMargin',
  stateMutability: 'payable',
  inputs: [
    { name: 'tradeHash', type: 'bytes32' },
    { name: 'tokenIn', type: 'address' },
    { name: 'amount', type: 'uint96' },
  ],
  outputs: [],
}] as const

const hash = await walletClient.writeContract({
  address: DIAMOND,
  abi: ADD_MARGIN_ABI,
  functionName: 'addMargin',
  args: [positionHash, USDC, parseUnits('5', 6)],
})
await publicClient.waitForTransactionReceipt({ hash })
```

`tokenIn` must be compatible with the position's `lvToken` — you cannot add MON to a position
settling in LVUSD. For native MON, pass the zero address and send the amount as `value`:

```ts
await walletClient.writeContract({
  address: DIAMOND,
  abi: ADD_MARGIN_ABI,
  functionName: 'addMargin',
  args: [positionHash, '0x0000000000000000000000000000000000000000', amount],
  value: amount,
})
```

## Remove margin

Withdraw collateral from an open position, raising its leverage.

```solidity
function removeMargin(
    bytes32 tradeHash,
    uint96 lvAmount,
    OracleUpdateData calldata updateData
) external payable;
```

Unlike `addMargin`, this **requires oracle data** — the protocol must value the position before
letting collateral out. `msg.value` must cover the oracle update fee.

Note that `lvAmount` is denominated in the position's **`lvToken`** (18 decimals for both LVUSD and
LVMON), not in `tokenIn`. A position funded with USDC still reports and withdraws margin at 18
decimals.

```ts
import { parseUnits } from 'viem'
import { fetchOracleUpdate } from './oracle'

const REMOVE_MARGIN_ABI = [{
  type: 'function',
  name: 'removeMargin',
  stateMutability: 'payable',
  inputs: [
    { name: 'tradeHash', type: 'bytes32' },
    { name: 'lvAmount', type: 'uint96' },
    {
      name: 'updateData', type: 'tuple', components: [
        { name: 'pythPriceUpdateData', type: 'bytes[]' },
        { name: 'pythProPriceUpdateData', type: 'bytes[]' },
      ],
    },
  ],
  outputs: [],
}] as const

const oracle = await fetchOracleUpdate(pairBase, USDC)

await walletClient.writeContract({
  address: DIAMOND,
  abi: REMOVE_MARGIN_ABI,
  functionName: 'removeMargin',
  args: [positionHash, parseUnits('2', 18), oracle.updateData],   // 2 LVUSD
  value: oracle.value,
})
```

The protocol rejects a removal that would leave the position in a bad state:

| Error | Meaning |
| :--- | :--- |
| `ExceedMaxLeverageAfterMarginRemoval` | Resulting leverage above the pair's maximum |
| `LiquidatableAfterMarginRemoval` | Position would be immediately liquidatable |
| `ExceedMaxTakeProfitAfterMarginRemoval` | Existing take-profit would exceed its allowed bound at the new leverage |
| `InsufficientMarginAmount` | `lvAmount` exceeds free margin |

Both operations emit `UpdateMargin(user, tradeHash, beforeMargin, margin)`.

## Take-profit and stop-loss

```solidity
function updateTradeTp(bytes32 tradeHash, uint128 takeProfit) external;
function updateTradeSl(bytes32 tradeHash, uint128 stopLoss) external;
function updateTradeTpAndSl(bytes32 tradeHash, uint128 takeProfit, uint128 stopLoss) external;
```

Prices are 1e18. Passing `0` for `stopLoss` removes it. No oracle data required — these only write
stored trigger values.

```ts
const UPDATE_TPSL_ABI = [{
  type: 'function',
  name: 'updateTradeTpAndSl',
  stateMutability: 'nonpayable',
  inputs: [
    { name: 'tradeHash', type: 'bytes32' },
    { name: 'takeProfit', type: 'uint128' },
    { name: 'stopLoss', type: 'uint128' },
  ],
  outputs: [],
}] as const

await walletClient.writeContract({
  address: DIAMOND,
  abi: UPDATE_TPSL_ABI,
  functionName: 'updateTradeTpAndSl',
  args: [positionHash, parseUnits('120000', 18), parseUnits('90000', 18)],
})
```

Take-profit is bounded by the position's leverage — an unreachably high target is clamped or
rejected. Stop-loss must be on the loss side of the entry price and is validated on write
(`InvalidStopLoss`).

Emits `UpdateTradeTp` and `UpdateTradeSl` with before/after values.

### When to use TP/SL orders instead

These functions set a **single** take-profit and a **single** stop-loss on the position itself. If
you need partial take-profits — scaling out at several price levels — use
[TP/SL orders](/onchain/tpsl-orders), which support multiple legs per position.

The two mechanisms coexist. Legacy positions support only the embedded fields.
