> For the complete documentation index, see [llms.txt](https://docs.flash.trade/flash-trade/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md).

# Trader Interactions

{% hint style="warning" %}

## Note

Flash v2 trading is **basket-backed**: your positions and orders live inside a per-wallet **basket**, funded from a **deposit ledger**. Complete the one-time setup below before you trade.
{% endhint %}

Set up `flashClient`, `poolConfig`, and `walletKeypair` as shown on the [Flash SDK v2 landing page](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md#setting-up-the-client). Every snippet on this page assumes all three are in scope. Trading instructions are sent with `sendAndConfirmErTransaction`, signed by your `walletKeypair` (or a [session key](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md#session-keys-optional-for-uis) if you've set one up for a UI).

### One-time setup

Run these once per wallet. The init/activate steps are idempotent — if the account already exists the SDK returns an empty instruction set, so they're safe to re-run; `depositDirect` always transfers funds.

```ts
import { BN } from '@coral-xyz/anchor'

const usdcMint = poolConfig.getTokenFromSymbol('USDC').mintKey

// 1) Deposit ledger (tracks your collateral).
await flashClient.sendAndConfirmTransaction(
  (await flashClient.initializeUserDepositLedger()).instructions,
)

// 2) Basket (holds your positions + orders).
await flashClient.sendAndConfirmTransaction(
  (await flashClient.initializeBasket()).instructions,
)

// 3) Trade vault for each collateral mint (once per mint, globally).
await flashClient.sendAndConfirmTransaction(
  (await flashClient.initTradeVault(usdcMint)).instructions,
)

// 4) Fund the deposit ledger (amount in base units).
await flashClient.sendAndConfirmTransaction(
  (await flashClient.depositDirect(usdcMint, new BN(25_000_000))).instructions, // 25 USDC
)

// 5) Activate the basket for trading.
await flashClient.sendAndConfirmTransaction(
  (await flashClient.delegateBasket(flashClient.wallet)).instructions,
)
```

{% hint style="info" %}
`depositDirect(tokenMint, amount, tokenProgramId?, depositor?)` wraps native SOL (`NATIVE_MINT`) automatically and does idempotent ATA creation for SPL tokens. Pass `TOKEN_2022_PROGRAM_ID` as the third arg for Token-2022 mints.
{% endhint %}

### Resolving the market

A position is scoped to a **market** — identified by a target asset, a collateral token, and a side. For a given target + side, the collateral is fixed by the market, so you look it up rather than choosing it:

```ts
import { Side, isVariant } from '@flash_trade/flash-sdk-v2'

const getMarket = (targetSymbol: string, side: Side) => {
  const targetCustody = poolConfig.custodies.find(
    (c) => c.mintKey.equals(poolConfig.getTokenFromSymbol(targetSymbol).mintKey),
  )!
  const market = poolConfig.markets.find(
    (m) =>
      m.targetCustody.equals(targetCustody.custodyAccount) &&
      isVariant(m.side, 'long') === isVariant(side, 'long'),
  )
  if (!market) throw new Error(`no ${isVariant(side, 'long') ? 'long' : 'short'} market for ${targetSymbol}`)
  const collateral = poolConfig.custodies.find((c) => c.custodyAccount.equals(market.collateralCustody))!
  return { market: market.marketAccount, side, collateralSymbol: collateral.symbol }
}
```

### Prices & slippage

Read the current oracle price and convert it to a slippage-bounded `ContractOraclePrice` with `getPriceAfterSlippage`:

```ts
import { BN } from '@coral-xyz/anchor'
import { Side, type ContractOraclePrice } from '@flash_trade/flash-sdk-v2'

const getEntryPrice = async (
  targetSymbol: string,
  side: Side,
  isEntry: boolean,
  slippageBps = new BN(100), // 1%
): Promise<ContractOraclePrice> => {
  const custody = poolConfig.custodies.find(
    (c) => c.mintKey.equals(poolConfig.getTokenFromSymbol(targetSymbol).mintKey),
  )!
  const program = flashClient.erProgram ?? flashClient.program
  const oracle = (await program.account.customOracle.fetch(custody.intOracleAccount)) as {
    price: BN
    expo: number
  }
  return flashClient.getPriceAfterSlippage(
    isEntry,
    slippageBps,
    { price: oracle.price, exponent: new BN(oracle.expo) },
    side,
  )
}
```

### Privilege accounts (Referral & Stake)

Position operations (`openPosition`, `closePosition`, `increasePositionSize`, `decreasePositionSize`) accept a trailing `(privilege, referralAccount, tokenStakeAccount)` triplet that determines your fee tier.

| `Privilege`                | When                             | Pass                                                                      |
| -------------------------- | -------------------------------- | ------------------------------------------------------------------------- |
| `Privilege.None` (default) | no FAF stake, no referral        | nothing — omit both accounts, trade at full fee                           |
| `Privilege.Stake`          | trader holds an active FAF stake | trader's own `tokenStakeAccount`                                          |
| `Privilege.Referral`       | trader was referred              | trader's own `referralAccount` **and** the referrer's `tokenStakeAccount` |

```ts
import { Privilege, findTokenStakeAddress, findReferralAddress } from '@flash_trade/flash-sdk-v2'

const [tokenStakeAccount] = findTokenStakeAddress(flashClient.wallet, flashClient.programId)
const [referralAccount]   = findReferralAddress(flashClient.wallet, flashClient.programId)
```

A missing or invalid privilege account never reverts the trade — the program just charges full fee. If you don't care about discounts, omit the trailing args entirely (they default to `Privilege.None`).

### Open a position

The 2nd and 3rd args are the market's collateral symbol — you pay collateral in the market's own token.

```ts
const collateralAmount = new BN(5_000_000)  // collateral, in the market's collateral-token base units
const leverage         = new BN(20_000)     // 2× (BPS_DECIMALS = 4)

const openPosition = async (targetSymbol: string, side: Side) => {
  const { market, collateralSymbol } = getMarket(targetSymbol, side)
  const price = await getEntryPrice(targetSymbol, side, true)

  // sizeAmount is in TARGET-token base units (not USD). Derive it from a quote at
  // your target leverage so you don't trip MinLeverage by guessing a raw size.
  const { sizeAmount } = await flashClient.views.getOpenPositionQuoteEr(poolConfig, {
    market, targetSymbol, collateralSymbol, receivingSymbol: collateralSymbol,
    amountIn: collateralAmount, leverage,
  })

  const { instructions } = await flashClient.openPosition(
    targetSymbol,
    collateralSymbol,  // lock symbol
    collateralSymbol,  // collateral symbol
    side,
    poolConfig,
    price,
    collateralAmount,
    sizeAmount,
    // optional: privilege, referralAccount, tokenStakeAccount
  )

  const sig = await flashClient.sendAndConfirmErTransaction(instructions, [walletKeypair])
  console.log('opened:', sig)
}

await openPosition('SOL', Side.Long)
```

{% hint style="info" %}
`openPosition` only opens a **new** position. To add to an existing position in the same market, use `increasePositionSize`.
{% endhint %}

### Close a position

Pass an optional `receivingSymbol` (6th arg) to receive proceeds as a different token; omit it to receive the market's collateral.

```ts
const closePosition = async (targetSymbol: string, side: Side) => {
  const { market, collateralSymbol } = getMarket(targetSymbol, side)
  const price = await getEntryPrice(targetSymbol, side, false) // exit price

  const { instructions } = await flashClient.closePosition(
    targetSymbol, collateralSymbol, side, poolConfig, price,
    // receivingSymbol?, privilege?, referralAccount?, tokenStakeAccount?
  )

  // If the position has trigger (TP/SL) orders, cancel them in the same tx:
  const { instructions: cancelIxs } = await flashClient.cancelAllTriggerOrders(market)

  const sig = await flashClient.sendAndConfirmErTransaction([...instructions, ...cancelIxs], [walletKeypair])
  console.log('closed:', sig)
}
```

### Increase / decrease position size

```ts
const { collateralSymbol } = getMarket('SOL', Side.Long)

// Increase: add size (and collateral — must be non-zero).
const inc = await flashClient.increasePositionSize(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  await getEntryPrice('SOL', Side.Long, true),
  new BN(20_000_000), // sizeDelta
  new BN(2_000_000),  // collateralAmount to add
  // receivingSymbol?, privilege?, referralAccount?, tokenStakeAccount?
)
await flashClient.sendAndConfirmErTransaction(inc.instructions, [walletKeypair])

// Decrease: partially close.
const dec = await flashClient.decreasePositionSize(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  await getEntryPrice('SOL', Side.Long, false),
  new BN(20_000_000), // sizeDelta to remove
  // dispensingSymbol?, privilege?, referralAccount?, tokenStakeAccount?
)
await flashClient.sendAndConfirmErTransaction(dec.instructions, [walletKeypair])
```

### Add / remove collateral

```ts
const { collateralSymbol } = getMarket('SOL', Side.Long)

// Add collateral (amount in the pay token's base units).
const add = await flashClient.addCollateral(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  new BN(3_000_000), // collateralDelta
  // receivingSymbol? — defaults to the market's collateral
)
await flashClient.sendAndConfirmErTransaction(add.instructions, [walletKeypair])

// Remove collateral (amount in USD, 6 decimals).
const remove = await flashClient.removeCollateral(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  new BN(2_000_000), // collateralDeltaUsd = $2.00
  // dispensingSymbol? — defaults to the market's collateral
)
await flashClient.sendAndConfirmErTransaction(remove.instructions, [walletKeypair])
```

{% hint style="warning" %}
`removeCollateral` takes a **USD** amount (6 decimals); `addCollateral` takes an amount in the pay token's base units.
{% endhint %}

### Limit orders

A limit order carries its own take-profit and stop-loss inline. Build the three prices as `ContractOraclePrice` from the live oracle exponent.

```ts
const { collateralSymbol } = getMarket('SOL', Side.Long)

const { instructions } = await flashClient.placeLimitOrder(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  limitPrice,        // ContractOraclePrice — trigger
  new BN(5_000_000), // reserveAmount (collateral reserved for the fill)
  new BN(50_000_000),// sizeAmount
  stopLossPrice,     // ContractOraclePrice (use { price: BN_ZERO, exponent } to skip)
  takeProfitPrice,   // ContractOraclePrice
  // receivingSymbol?
)
await flashClient.sendAndConfirmErTransaction(instructions, [walletKeypair])
```

Edit and cancel by `orderId` (the order's index in your basket):

```ts
const edit = await flashClient.editLimitOrder(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  orderId, limitPrice, sizeAmount, stopLossPrice, takeProfitPrice, /* receivingSymbol? */
)
await flashClient.sendAndConfirmErTransaction(edit.instructions, [walletKeypair])

const cancel = await flashClient.cancelLimitOrder('SOL', collateralSymbol, Side.Long, poolConfig, orderId)
await flashClient.sendAndConfirmErTransaction(cancel.instructions, [walletKeypair])
```

### Trigger orders (Take Profit / Stop Loss)

A trigger order attaches a TP or SL to an open position. `isStopLoss` selects which: `true` = stop-loss, `false` = take-profit. `deltaSizeAmount` is how much of the position to close when it fires.

```ts
const { market, collateralSymbol } = getMarket('SOL', Side.Long)

const tp = await flashClient.placeTriggerOrder(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  takeProfitPrice, positionSizeAmount, false, // isStopLoss = false → take-profit
  // receiveSymbol?
)
const sl = await flashClient.placeTriggerOrder(
  'SOL', collateralSymbol, Side.Long, poolConfig,
  stopLossPrice, positionSizeAmount, true,    // isStopLoss = true → stop-loss
)
await flashClient.sendAndConfirmErTransaction([...tp.instructions, ...sl.instructions], [walletKeypair])

// Edit / cancel
await flashClient.editTriggerOrder('SOL', collateralSymbol, Side.Long, poolConfig, orderId, newPrice, deltaSize, isStopLoss)
await flashClient.cancelTriggerOrder(market, orderId, isStopLoss) // takes the market PublicKey
await flashClient.cancelAllTriggerOrders(market)
```

{% hint style="info" %}
**TP/SL price rules:**

* **Stop Loss** — above liq price & below current price for LONG; below liq price & above current price for SHORT.
* **Take Profit** — above current price for LONG; below current price for SHORT.
* **Virtual tokens** — take profit must be below the max-profit price for LONG.
  {% endhint %}

### Quotes (views)

Quote before you trade. The `*Er` variants read the basket-held position (pass `owner` for position-bound quotes). All take the `poolConfig` and an arg object.

```ts
// collateralSymbol must be the market's collateral token (from getMarket), not a
// hardcoded 'USDC' — the wrong symbol makes the market PDA mismatch (ConstraintSeeds).
const { market, collateralSymbol } = getMarket('SOL', Side.Long)
const owner = flashClient.wallet

const openQuote = await flashClient.views.getOpenPositionQuoteEr(poolConfig, {
  market, targetSymbol: 'SOL', collateralSymbol, receivingSymbol: collateralSymbol,
  amountIn: new BN(5_000_000), leverage: new BN(20000), // 2x, BPS_DECIMALS = 4
})

const closeQuote = await flashClient.views.getClosePositionQuoteEr(poolConfig, {
  owner, market, targetSymbol: 'SOL', collateralSymbol,
  dispensingSymbol: collateralSymbol, sizeDeltaUsd: new BN(10_000_000),
})

const pnl  = await flashClient.views.getPnlEr(poolConfig, { owner, market, targetSymbol: 'SOL', collateralSymbol })
const data = await flashClient.views.getPositionDataEr(poolConfig, { owner, market, targetSymbol: 'SOL', collateralSymbol })
const liq  = await flashClient.views.getLiquidationPriceEr(poolConfig, { owner, market, targetSymbol: 'SOL', collateralSymbol })

// Add/remove-collateral quotes, entry/exit price & fee, liquidation state — same shape.
```

{% hint style="info" %}
Views are read-only simulations (no signing, no transaction). Poll `getPositionDataEr` for a live PnL / leverage / liquidation-price ticker.
{% endhint %}

### Reading positions & orders

Your open positions and orders are embedded in the **basket**:

```ts
const basket = await flashClient.erAccounts!.fetchBasket(flashClient.wallet)

basket.positions // PositionMeta[] — size, collateral, entry, market, …
basket.orders    // OrderMeta[]   — limit + trigger orders

basket.getPosition(marketPk)     // the position for a market, if any
basket.hasOpenPosition(marketPk) // boolean
basket.getOpenPositionCount()
```

To stream updates, subscribe to the basket PDA with `onAccountChange` — see [Reading accounts](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md#reading-accounts) on the landing page.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
