# Portfolio

Access positions, holdings, order book, trade book, limits, and user profile through the Go SDK.

`GetPositions`, `GetHoldings`, `GetOrderBook`, and `GetTradeBook` return the API **`data`** payload as typed slices. `GetUserDetails` and `GetLimits` keep the `{ status, data }` envelope (`user.Data`, `limits.Data`). Field names match live `edge.arrow.trade` responses.

## Positions

```go
positions, err := client.GetPositions()
if err != nil {
    log.Fatal(err)
}
for _, p := range positions {
    fmt.Printf("%s (%s) qty=%s product=%s\n", p.Symbol, p.Exchange, p.Qty, p.Product)
    fmt.Printf("  Day buy: %s @ %s\n", p.DayBuyQty, p.DayBuyAvgPrice)
    fmt.Printf("  Day sell: %s @ %s\n", p.DaySellQty, p.DaySellAvgPrice)
    if p.Ltp != "" && p.Ltp != "0" {
        fmt.Println("  LTP:", p.Ltp)
    }
}
```

### Sample response

```json
{
  "userID": "AJ0001",
  "token": "1398464",
  "exchange": "MCXFO",
  "symbol": "GOLDPETAL30JUN26F",
  "segment": "FO",
  "product": "M",
  "qty": "0",
  "avgPrice": "0",
  "dayBuyQty": "1",
  "daySellQty": "1",
  "dayBuyAmount": "14675",
  "dayBuyAvgPrice": "14675",
  "daySellAmount": "14673",
  "daySellAvgPrice": "14673",
  "ltp": "0",
  "tickSize": "1",
  "lotSize": "1",
  "close": "0",
  "optionType": "XX"
}
```

### Position fields

| Go field | JSON | Description |
|----------|------|-------------|
| `UserID` | `userID` | User identifier |
| `Token` | `token` | Instrument token |
| `Exchange` | `exchange` | Exchange code (e.g. `NSE`, `NFO`, `MCXFO`) |
| `Symbol` | `symbol` | Trading symbol |
| `Segment` | `segment` | Segment (e.g. `CM`, `FO`) |
| `Product` | `product` | Product type (`I` / `C` / `M`) |
| `Qty` | `qty` | Net position quantity |
| `AvgPrice` | `avgPrice` | Average entry price |
| `DayBuyQty` | `dayBuyQty` | Intraday buy quantity |
| `DaySellQty` | `daySellQty` | Intraday sell quantity |
| `Ltp` | `ltp` | Last traded price (may be `"0"` off-hours) |
| `TickSize` | `tickSize` | Tick size |
| `LotSize` | `lotSize` | Lot size |
| `Close` | `close` | Previous close |
| `OptionType` | `optionType` | Option type (`CE`, `PE`, `XX`) |
| `RealisedPnL` | `realisedPnL` | Realized P&L (may be empty) |
| `UnrealisedMarkToMarket` | `unrealisedMarkToMarket` | Unrealized MTM (may be empty) |

See also [Positions API](../rest-api/positions.md).

## Holdings

```go
holdings, err := client.GetHoldings()
if err != nil {
    log.Fatal(err)
}
for _, h := range holdings {
    tradingSymbol := "N/A"
    if len(h.Symbols) > 0 {
        tradingSymbol = h.Symbols[0].TradingSymbol
    }
    fmt.Printf("%s qty=%s sellable=%s avg=%s\n", tradingSymbol, h.Qty, h.SellableQty, h.AvgPrice)
}
```

### Holdings fields

| Go field | Description |
|----------|-------------|
| `Symbols` | Per-exchange entries: `Symbol`, `TradingSymbol`, `Exchange`, `Token` |
| `Qty` | Total quantity |
| `AvgPrice` | Average purchase price |
| `UsedQty` | Quantity already used |
| `T1Qty` | T+1 quantity |
| `DepositoryQty` | Depository quantity |
| `CollateralQty` | Collateral quantity |
| `SellableQty` | Quantity available to sell |
| `Ltp` | Last traded price |
| `Pnl` | P&L (may be empty until LTP is populated) |
| `Close` | Previous close |

## Order Book

```go
orders, err := client.GetOrderBook()
if err != nil {
    log.Fatal(err)
}
for _, o := range orders {
    fmt.Printf("%s %s %s %s @ %s status=%s\n",
        o.ID, o.Symbol, o.TransactionType, o.Quantity, o.Price, o.OrderStatus)
    if o.RejectReason != "" {
        fmt.Println("  Reason:", o.RejectReason)
    }
}
```

`OrderTime` is returned as the API string (Go does not rewrite it to epoch seconds).

### Order book fields (common)

| Go field | Description |
|----------|-------------|
| `ID` | Order identifier (use for modify/cancel) |
| `UserID` | User identifier |
| `AccountID` | Account identifier |
| `Exchange` | Exchange code |
| `Symbol` | Trading symbol |
| `Token` | Instrument token |
| `OrderStatus` | `PENDING`, `OPEN`, `COMPLETE`, `CANCELLED`, `REJECTED`, etc. |
| `ReportType` | Event type (e.g. `Fill`, `Rejected`) |
| `TransactionType` | `B` or `S` |
| `Order` | `LMT`, `MKT`, etc. |
| `Quantity` / `Price` | Order size and price |
| `FillShares` / `AveragePrice` | Fill quantity and average |
| `RejectReason` | Present when rejected |
| `Remarks` | Order tag |
| `Validity` | `DAY`, `IOC`, etc. |
| `OrderTriggerPrice` | Trigger price for stop orders |

## Trade Book

```go
trades, err := client.GetTradeBook()
if err != nil {
    log.Fatal(err)
}
for _, t := range trades {
    fmt.Printf("%s %s fill %s @ %s %s\n", t.OrderID, t.Symbol, t.Quantity, t.FillPrice, t.FillTime)
}

var mcx []arrow.Trade
for _, t := range trades {
    if t.Exchange == "MCXFO" {
        mcx = append(mcx, t)
    }
}
```

### Trade book fields

| Go field | Description |
|----------|-------------|
| `OrderID` | Related order identifier |
| `ID` | Trade / event identifier |
| `Exchange` | Exchange code |
| `Symbol` | Trading symbol |
| `Quantity` | Trade quantity |
| `Product` | Product type |
| `TransactionType` | `B` or `S` |
| `FillPrice` | Execution price |
| `FillTime` | Fill timestamp |

!!! note
    Trade rows use **`OrderID`**, not `orderNo`.

## User Limits & Funds

```go
limits, err := client.GetLimits()
if err != nil {
    log.Fatal(err)
}
for _, allocation := range limits.Data.Allocations {
    fmt.Println("Segment:", allocation["segment"], "cash:", allocation["cashCurrent"])
}
fmt.Println("Allocated:", limits.Data.Margin["allocated"])
fmt.Println("Usable margin:", limits.Data.Margin["usableMargin"])
fmt.Println("Net PnL:", limits.Data.Margin["netPnl"])
```

### Margin summary fields (live)

| Field | Description |
|-------|-------------|
| `allocated` | Total allocated margin |
| `utilized` | Margin utilized |
| `usableMargin` | Margin available for trading |
| `netPnl` | Net P&L |
| `mtmLoss` | Mark-to-market loss |
| `totalCash` | Total cash |
| `spanMargin` | SPAN margin |
| `exposureMargin` | Exposure margin |
| `totalMargin` | Total margin |

### Allocation fields

| Field | Description |
|-------|-------------|
| `segment` | `CM`, `FO`, `MCX`, etc. |
| `cashCurrent` | Current cash |
| `cashOpening` | Opening cash |
| `cashEqCurrent` | Current cash equivalent |
| `nonCashCurrent` | Current non-cash |

See [Funds API](../rest-api/funds.md) for additional REST reference.

## User Details

```go
user, err := client.GetUserDetails()
if err != nil {
    log.Fatal(err)
}
fmt.Println("User ID:", user.Data.ID)
fmt.Println("Name:", user.Data.Name)
fmt.Println("Email:", user.Data.Email)
fmt.Println("TOTP enabled:", user.IsTotpEnabled())
fmt.Println("Has NSE:", user.HasExchangeAccess("NSE"))
```

Helpers on `*User`: `HasDefaultBankAccount()`, `GetDefaultBankAccount()`, `HasExchangeAccess(exchange)`, `IsTotpEnabled()`.

## Complete Example

```go
package main

import (
    "fmt"
    "log"

    "github.com/arrow-trade/go-arrow/arrow"
)

func portfolioDashboard(client *arrow.Client) {
    user, err := client.GetUserDetails()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Welcome,", user.Data.Name)

    limits, _ := client.GetLimits()
    if limits != nil {
        fmt.Println("Usable margin:", limits.Data.Margin["usableMargin"])
    }

    holdings, _ := client.GetHoldings()
    fmt.Println("Holdings:", len(holdings))

    positions, _ := client.GetPositions()
    fmt.Println("Position rows:", len(positions))

    orders, _ := client.GetOrderBook()
    open := 0
    for _, o := range orders {
        if o.OrderStatus == "PENDING" || o.OrderStatus == "OPEN" {
            open++
        }
    }
    fmt.Printf("Today's orders: %d (%d open)\n", len(orders), open)

    trades, _ := client.GetTradeBook()
    fmt.Println("Trades executed:", len(trades))
}
```
