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

# Trading

> Placing orders and managing positions

## Overview

The SDK provides high-level methods for trading on Kalshi markets:

* `executeBuyFlow` / `executeSellFlow` - Place buy/sell orders with automatic signature generation
* `executeCancelOrderFlow` - Cancel existing orders
* `executeMintFlow` / `executeBurnFlow` - Create/destroy position tokens
* `getUserOrders` / `getUserPositions` - Query user's orders and positions

<Info>
  To avoid ambiguity, we denote the smallest possible multiple of USDC (0.000001 USDC) as one `uusdc`,
  which stands for µUSDC (micro-USDC), and denote the smallest possible multiple of a contract (0.01 contract)
  as one `ccontract` (centicontract).

  User-facing USDC balances are specified as fixed-point strings (e.g. `"1.2625"` for USDC). Contract quantities
  in the API and SDK are specified as integer `ccontracts` strings (e.g. `"1050"` for 10.50 contracts at precision 2).
</Info>

## Developer Accounts

If you have a developer account, you can set it at the client level to automatically apply it to all orders:

```typescript theme={null}
const client = createBisonClient({ 
  baseUrl: 'https://api.bison.markets',
  devAccountId: 'your-dev-account-id'
});

// All orders automatically include your dev account ID
await client.executeBuyFlow({
  walletClient,
  publicClient,
  userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  chain: 'base',
  marketId: 'KXFEDDECISION-26JAN-T425',
  side: 'yes',
  ccontracts: '1000', // 10.00 contracts * 100
  priceUusdc: '650000',
});
```

You can also override the dev account for specific orders when using the lower-level `placeOrder` method:

```typescript theme={null}
await client.placeOrder({
  chain: 'base',
  marketId: 'KXFEDDECISION-26JAN-T425',
  ccontracts: '1000', // 10.00 contracts * 100
  priceUusdc: '650000',
  action: 'buy',
  side: 'yes',
  userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  signature: '0x...',
  expiry: 1234567890,
  devAccountId: 'different-dev-account' // Overrides client default
});
```

See the [Developer Accounts](/general/dev-accounts) guide for more information on how to get a dev account and earn from trading fees.

## Placing Orders

Place limit orders on Kalshi markets. The SDK handles EIP-712 signature generation automatically.

### `executeBuyFlow`

```typescript theme={null}
async executeBuyFlow(params: {
  walletClient: WalletClient;
  publicClient: PublicClient;
  userAddress: `0x${string}`;
  chain: string;
  marketId: string;
  side: 'yes' | 'no';
  ccontracts: string;
  priceUusdc: string;
  onEvent?: (event: BisonEvent) => void;
  onError?: (error: Error) => void;
}): Promise<{ disconnect: () => void; txHash: `0x${string}` | null }>
```

#### Parameters

<ParamField path="walletClient" type="WalletClient" required>
  Viem wallet client for signing messages
</ParamField>

<ParamField path="publicClient" type="PublicClient" required>
  Viem public client for reading contract state
</ParamField>

<ParamField path="userAddress" type="string" required>
  User's Ethereum address
</ParamField>

<ParamField path="chain" type="string" required>
  Chain identifier (`"base"` or `"bsc"`). Vault address is automatically resolved from the `/info` endpoint.
</ParamField>

<ParamField path="marketId" type="string" required>
  Kalshi market ticker (e.g., `"KXFEDDECISION-26JAN-T425"`)
</ParamField>

<ParamField path="side" type="string" required>
  Position side: `"yes"` or `"no"`
</ParamField>

<ParamField path="ccontracts" type="string" required>
  Number of ccontracts as an integer string (e.g., `"1050"` for 10.50 contracts at precision 2)
</ParamField>

Tip: use `contractsToCcontracts("10.50", 2).toString()` from the SDK to convert a fixed-point contracts string.

<ParamField path="priceUusdc" type="string" required>
  Limit price in µUSDC as an integer string
</ParamField>

<ParamField path="onEvent" type="function">
  Optional callback for order events (placed, filled, etc.)
</ParamField>

<ParamField path="onError" type="function">
  Optional callback for WebSocket errors
</ParamField>

#### Returns

Object containing:

* `disconnect` - Function to stop listening for events
* `txHash` - Transaction hash (currently always `null` for off-chain orders)

#### Example

```typescript theme={null}
import { createBisonClient } from '@bison-markets/sdk-ts';
import { createWalletClient, createPublicClient, http, custom } from 'viem';
import { base } from 'viem/chains';

const client = createBisonClient({ 
  baseUrl: 'https://api.bison.markets' 
});

const walletClient = createWalletClient({
  chain: base,
  transport: custom(window.ethereum!),
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(),
});

// Buy 10 YES contracts at 65 cents each
const result = await client.executeBuyFlow({
  walletClient,
  publicClient,
  userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  chain: 'base',
  marketId: 'KXFEDDECISION-26JAN-T425',
  side: 'yes',
  ccontracts: '1000', // 10.00 contracts * 100
  priceUusdc: '650000', // $0.65 * 10^6
  onEvent: (event) => {
    if (event.type === 'order_placed') {
      console.log('Order placed successfully:', event.orderId);
    } else if (event.type === 'order_filled') {
      console.log('Order filled:', event.orderId);
    }
  },
  onError: (error) => {
    console.error('WebSocket error:', error);
  },
});

// Stop listening for events when done
// result.disconnect();
```

### `executeSellFlow`

Works identically to `executeBuyFlow` but places a sell order.

```typescript theme={null}
async executeSellFlow(params: {
  walletClient: WalletClient;
  publicClient: PublicClient;
  userAddress: `0x${string}`;
  chain: string;
  marketId: string;
  side: 'yes' | 'no';
  ccontracts: string;
  priceUusdc: string;
  onEvent?: (event: BisonEvent) => void;
  onError?: (error: Error) => void;
}): Promise<{ disconnect: () => void; txHash: `0x${string}` | null }>
```

#### Example

```typescript theme={null}
// Sell 5 NO contracts at 40 cents each
const result = await client.executeSellFlow({
  walletClient,
  publicClient,
  userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  chain: 'base',
  marketId: 'KXFEDDECISION-26JAN-T425',
  side: 'no',
  ccontracts: '500', // 5.00 contracts * 100
  priceUusdc: '400000', // $0.40 * 10^6
});
```

## Cancelling Orders

Cancel an existing open order.

### `executeCancelOrderFlow`

```typescript theme={null}
async executeCancelOrderFlow(params: {
  walletClient: WalletClient;
  userAddress: `0x${string}`;
  chain: string;
  orderId: string;
}): Promise<void>
```

#### Parameters

<ParamField path="walletClient" type="WalletClient" required>
  Viem wallet client for signing messages
</ParamField>

<ParamField path="userAddress" type="string" required>
  User's Ethereum address
</ParamField>

<ParamField path="chain" type="string" required>
  Chain identifier (`"base"` or `"bsc"`). Vault address is automatically resolved from the `/info` endpoint.
</ParamField>

<ParamField path="orderId" type="string" required>
  ID of the order to cancel (from order event or `getUserOrders`)
</ParamField>

#### Example

```typescript theme={null}
await client.executeCancelOrderFlow({
  walletClient,
  userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  chain: 'base',
  orderId: 'abc123-order-id',
});

console.log('Order cancellation requested');
```

## Querying Orders & Positions

### `getUserOrders`

Get orders for a user with optional filtering and pagination.

```typescript theme={null}
async getUserOrders(
  userId: string,
  params?: GetUserOrdersParams
): Promise<GetUserOrdersResponse>
```

#### Parameters

<ParamField path="userId" type="string" required>
  User's Ethereum address
</ParamField>

<ParamField path="params" type="object">
  Optional filtering and pagination parameters
</ParamField>

<Expandable title="params properties">
  <ParamField path="status" type="string">
    Filter by order status: `"pending"`, `"filled"`, `"cancelled"`, or `"closed"` (filled OR cancelled)
  </ParamField>

  <ParamField path="marketId" type="string">
    Filter by market ticker
  </ParamField>

  <ParamField path="action" type="string">
    Filter by action: `"buy"` or `"sell"`
  </ParamField>

  <ParamField path="side" type="string">
    Filter by side: `"yes"` or `"no"`
  </ParamField>

  <ParamField path="createdAfter" type="number">
    Orders created after this timestamp (milliseconds)
  </ParamField>

  <ParamField path="createdBefore" type="number">
    Orders created before this timestamp (milliseconds)
  </ParamField>

  <ParamField path="sortBy" type="string">
    Sort by field: `"createdAt"` (default) or `"updatedAt"`
  </ParamField>

  <ParamField path="sortOrder" type="string">
    Sort direction: `"desc"` (default) or `"asc"`
  </ParamField>

  <ParamField path="limit" type="number">
    Max orders per page (default: 50, max: 200)
  </ParamField>

  <ParamField path="cursor" type="string">
    Pagination cursor from previous response
  </ParamField>
</Expandable>

#### Response

The response includes an `orders` array and a `pagination` object:

```typescript theme={null}
{
  orders: Order[];
  pagination: {
    total: number;      // Total matching orders
    hasMore: boolean;   // More pages available
    nextCursor?: string; // Cursor for next page
  };
}
```

#### Examples

**Get all pending orders:**

```typescript theme={null}
const { orders } = await client.getUserOrders(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  { status: 'pending' }
);

console.log('Open orders:');
orders.forEach(order => {
  const requestedContracts = Number(order.requestedCcontracts) / 100;
  const price = Number(order.priceUusdc) / 1_000_000;
  console.log(`${order.action} ${requestedContracts} ${order.side} @ $${price}`);
  console.log(`  Market: ${order.marketId}`);
  console.log(`  Order ID: ${order.kalshiOrderId}`);
});
```

**Get order history with pagination:**

```typescript theme={null}
// First page of completed orders
const result = await client.getUserOrders(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  { 
    status: 'closed',
    sortBy: 'updatedAt',
    sortOrder: 'desc',
    limit: 10 
  }
);

console.log(`Showing ${result.orders.length} of ${result.pagination.total} orders`);

// Fetch next page if available
if (result.pagination.hasMore) {
  const nextPage = await client.getUserOrders(
    '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    { 
      status: 'closed',
      sortBy: 'updatedAt',
      sortOrder: 'desc',
      limit: 10,
      cursor: result.pagination.nextCursor
    }
  );
}
```

**Filter by market:**

```typescript theme={null}
const { orders } = await client.getUserOrders(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  { marketId: 'KXFEDDECISION-26JAN-T425' }
);
```

**Filter by date range:**

```typescript theme={null}
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;

const { orders } = await client.getUserOrders(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  { 
    status: 'filled',
    createdAfter: oneWeekAgo 
  }
);
```

### `getUserPositions`

Get all positions for a user.

```typescript theme={null}
async getUserPositions(userId: string): Promise<GetUserPositionsResponse>
```

#### Example

```typescript theme={null}
const { positions } = await client.getUserPositions(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);

console.log('Current positions:');
positions.forEach(position => {
  console.log(`Market: ${position.marketId}`);
  const contracts = Number(position.ccontracts) / 100;
  console.log(`  ${position.side.toUpperCase()}: ${contracts} contracts`);
});
```

## Position Token Utilities

Get token addresses and balances for position tokens.

### `getPositionTokenAddress`

```typescript theme={null}
async getPositionTokenAddress(params: {
  publicClient: PublicClient;
  chain: 'base' | 'bsc';
  marketId: string;
  side: 'yes' | 'no';
}): Promise<`0x${string}` | null>
```

Returns the ERC20 token address for a position, or `null` if not yet created. The vault address is automatically resolved from the `/info` endpoint.

### `getTokenBalance`

```typescript theme={null}
async getTokenBalance(params: {
  publicClient: PublicClient;
  tokenAddress: `0x${string}`;
  userAddress: `0x${string}`;
}): Promise<bigint>
```

Returns the token balance for a user.

### `addTokenToWallet`

```typescript theme={null}
async addTokenToWallet(params: {
  tokenAddress: `0x${string}`;
  marketId: string;
  side: 'yes' | 'no';
}): Promise<boolean>
```

Prompts the user to add a position token to their wallet (MetaMask, etc.).

#### Example

```typescript theme={null}
// Get token address
const tokenAddress = await client.getPositionTokenAddress({
  publicClient,
  chain: 'base',
  marketId: 'KXFEDDECISION-26JAN-T425',
  side: 'yes',
});

if (tokenAddress) {
  // Get user's balance
  const balance = await client.getTokenBalance({
    publicClient,
    tokenAddress,
    userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  });
  
  console.log('Token balance:', balance.toString());
  
  // Add to wallet
  const added = await client.addTokenToWallet({
    tokenAddress,
    marketId: 'KXFEDDECISION-26JAN-T425',
    side: 'yes',
  });
  
  if (added) {
    console.log('Token added to wallet');
  }
}
```

## Error Handling

```typescript theme={null}
try {
  await client.executeBuyFlow({
    walletClient,
    publicClient,
    userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    chain: 'base',
    marketId: 'KXFEDDECISION-26JAN-T425',
    side: 'yes',
    ccontracts: '1000', // 10.00 contracts * 100
    priceUusdc: '650000',
  });
} catch (error) {
  if (error instanceof Error) {
    if (error.message.includes('insufficient balance')) {
      console.error('Not enough USDC deposited');
    } else if (error.message.includes('User rejected')) {
      console.error('User cancelled signature request');
    } else {
      console.error('Failed to place order:', error.message);
    }
  }
}
```

## Important Notes

* All order prices are in µUSDC (1 USD = 1,000,000 µUSDC, so \$0.65 = 650000 µUSDC)
* Order signatures expire after 10 minutes
* Minting withdraws positions from the exchange to your wallet as ERC-20 tokens
* Burning deposits ERC-20 tokens from your wallet back to the exchange as tradeable positions
* The SDK automatically handles EIP-712 signature generation
* WebSocket connection is automatically established for buy/sell flows
* Always call `disconnect()` to clean up WebSocket connections
* Developer account IDs set at the client level are automatically applied to all orders
