# 5paisa TypeScript SDK

TypeScript SDK for 5paisa APIs.

---

## Installation

```sh
npm install 5paisa-ts
# or
pnpm add 5paisa-ts
# or
bun add 5paisa-ts
```

The package targets Node.js 18+ (or compatible runtimes such as Bun) where `fetch` and `WebSocket` are available globally.

---

## Getting started

```ts
import {
  FivePaisaClient,
  type FivePaisaCredentials,
} from '5paisa-ts';

const credentials: FivePaisaCredentials = {
  APP_NAME: 'YOUR_APP_NAME',
  APP_SOURCE: 'YOUR_APP_SOURCE',
  USER_ID: 'YOUR_USER_ID',
  PASSWORD: 'YOUR_PASSWORD',
  USER_KEY: 'YOUR_USER_KEY',
  ENCRYPTION_KEY: 'YOUR_ENCRYPTION_KEY',
};

const client = new FivePaisaClient(credentials);

// Example: authenticate using an OAuth request token from 5paisa
const accessToken = await client.getOauthSession('REQUEST_TOKEN_FROM_5PAISA');

// Or: if you already have an access token
client.setAccessToken(accessToken, 'CLIENT_CODE');

// Fetch basic information
const holdings = await client.holdings();
const margin = await client.margin();

console.log('Holdings', holdings);
console.log('Margin', margin);
```

For full API authentication details, see the sections below.

---

## Credentials and authentication

### 1. Configure API keys

Get your API keys from the 5paisa developer portal and keep them outside of source control (for example in env files):

```conf
[KEYS]
APP_NAME=YOUR_APP_NAME
APP_SOURCE=YOUR_APP_SOURCE
USER_ID=YOUR_USER_ID
PASSWORD=YOUR_PASSWORD
USER_KEY=YOUR_USER_KEY
ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY
```

### 2. Create a client

```ts
import {
  FivePaisaClient,
  type FivePaisaCredentials,
} from '5paisa-ts';

const credentials: FivePaisaCredentials = {
  APP_NAME: process.env.APP_NAME!,
  APP_SOURCE: process.env.APP_SOURCE!,
  USER_ID: process.env.USER_ID!,
  PASSWORD: process.env.PASSWORD!,
  USER_KEY: process.env.USER_KEY!,
  ENCRYPTION_KEY: process.env.ENCRYPTION_KEY!,
};

const client = new FivePaisaClient(credentials);
```

### 3. OAuth request token flow

This is the recommended flow and mirrors the `py5paisa` behaviour.

1. Log in to the vendor login url from a browser and obtain a short lived request token.
2. Exchange the request token for an access token.

```ts
// After you obtain `requestToken` from the 5paisa login redirect url:
const accessToken = await client.getOauthSession(requestToken);

// You can also call getAccessToken(requestToken) directly
// const accessToken = await client.getAccessToken(requestToken);

console.log('Access token', accessToken);
```

Once you have the token, you can reuse it on a new process without repeating the browser flow:

```ts
client.setAccessToken(accessToken, 'CLIENT_CODE');
```

### 4. TOTP + PIN flow

The client also exposes the TOTP login flow equivalent to the Python SDK.

```ts
// Step 1: request token via TOTP + PIN
const requestToken = await client.getRequestToken(
  'CLIENT_CODE',
  'TOTP_VALUE',
  'PIN',
);

// Step 2: exchange the request token for access token
if (requestToken) {
  const accessToken = await client.getAccessToken(requestToken);
  console.log('Access token', accessToken);
}

// Or use the convenience helper which does both steps internally
const accessTokenFromTotp = await client.getTotpSession(
  'CLIENT_CODE',
  'TOTP_VALUE',
  'PIN',
);
```

### 5. Session helpers

The following helpers are available for session management and diagnostics:

- `getAccessToken(requestToken?)`  get or refresh the current access token
- `getOauthSession(requestToken)`  convenience wrapper around `getAccessToken`
- `getTotpSession(clientCode, totp, pin)`  run the full TOTP login flow
- `setAccessToken(accessToken, clientCode)`  set an existing token and client code
- `loginCheck()`  validate an existing session for the current client code
- `jwtValidate()`  validate the current JWT token and get the message from the server

---

## Working with scrip master and symbols

The TypeScript client provides the same scrip master utilities as `py5paisa`.

### Fetch the full scrip master

```ts
const scrips = await client.getScrips();
console.log('Total scrips', scrips.length);
```

### Query a specific scrip

The arguments map to the Python `query_scrips` function: exchange, exchangeType, symbol, strike, type, expiry.

```ts
// Example: cash equity
const cash = await client.queryScrips(
  'N',      // exchange
  'C',      // exchange type
  'ITC',    // symbol
  '0',      // strike, 0 for cash stocks
  'XX',     // type: XX for cash and futures, EQ for indices, CE or PE for options
  '',       // expiry (empty for cash)
);

// Example: options
const options = await client.queryScrips(
  'N',
  'D',
  'NIFTY',
  '22300',
  'CE',
  '2024-04-25',
);
```

---

## User information, reports and portfolio

These methods give you holdings, positions, margins and reports.

```ts
// Holdings
const holdings = await client.holdings();

// Margin
const margin = await client.margin();

// Net positions (current positions)
const positions = await client.positions();

// Day-wise net positions
const intradayPositions = await client.positionsDay();

// Order book
const orderBook = await client.orderBook();

// Trade book
const tradeBook = await client.getTradebook();

// Idea buy and trade recommendations
const buyIdeas = await client.getBuy();
const tradeIdeas = await client.getTrade();

// Tax report
const taxReport = await client.taxReport('2024-01-01', '2024-06-26');

// Ledger report
const ledger = await client.fetchLedger('2024-01-01', '2024-06-26');
```

The `taxReport` and `fetchLedger` helpers validate the date format and ensure that `fromDate` is not later than `toDate`.

---

## Market data and option chain

### Market feed (snapshot)

```ts
// Example option instruments for NIFTY
const reqList = [
  {
    Exch: 'N',
    ExchType: 'D',
    Symbol: 'NIFTY 22 APR 2021 CE 15200.00',
    Expiry: '20210422',
    StrikePrice: '15200',
    OptionType: 'CE',
  },
  {
    Exch: 'N',
    ExchType: 'D',
    Symbol: 'NIFTY 22 APR 2021 PE 15200.00',
    Expiry: '20210422',
    StrikePrice: '15200',
    OptionType: 'PE',
  },
];

const feed = await client.fetchMarketFeed(reqList);
console.log(feed);
```

### Market feed by scrip

When you already know the scrip code, you can use `fetchMarketFeedScrip`.

```ts
const scripFeed = await client.fetchMarketFeedScrip([
  { Exch: 'N', ExchType: 'C', ScripCode: 2885 },
]);
```

### Market status

```ts
const marketStatus = await client.getMarketStatus();
console.log(marketStatus);
```

### Market depth and snapshot

```ts
// Market depth from the V3 endpoint
const depth = await client.fetchMarketDepth([
  { Exch: 'N', ExchType: 'C', ScripCode: 1660 },
]);

// Market depth by symbol
const depthBySymbol = await client.fetchMarketDepthBySymbol([
  {
    Exch: 'N',
    ExchType: 'C',
    Symbol: 'RELIANCE',
  },
]);

// Market depth by scrip convenience helper
const depthByScrip = await client.fetchMarketDepthByScrip({
  Exchange: 'N',
  ExchangeType: 'C',
  ScripCode: '1660',
});

// Full market snapshot for multiple scrips
const snapshot = await client.fetchMarketSnapshot([
  { Exchange: 'N', ExchangeType: 'C', ScripCode: '2885' },
  { Exchange: 'N', ExchangeType: 'C', ScripData: 'ITC_EQ' },
]);
```

### Option chain

```ts
// Get all active expiries for a symbol
const expiries = await client.getExpiry('N', 'NIFTY');

// Pick one expiry timestamp from the response and fetch the option chain
const someExpiryTs = 1647507600000;
const optionChain = await client.getOptionChain('N', 'NIFTY', someExpiryTs);
```

### Historical OHLC data

```ts
// historicalData(exch, exchangeSegment, scripCode, timeframe, from, to)
const candles = await client.historicalData(
  'N',
  'C',
  1660,
  '15m',
  '2021-05-25',
  '2021-06-16',
);

console.log(candles);
```

Accepted time frames: `1m`, `3m`, `5m`, `10m`, `15m`, `30m`, `60m`, `1d`.

---

## Placing, modifying and cancelling orders

The core order placement APIs mirror those from `py5paisa`.

### Basic cash order

```ts
import {
  FivePaisaClient,
  type FivePaisaCredentials,
  Exchange,
  ExchangeSegment,
} from '5paisa-ts';

// ... create credentials and client as shown earlier

await client.placeOrder({
  Exchange: Exchange.NSE,            // or 'N'
  ExchangeType: ExchangeSegment.CASH, // or 'C'
  ScripCode: 1660,
  Qty: 1,
  Price: 205,
  OrderType: 'B',                    // B for buy, S for sell
  IsIntraday: true,
});
```

### Stop loss order

```ts
await client.placeOrder({
  Exchange: Exchange.NSE,
  ExchangeType: ExchangeSegment.CASH,
  ScripCode: 1660,
  Qty: 1,
  Price: 208,
  OrderType: 'B',
  IsIntraday: true,
  IsStopLossOrder: true,
  StopLossPrice: 207.5,
});
```

### After market orders

Pass `AHPlaced: AHPlaced.AFTER_MARKET_CLOSED` for offline orders.

```ts
import { AHPlaced } from '5paisa-ts';

await client.placeOrder({
  Exchange: Exchange.NSE,
  ExchangeType: ExchangeSegment.CASH,
  ScripCode: 1660,
  Qty: 1,
  Price: 205,
  OrderType: 'B',
  IsIntraday: false,
  AHPlaced: AHPlaced.AFTER_MARKET_CLOSED,
});
```

### Modify and cancel

```ts
// Modify an order
await client.modifyOrder({
  ExchOrderID: '1100000017861430',
  Price: 261,
});

// Cancel an order
await client.cancelOrder('1100000017795041');

// Cancel multiple regular orders in bulk
await client.cancelBulkOrder([
  { ExchOrderID: 'ORDER_ID_1' },
  { ExchOrderID: 'ORDER_ID_2' },
]);
```

### Bracket and cover orders

```ts
// Bracket order
await client.boOrder({
  Exchange: 'N',
  ExchangeType: 'C',
  ScripCode: 1660,
  OrderType: 'B',
  Qty: 1,
  LimitPriceInitialOrder: 330,
  TriggerPriceInitialOrder: 0,
  LimitPriceProfitOrder: 345,
  TriggerPriceForSL: 320,
  RequestType: 'P',
  AtMarket: false,
});

// Modify bracket order
await client.modifyBoOrder({
  ExchangeOrderID: '1100000017861430',
  LimitPriceInitialOrder: 331,
  LimitPriceProfitOrder: 346,
});

// Cancel a bracket order leg
await client.cancelBoOrder({
  ExchangeOrderID: '1100000017861430',
});

// Cover order
await client.coverOrder({
  Exchange: 'N',
  ExchangeType: 'C',
  ScripCode: 1660,
  OrderType: 'B',
  Qty: 1,
  LimitPriceInitialOrder: 330,
  TriggerPriceForSL: 320,
});

// Modify cover order
await client.modifyCoverOrder({
  ExchangeOrderID: '1100000017861430',
  TriggerPriceForSL: 321,
});

// Cancel cover order leg
await client.cancelCoverOrder({
  ExchangeOrderID: '1100000017861430',
});
```

> Note: for cover orders, using `LimitPriceProfitOrder: 0` is equivalent to the Python behaviour.

### Order margin calculation

Single order margin:

```ts
const marginResponse = await client.orderMargin({
  AtMarket: 'Y',
  LimitRate: 0,
  // other fields as per 5paisa order margin api
});
```

Multi leg margin for strategies:

```ts
const marginForMultipleOrders = await client.multiOrderMargin({
  CoverPositions: 'Y',
  Orders: [
    {
      Exch: 'N',
      ExchType: 'C',
      ScripCode: 2885,
      PlaceModifyCancel: 'P',
      OrderType: 'B',
      Price: 0,
      Qty: 1,
      IsIntraday: false,
    },
    {
      Exch: 'B',
      ExchType: 'C',
      ScripCode: 512070,
      PlaceModifyCancel: 'P',
      OrderType: 'S',
      Price: 0,
      Qty: 1,
      IsIntraday: true,
    },
  ],
});
```

### Square off and position conversion

```ts
// Convert an existing position
await client.positionConversion(
  'N',          // Exch
  'C',          // ExchType
  'BPCL_EQ',    // ScripData
  'B',          // TradeType (B or S)
  5,            // ConvertQty
  'D',          // ConvertFrom (D or I)
  'I',          // ConvertTo (D or I)
);

// Square off all open positions
await client.squareoffAll();
```

### Bulk order placement

```ts
const bulkOrderPayload = {
  Exchange: 'N',
  ExchangeType: 'C',
  OrderList: [
    {
      Exchange: 'N',
      ExchangeType: 'C',
      ScripCode: 0,
      ScripData: 'ITC_EQ',
      Price: '440',
      OrderType: 'Buy',
      Qty: 1,
      DisQty: '0',
      StopLossPrice: '0',
      IsIntraday: true,
      iOrderValidity: '0',
      RemoteOrderID: '50000091_220620',
    },
    {
      Exchange: 'N',
      ExchangeType: 'C',
      ScripCode: 0,
      ScripData: 'IDEA_EQ',
      Price: '15',
      OrderType: 'Buy',
      Qty: 1,
      DisQty: '0',
      StopLossPrice: '0',
      IsIntraday: true,
      iOrderValidity: '0',
      RemoteOrderID: '50000091_220620',
    },
  ],
};

await client.placeOrderBulk(bulkOrderPayload);
```

---

## Basket orders

The SDK exposes a `BasketOrder` class and a family of basket helper methods.

```ts
import { BasketOrder } from '5paisa-ts';

// Create a new basket
await client.createBasket('My strategy basket');

// Get all baskets
const baskets = await client.getBasket();

// Rename a basket
await client.renameBasket('Renamed strategy basket', 1234);

// Clone an existing basket
await client.cloneBasket(1234);

// Delete baskets in bulk
await client.deleteBasket([
  { BasketID: '1234' },
  { BasketID: '5678' },
]);

// Add an order to one or more baskets
const orderToBasket = new BasketOrder({
  Exchange: 'N',
  ExchangeType: 'C',
  Price: 23000,
  OrderType: 'BUY',
  Qty: 1,
  ScripCode: '1660',
  DelvIntra: 'I',
});

const basketList = [
  { BasketID: '1234' },
  { BasketID: '5678' },
];

await client.addBasketOrder(orderToBasket, basketList);

// Execute a basket
await client.executeBasket(1234);

// Get orders inside a basket
const ordersInBasket = await client.getOrderInBasket(1234);

// Margin required for a basket
const basketMargin = await client.basketMargin('1234', 'Y');
```

---

## Order status, trades and history

```ts
// Trade information
const tradeInfoRequest = [
  {
    Exch: 'N',
    ExchType: 'C',
    ScripCode: 20374,
    ExchOrderID: '1000000015310807',
  },
];

const tradeInfo = await client.fetchTradeInfo(tradeInfoRequest);

// Order status
const orderStatusRequest = [
  {
    Exch: 'N',
    ExchType: 'C',
    ScripCode: 20374,
    RemoteOrderID: '90980441',
  },
];

const orderStatus = await client.fetchOrderStatus(orderStatusRequest);

// Trade history by exchange order id
const history = await client.getTradeHistory('1000000015310807');
```

---

## WebSocket streaming

WebSocket streaming closely follows the Python `Request_Feed`, `connect` and `receive_data` helpers.

### Subscribe to one or more scrips

```ts
import {
  FivePaisaClient,
  type FivePaisaCredentials,
} from '5paisa-ts';

const credentials: FivePaisaCredentials = {
  APP_NAME: 'YOUR_APP_NAME',
  APP_SOURCE: 'YOUR_APP_SOURCE',
  USER_ID: 'YOUR_USER_ID',
  PASSWORD: 'YOUR_PASSWORD',
  USER_KEY: 'YOUR_USER_KEY',
  ENCRYPTION_KEY: 'YOUR_ENCRYPTION_KEY',
};

const client = new FivePaisaClient(credentials);

// Authenticate first using OAuth or TOTP so that jwtToken and clientCode are set
await client.getOauthSession('REQUEST_TOKEN');

const subscribeList = [
  { Exch: 'N', ExchType: 'C', ScripCode: 1660 },
  { Exch: 'N', ExchType: 'D', ScripCode: 61211 },
];

const payload = client.requestFeed('mf', 's', subscribeList);

client.connect(payload);

client.receiveData((event) => {
  const data = JSON.parse(event.data as string);
  console.log('Tick', data);
});
```

### Unsubscribe and close

```ts
// Unsubscribe from specific scrips
const unsubscribePayload = client.requestFeed('mf', 'u', [
  { Exch: 'N', ExchType: 'C', ScripCode: 1660 },
]);

client.connect(unsubscribePayload);

// Close websocket
client.closeData();
```

You can also use `sendData` to attach a custom `onopen` handler and `errorData` to attach an error handler.

### 20 depth market data

The `socket20Depth` helper maps to the Python `socket_20_depth` utility.

```ts
const depthPayload = {
  method: 'subscribe',
  operation: '20depth',
  instruments: ['NC2885'],
};

await client.socket20Depth(depthPayload);

client.receiveData((event) => {
  const data = JSON.parse(event.data as string);
  console.log('Depth 20 tick', data);
});
```

---

## Strategy execution (Strategies class)

The `Strategies` class is a TypeScript port of `py5paisa.strategy.strategies`. It executes multi leg strategies as market orders; use it with care.

```ts
import {
  Strategies,
  type FivePaisaCredentials,
} from '5paisa-ts';

const credentials: FivePaisaCredentials = {
  APP_NAME: 'YOUR_APP_NAME',
  APP_SOURCE: 'YOUR_APP_SOURCE',
  USER_ID: 'YOUR_USER_ID',
  PASSWORD: 'YOUR_PASSWORD',
  USER_KEY: 'YOUR_USER_KEY',
  ENCRYPTION_KEY: 'YOUR_ENCRYPTION_KEY',
};

// Use either requestToken or clientCode + TOTP + PIN
const strategy = new Strategies(credentials, 'CLIENT_CODE', 'TOTP_VALUE', 'PIN');

// Each of the following executes a full strategy at market
await strategy.shortStraddle('banknifty', '37000', 50, '20210610', 'I', {
  tag: 'MyShortStraddle',
});

await strategy.shortStrangle('banknifty', ['35300', '37000'], 50, '20210610', 'D', {
  tag: 'MyShortStrangle',
});

await strategy.longStraddle('banknifty', '37000', 50, '20210610', 'I', {
  tag: 'MyLongStraddle',
});

await strategy.longStrangle('banknifty', ['35300', '37000'], 50, '20210610', 'D', {
  tag: 'MyLongStrangle',
});

await strategy.ironCondor('NIFTY', ['15000', '15200'], ['15100', '15150'], 75, '20210603', 'I', {
  tag: 'MyIronCondor',
});

await strategy.ironFly('NIFTY', ['15000', '15200'], '15100', 75, '20210610', 'I', {
  tag: 'MyIronFly',
});

await strategy.callCalendar('nifty', '15600', 75, ['20210603', '20210610'], 'I', {
  tag: 'MyCallCalendar',
});

await strategy.putCalendar('nifty', '15600', 75, ['20210603', '20210610'], 'I', {
  tag: 'MyPutCalendar',
});

// Square off all legs that were tagged with a given strategy tag
await strategy.squareoff('MyIronCondor');
```

**Strategies methods covered:**

- `shortStraddle`
- `shortStrangle`
- `longStraddle`
- `longStrangle`
- `ironFly`
- `ironCondor`
- `callCalendar`
- `putCalendar`
- `squareoff`

---

## Types, enums and helper classes

The SDK exports several enums and classes to help you build strongly typed payloads.

### Order related enums

```ts
import {
  Exchange,
  ExchangeSegment,
  OrderFor,
  OrderSide,
  OrderValidity,
  AHPlaced,
  RequestType,
} from '5paisa-ts';

// Examples
const ex = Exchange.NSE;            // 'N'
const seg = ExchangeSegment.DERIVATIVE; // 'D'
const side = OrderSide.BUY;        // 'BUY'
const validity = OrderValidity.DAY; // 0
```

### Order, BoCoOrder and BasketOrder classes

```ts
import {
  Order,
  BoCoOrder,
  BasketOrder,
} from '5paisa-ts';

const order = new Order({
  exchange: Exchange.NSE,
  exchangeSegment: ExchangeSegment.CASH,
  price: 205,
  orderType: 'B',
  quantity: 1,
  scripCode: 1660,
  isIntraday: true,
});

const bracket = new BoCoOrder({
  scripCode: 1660,
  qty: 1,
  limitPriceInitialOrder: 330,
  triggerPriceInitialOrder: 0,
  limitPriceProfitOrder: 345,
  buySell: 'B',
  exch: 'N',
  exchType: 'C',
  requestType: 'P',
  limitPriceForSL: 320,
  triggerPriceForSL: 320,
});

const basketOrder = new BasketOrder({
  Exchange: 'N',
  ExchangeType: 'C',
  Price: 23000,
  OrderType: 'BUY',
  Qty: 1,
  ScripCode: '1660',
  DelvIntra: 'I',
});
```

These classes mirror the structure of the Python SDK and are primarily useful for building complex orders or reusing payloads.

### Low level constants

Advanced users can also import the following from `5paisa-ts` for custom integrations:

- `HEADERS`, `GENERIC_PAYLOAD`, `LOGIN_PAYLOAD`, `LOGIN_CHECK_PAYLOAD`
- `WS_PAYLOAD`, `JWT_HEADERS`, `JWT_PAYLOAD`, `SOCKET_DEPTH_PAYLOAD`
- `VTT_TYPE`, `SUBSCRIPTION_KEY`, `TODAY_TIMESTAMP`, `NEXT_DAY_TIMESTAMP`
- All route constants from `urlconst.ts` such as `BASE_URL`, `LOGIN_ROUTE`, `SCRIP_MASTER_ROUTE`, `ORDER_PLACEMENT_ROUTE`, `HISTORICAL_DATA_ROUTE`, `MARKET_FEED_ROUTE`, etc.

These are not required for normal usage, but are exported for completeness and advanced scenarios.

---

## Complete FivePaisaClient api overview

For quick reference, the public methods on `FivePaisaClient` are:

- **Authentication and session**
  - `getRequestToken`
  - `getOauthSession`
  - `getAccessToken`
  - `getTotpSession`
  - `setAccessToken`
  - `loginCheck`
  - `jwtValidate`

- **Scrip master**
  - `getScrips`
  - `queryScrips`

- **User information and reports**
  - `holdings`
  - `margin`
  - `orderBook`
  - `positions`
  - `positionsDay`
  - `getTradebook`
  - `getBuy`
  - `getTrade`
  - `taxReport`
  - `fetchLedger`

- **Market data**
  - `fetchMarketFeed`
  - `fetchMarketFeedScrip`
  - `fetchMarketDepth`
  - `fetchMarketDepthBySymbol`
  - `fetchMarketDepthByScrip`
  - `fetchMarketSnapshot`
  - `getMarketStatus`
  - `historicalData`
  - `getExpiry`
  - `getOptionChain`

- **Orders and positions**
  - `placeOrder`
  - `modifyOrder`
  - `cancelOrder`
  - `cancelBulkOrder`
  - `orderMargin`
  - `multiOrderMargin`
  - `placeOrderBulk`
  - `positionConversion`
  - `squareoffAll`
  - `vttOrder`

- **Basket orders**
  - `getBasket`
  - `createBasket`
  - `renameBasket`
  - `deleteBasket`
  - `cloneBasket`
  - `executeBasket`
  - `getOrderInBasket`
  - `addBasketOrder`
  - `basketMargin`

- **Order status, trades and history**
  - `fetchOrderStatus`
  - `fetchTradeInfo`
  - `getTradeHistory`

- **WebSocket and streaming**
  - `requestFeed`
  - `connect`
  - `sendData`
  - `receiveData`
  - `closeData`
  - `errorData`
  - `socket20Depth`

This README, combined with the examples above, provides usage coverage for all of these methods as well as all exported strategy functions and helper types.
