This project runs a single Go service that logs into Angel One, reads live market data, and stores raw ticks in Postgres.
It supports two ingestion paths at the same time:
websocket: Angel One Smart Stream live feedpoller: repeated REST quote polling
Both sources write into the same append-only live_ticks table.
The service:
- Loads configuration from environment variables or
.env - Logs into Angel One using
API_KEY,CLIENT_ID,MPIN, andTOTP_SECRET - Connects to Postgres
- Creates the
live_tickstable if it does not exist - Starts websocket ingestion, poller ingestion, or both
- Buffers incoming ticks and writes them to Postgres in batches
The main entrypoint is cmd/ingestor/main.go.
cmd/ingestor: service entrypointinternal/config: environment config loading and validationinternal/auth: Angel One login and session creationinternal/ingest/websocket: websocket ingestion and binary packet parsinginternal/ingest/poller: REST polling ingestioninternal/service: batching and shutdown-safe flushinginternal/storage/postgres: Postgres schema setup and bulk inserts
Old prototype files still exist at the repo root, but they are excluded from the default build with //go:build ignore.
- Go installed locally
- Postgres available and reachable from
DB_URL - Angel One credentials
- Create a
.envfile from.env.example - Fill in your real credentials and database URL
- Run:
go run ./cmd/ingestorOr with Docker Compose:
docker compose upRequired:
DB_URL: Postgres connection stringAPI_KEY: Angel One API keyCLIENT_ID: Angel One client codeMPIN: Angel One MPIN/passwordTOTP_SECRET: TOTP secret for login
Ingestion toggles:
ENABLE_WEBSOCKET:trueorfalse, defaulttrueENABLE_POLLER:trueorfalse, defaulttrue
Websocket settings:
WEBSOCKET_MODE: default2WEBSOCKET_TOKENS: JSON array of websocket subscriptionsWEBSOCKET_PING_PERIOD: default30sWEBSOCKET_URL: defaults to Angel One Smart Stream URL
Poller settings:
POLLER_MODE: defaultLTPPOLLER_INSTRUMENTS: JSON array of instruments to pollPOLL_INTERVAL: default1sQUOTE_URL: defaults to Angel One quote endpoint
Batching/runtime:
BATCH_SIZE: default500FLUSH_INTERVAL: default5sQUEUE_SIZE: default2048LOGIN_URL: override Angel One login endpoint if needed
WEBSOCKET_TOKENS must be a JSON array like:
[
{
"exchange_type": 1,
"tokens": ["99926000", "2885"]
},
{
"exchange_type": 3,
"tokens": ["99919000"]
}
]POLLER_INSTRUMENTS must be a JSON array like:
[
{
"exchange": "NSE",
"symbol_token": "99926000"
},
{
"exchange": "NSE",
"symbol_token": "2885"
}
]The service creates a live_ticks table automatically. Important columns:
sourcetokenexchangeexchange_typetrading_symbolevent_timereceived_atltpvolumeopen_pricehigh_pricelow_priceclose_pricetotal_buy_qtytotal_sell_qtyavg_traded_priceupper_circuitlower_circuithigh_52_weeklow_52_week
Schema creation and inserts are handled in store.go.
Run websocket only:
ENABLE_WEBSOCKET=true
ENABLE_POLLER=falseRun poller only:
ENABLE_WEBSOCKET=false
ENABLE_POLLER=trueRun both:
ENABLE_WEBSOCKET=true
ENABLE_POLLER=trueStart the service, then check Postgres:
select source, token, event_time, ltp
from live_ticks
order by id desc
limit 20;If rows are appearing, ingestion is working.
GOCACHE=$(pwd)/.gocache go test ./...The local GOCACHE override is useful if the default Go cache path is restricted in your environment.
load config: ... is required
- A required env var is missing
login failed: ...
- Angel One credentials, TOTP secret, or API key are wrong
No rows in live_ticks
- Check token JSON formatting first
- Check that the selected exchanges and symbol tokens are valid
- Check Postgres connectivity from
DB_URL - If websocket is enabled, confirm your feed tokens and subscription mode are accepted
Only poller rows or only websocket rows appear
- That usually means one ingestion path is working and the other is failing; inspect service logs