# Market Data

The Go SDK covers REST quotes, historical candles, instruments, option chain, Greeks, holidays, and the index list.

## Quote Modes

REST quotes use `InfoQuoteMode` (not `StreamMode`).

| Mode | Code | Description | Data Included |
|------|------|-------------|---------------|
| LTP | `arrow.InfoQuoteLTP` (`ltp`) | Last Traded Price | `ltp`, `close`, `token` |
| OHLCV | `arrow.InfoQuoteOHLCV` (`ohlcv`) | OHLC & Volume | `open`, `high`, `low`, `close`, `ltp`, `volume`, `ltt`, `oi`, `token` |
| FULL | `arrow.InfoQuoteFull` (`full`) | Complete market depth | OHLCV-style fields plus `bids`, `asks`, `symbol`, etc. |

!!! note "Price scaling"
    REST quote endpoints return prices as **integers in paise** (×100). Divide by `100` for rupees. See [Instrument Quotes](../rest-api/symbol-quotes.md).

!!! note "Argument order"
    `GetQuote(exchange, symbol, mode)` — exchange first. Python is `get_quote(mode, symbol, exchange)`.

## Single Instrument Quote

`GetQuote` returns `map[string]any`.

### LTP Quote

```go
quote, err := client.GetQuote(arrow.ExchangeNSE, "RELIANCE-EQ", arrow.InfoQuoteLTP)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("LTP: ₹%.2f\n", asFloat(quote["ltp"])/100)
fmt.Printf("Close: ₹%.2f\n", asFloat(quote["close"])/100)
fmt.Println("Token:", quote["token"])
```

### OHLCV Quote

```go
quote, err := client.GetQuote(arrow.ExchangeNSE, "RELIANCE-EQ", arrow.InfoQuoteOHLCV)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("O: %.2f H: %.2f L: %.2f C: %.2f vol=%v\n",
    asFloat(quote["open"])/100,
    asFloat(quote["high"])/100,
    asFloat(quote["low"])/100,
    asFloat(quote["close"])/100,
    quote["volume"],
)
```

### Full Quote (Market Depth)

```go
quote, err := client.GetQuote(arrow.ExchangeNSE, "RELIANCE-EQ", arrow.InfoQuoteFull)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("LTP: ₹%.2f\n", asFloat(quote["ltp"])/100)

bids, _ := quote["bids"].([]any)
asks, _ := quote["asks"].([]any)
if len(bids) > 0 {
    bid := bids[0].(map[string]any)
    fmt.Printf("Best bid: ₹%.2f x %v\n", asFloat(bid["price"])/100, bid["quantity"])
}
if len(asks) > 0 {
    ask := asks[0].(map[string]any)
    fmt.Printf("Best ask: ₹%.2f x %v\n", asFloat(ask["price"])/100, ask["quantity"])
}
```

JSON numbers decode as `float64` in `map[string]any`. A small helper:

```go
func asFloat(v any) float64 {
    switch n := v.(type) {
    case float64:
        return n
    case int:
        return float64(n)
    default:
        return 0
    }
}
```

## Multiple Instrument Quotes

```go
quotes, err := client.GetQuotes([]arrow.QuoteInstrument{
    {Exchange: string(arrow.ExchangeNSE), Symbol: "ADANIENT-EQ"},
    {Exchange: string(arrow.ExchangeMCXFO), Symbol: "GOLDPETAL31JUL26F"},
    {Exchange: string(arrow.ExchangeBSE), Symbol: "RELIANCE"},
}, arrow.InfoQuoteLTP)
if err != nil {
    log.Fatal(err)
}
for _, q := range quotes {
    fmt.Printf("Token %v LTP ₹%.2f\n", q["token"], asFloat(q["ltp"])/100)
}
```

!!! note "Batch quote shape"
    `GetQuotes` returns rows with **`token`**, **`ltp`**, and **`close`** only for LTP mode. There is no `symbol` or `exchange` field in batch responses — match rows using `token`.

## Greeks

`GetGreeks` POSTs `{ exchange, symbol }` pairs (not raw token integers).

```go
raw, err := client.GetGreeks([]arrow.GreeksInstrument{
    {Exchange: string(arrow.ExchangeNFO), Symbol: "NIFTY16JUN26C23150"},
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(raw))
```

## Historical Candle Data

`GetCandleData` calls `https://historical-api.arrow.trade/candle/{exchange}/{token}/{interval}`. Success is a **JSON array** of rows (not the usual `{data,status}` envelope). `ExchangeMCXFO` is sent as `mcx` in the path.

| Interval | Code |
|----------|------|
| 1 Minute | `min` |
| 3 / 5 / 10 / 15 / 30 Minutes | `3min`, `5min`, `10min`, `15min`, `30min` |
| 1 Hour | `hour` |
| 2 / 3 / 4 Hours | `2hours`, `3hours`, `4hours` |
| 1 Day | `day` |
| 1 Week | `week` |
| 1 Month | `month` |

See [Historical Data API](../rest-api/historical-candle-data.md). Use ISO datetimes (`YYYY-MM-DDTHH:MM:SS`). Set `oi` to `true` only for NFO (adds `oi=1` and an extra field per row).

```go
candles, err := client.GetCandleData(
    arrow.ExchangeNSE,
    "3045",
    "5min",
    "2024-01-15T09:15:00",
    "2024-01-15T15:30:00",
    false,
)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(candles))
```

### Daily candles

```go
end := time.Now()
start := end.AddDate(0, 0, -30)
candles, err := client.GetCandleData(
    arrow.ExchangeNSE,
    "3045",
    "day",
    start.Format("2006-01-02T15:04:05"),
    end.Format("2006-01-02T15:04:05"),
    false,
)
```

### F&O candles with Open Interest

```go
candles, err := client.GetCandleData(
    arrow.ExchangeNFO,
    "46799",
    "15min",
    "2024-01-15T09:15:00",
    "2024-01-15T15:30:00",
    true, // oi=1
)
```

Each candle is typically `[timestamp, open, high, low, close, volume]` with OI as a 7th element when requested. Prices are in paise.

## Instruments

Download the instrument master as CSV. Unlike Python `get_instruments()` (always `/all`), Go takes a segment:

| Constant | Path |
|----------|------|
| `arrow.InstrumentSegmentAll` | `/all` |
| `arrow.InstrumentSegmentNSE` | `/nse` |
| `arrow.InstrumentSegmentBSE` | `/bse` |
| `arrow.InstrumentSegmentMCX` | `/mcx` |
| `arrow.InstrumentSegmentIndices` | `/indices` |

```go
csvText, err := client.GetInstrumentsCSV(arrow.InstrumentSegmentAll)
if err != nil {
    log.Fatal(err)
}

rows, err := client.GetInstruments(arrow.InstrumentSegmentMCX)
if err != nil {
    log.Fatal(err)
}
for _, row := range rows {
    fmt.Println(row)
}
```

See [Symbols API](../rest-api/symbols.md) for CSV column definitions. Refresh after **8:00 AM IST** daily.

## Option Chain

### Option chain symbols

Returns `{ "equity": {...}, "indices": {...} }` maps of underlyings to expiry lists.

```go
symbols, err := client.GetAllOptionChainSymbols()
if err != nil {
    log.Fatal(err)
}
// symbols["equity"]["NSE:RELIANCE-EQ"] => []string{"26-MAY-2026", ...}
fmt.Println(symbols["indices"]["INDEX:NIFTY"])
```

### Option chain

`GetOptionChain` returns `json.RawMessage`.

**Index options** — use `ExchangeINDEX`:

```go
chain, err := client.GetOptionChain(arrow.OptionChainRequest{
    Underlying: "NIFTY",
    Exchange:   arrow.ExchangeINDEX,
    Count:      "10",
    Expiry:     "16-JUN-2026",
})
```

**Equity options** — use `ExchangeNSE`:

```go
chain, err := client.GetOptionChain(arrow.OptionChainRequest{
    Underlying: "RELIANCE-EQ",
    Exchange:   arrow.ExchangeNSE,
    Count:      "20",
    Expiry:     "28-JUL-2026",
})
```

### Option chain leg fields (live)

| Field | Description |
|-------|-------------|
| `symbol` | Contract symbol (e.g. `NIFTY16JUN26C23150`) |
| `token` | Option instrument token |
| `strikePrice` | Strike price string |
| `optionType` | `CE` or `PE` |
| `segment` | e.g. `NSEFO` |
| `lotSize` | Lot size |
| `tickSize` | Tick size |
| `openingOI` | Opening open interest |

## Index List

```go
indices, err := client.GetIndexList()
if err != nil {
    log.Fatal(err)
}
for _, idx := range indices {
    name := idx["name"]
    if name == nil {
        name = idx["indexName"]
    }
    fmt.Printf("%v: token %v\n", name, idx["token"])
}
```

## Market Holidays

```go
holidays, err := client.GetHolidays()
if err != nil {
    log.Fatal(err)
}
for date, name := range holidays.Holidays {
    fmt.Printf("  %s: %s\n", date, name)
}
```

`HolidaysData` also has `SpecialTradingDays`.

## Example: Market Scanner

```go
func marketScanner(client *arrow.Client, watchlist []struct {
    Symbol string
    Token  float64
}) {
    instruments := make([]arrow.QuoteInstrument, 0, len(watchlist))
    tokenToSymbol := map[float64]string{}
    for _, w := range watchlist {
        instruments = append(instruments, arrow.QuoteInstrument{
            Exchange: string(arrow.ExchangeNSE),
            Symbol:   w.Symbol,
        })
        tokenToSymbol[w.Token] = w.Symbol
    }

    quotes, err := client.GetQuotes(instruments, arrow.InfoQuoteOHLCV)
    if err != nil {
        log.Fatal(err)
    }
    for _, q := range quotes {
        token := asFloat(q["token"])
        symbol := tokenToSymbol[token]
        closePx := asFloat(q["close"])
        ltp := asFloat(q["ltp"])
        if closePx == 0 {
            continue
        }
        change := (ltp - closePx) / closePx * 100
        fmt.Printf("%s change %.2f%% volume %v\n", symbol, change, q["volume"])
    }
}
```

## Quote Response Fields

### LTP Mode Fields

| Field | Type | Description |
|-------|------|-------------|
| `token` | number | Instrument token |
| `ltp` | number | Last traded price (paise) |
| `close` | number | Previous close (paise) |

### OHLCV Mode Fields

| Field | Type | Description |
|-------|------|-------------|
| `open` / `high` / `low` / `close` | number | OHLC (paise) |
| `ltp` | number | Last traded price |
| `volume` | number | Traded volume |

### Full Mode Additional Fields

| Field | Type | Description |
|-------|------|-------------|
| `oi` | number | Open Interest |
| `bids` / `asks` | array | 5 depth levels |
| `symbol` | string | Trading symbol (full mode) |
