Skip to main content

GreekSoft API

PyPI version Python versions License: MIT

greeksoft is a synchronous Python client for Greeksoft CTCL APIs.

It provides:

  • Session authentication and login bootstrap, with nothing persisted to disk
  • REST APIs for quotes, orders, positions, holdings, margins, contracts, OHLC (including a TradingView-style market-id based endpoint)
  • Bulk order placement, automatically paced to the session's JWT-derived rate limit
  • Apollo websocket market stream — traditional JSON feed (data_stream()), plus an optional raw binary feed for index, market-depth and open-interest data (also delivered through data_stream())
  • Optional IRIS websocket order-event stream
  • Auto-reconnect for Apollo/IRIS (same session) on disconnect
  • Fail-fast auth handling for login/password/account issues

Install

pip install greeksoft

Quick Start

from greeksoft import GreekAPI

api = GreekAPI(
    user="username",
    s_pwd="session_password",
    pwd="transaction_password",
    procli="1",             # "1" retail, other values for pro
    ac_no="account_no",     # required when procli == "1"
    is_secure=False,        # True for HTTPS/WSS
    is_base_64=False,       # True when server expects base64 payloads
    rest_ip="192.168.0.1",  # server REST_IP or masked DNS
    rest_port="3000",       # server REST_PORT
    iris=True,              # True => also start the IRIS order-update stream
)

# One-shot REST call
quote = api.token_broadcast("102000000", "option")
print(quote)

# Live market-data streaming
api.start_apollo(["102000000", "102000001"])
for tick in api.data_stream():
    print(tick)
    break  # data_stream() is an infinite generator -- remove this in real usage

api.close_connection()

That's it — no separate login step, no manual session-token handling, and nothing else to configure. Authentication is handled internally the moment GreekAPI(...) is constructed (see Credentials below).

Dependencies Used by greeksoft.py

  • requests
  • pandas
  • websocket-client
  • orjson (optional, auto-detected if installed)
  • ujson

Import

from greeksoft import GreekAPI

Initialize

api = GreekAPI(
    user="username",
    s_pwd="session_password",
    pwd="transaction_password",
    procli="1",            # "1" retail, other values for pro
    ac_no="account_no",    # required when procli == "1"
    is_secure=False,       # True for HTTPS/WSS
    is_base_64=False,      # True when server expects base64 payloads
    rest_ip="192.168.0.1", # Server REST_IP or Masked DNS
    rest_port="3000",      # Server REST_PORT
    iris=True              # True => start IRIS websocket also
)

Auth Failure Behavior

  • If login/auth returns known error codes (1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18), client prints red warning text and exits (including 1/18, "password expired").
  • If getlogininfo() does not return gcid — client prints red warning text and exits.
  • Once auth is blocked, REST calls are also blocked, and this cannot currently be cleared without reconstructing the client.

Credentials

Nothing is stored. The client never writes your password or session token to disk — a fresh session token is requested every time GreekAPI(...) is constructed, and password expiry is not handled automatically: if the server reports it, the client prints the error and exits. Use update_password(old, new) yourself, then pass the new password to the constructor.

Start Apollo Streaming

token_list = ["102000000", "102000001"]
api.start_apollo(token_list)

for tick in api.data_stream():
    print(tick)

Each JSON tick is a merged dict: {**data, 'oi': oi, 'bcast_time': bcast_time}. bcast_time is the server's broadcast timestamp for that tick, not a client-side receive time.

IRIS Order-Update Streaming

Requires iris=True at construction (and an IRIS port resolved during login). order_stream() is a second, independent blocking generator — only RMS-rejection order updates are delivered on it.

import threading

def watch_orders():
    for update in api.order_stream():
        print(update)

threading.Thread(target=watch_orders, daemon=True).start()

Binary WebSocket Feed (Index & Market Depth)

A second feed on the same Apollo socket — struct-packed binary packets instead of base64/JSON. The wire-level subscription requests (index tokens + token_list on the traditional JSON channel) are always sent the same way regardless of broadcast — but what actually reaches data_stream() differs sharply by mode:

  • broadcast="JSON" (default) — data_stream() yields only JSON: the full merged dict, for both index and your subscribed tokens. No binary data.
  • broadcast="BINARY" — token_list is also subscribed on the raw binaryData feed, and all JSON-shaped broadcast output is suppressed entirely — both index and your own subscribed tokens. data_stream() then yields only binary-decoded packets, no JSON at all.

Index tokens are hardcoded (GreekAPI.DEFAULT_INDEX_TOKENS — NIFTY, BANKNIFTY, FINNIFTY, BANKEX, SENSEX) and always subscribed — there's no parameter to override them, and no way to skip them, in either mode.

Easiest way — the broadcast flag on start_apollo():

api.start_apollo(["102000000"], broadcast="BINARY")   # 'JSON' (default) | 'BINARY'

for pkt in api.data_stream():
    print(pkt)   # {'packet_type': 'index' | 'depth' | 'oi' | 'raw', ...} -- binary only, no JSON

broadcast="BINARY" is equivalent to calling subscribe_binary_market_data(token_list) yourself right after start_apollo(), just done for you at connect time and automatically re-sent on every reconnect — plus it suppresses JSON output entirely.

Manual, fine-grained control — works independently of the broadcast flag:

api.start_apollo(["102000000"])   # JSON mode (default) -- full merged-dict ticks

api.subscribe_binary_idx_tkn(["101999957"])          # additionally, index tokens on binaryIndex
api.subscribe_binary_market_data(["102000000"])      # additionally, depth/MBP tokens on binaryData

for pkt in api.data_stream():
    if isinstance(pkt, dict) and 'packet_type' in pkt:
        print("binary:", pkt)
    else:
        print("json:", pkt)

Manually subscribing to binary while still in broadcast="JSON" mode does not suppress JSON — you get both feeds interleaved, so check for the 'packet_type' key to tell them apart. JSON suppression only happens automatically when broadcast="BINARY" was passed to start_apollo().

  • packet_type: 'index' — decoded binaryIndex packet (index value, high/low, % change, lIndexCode, ...).
  • packet_type: 'depth' — decoded binaryData (MBP) packet: 5 bid + 5 ask levels, LTP, volume, OHLC, lToken, plus lIndicativeImbalanceQty and lLimitImbalanceQty (and casrefprice on 278-byte packets).
  • packet_type: 'oi' — decoded OpenInterest-family packet. Two genuinely different structures, both reported under this same packet_type (distinguished by which keys are present, since dispatch is by exact byte length before tagging):
    • 26 bytes, BSE (decode_binary_oi_lite, GREEK_OI_BSE, iGreekCode 5067) — fields: iGreekCode, lLogTime, iMarketType, lOurToken, lCurrentOi (Open Interest), lToken, iMarketId. The meaning of iMarketType values is undocumented.
    • 58 bytes, NSE (decode_binary_oi, GREEK7202, iGreekCode 5002) — layout per the official spec.
  • packet_type: 'stats' — decoded 36-byte packet. Only lOurToken is verified; fields suffixed _unconfirmed have no documented meaning and are informational only.
  • packet_type: 'raw' — an unrecognized packet size, surfaced as {'raw': <hex>, 'size': N} rather than silently dropped.

Binary subscriptions are re-sent automatically after an Apollo reconnect, the same way the common subscription's tokens already are. unsubscribe_binary_idx_tkn(token) / unsubscribe_binary_market_data(token_list) remove them.

subscribe_binary_market_data() sends each token's real exchange segment alongside it ({"token": "3918", "segment": "1"}), looked up automatically from the contract master (get_contract_data(), fetched and cached lazily on first use — no manual step needed):

ExchangeSegMent segment
NSEEQ 1
NSEFO 2
BSEEQ 4
BSEFO 5

A token not found in the contract master (or with an unrecognized segment) is still sent, just without a segment key, rather than guessing. subscribe_binary_idx_tkn() is unaffected — index codes aren't tradable contracts and don't need a segment.

Performance Notes

  • Websocket callback is lightweight (on_message enqueues raw payloads).
  • Decoding/routing runs in a dedicated worker thread.
  • Market and order outputs are stored in rolling buffers (deque(maxlen=...)).
  • Requests use persistent HTTP session pooling (requests.Session) and default timeout.
  • Buffer overwrite/drop counters are logged periodically when backpressure happens.

Auto-Reconnect Behavior

  • On Apollo/IRIS disconnect/crash, client retries every 5 seconds
  • Retry window is 10 minutes
  • Reconnect uses the same session_id (no forced re-login)
  • Apollo sends no heartbeat — the continuous broadcast data flowing over that socket keeps it alive on its own. Only IRIS sends a periodic heartbeat (order updates can go quiet for long stretches with nothing to keep the connection alive otherwise).
  • IRIS's heartbeat loop stops gracefully if the socket is already closed (no thread-crash traceback).

Key REST Examples

Single token quote

quote = api.token_broadcast("102000000", "option")
print(quote)

Multiple token quote

quotes = api.multiple_token_broadcast("option", "NSE", ["102000000", "102000001"])
print(quotes)

Market depth (MBP)

depth = api.MBP_data("NSE", ["102000000", "102000001"])
print(depth)

Server time

print(api.server_time())   # e.g. '25-08-2026 10:30:00' (IST)

Margin calculation

margin = api.margin_calculator(token="102000000", ltp=2450.50, net_qty=75, buy_sell="Buy")

margin_multi = api.multi_token_margin_calc(
    token_list=["102000000", "102000001"],
    qty_list=[75, 150],
    ltp_list=[2450.50, 1200.00],
    side_list=["Buy", "Sell"],
)

Place order

resp = api.place_order(
    tokenno="102000000",
    symbol="RELIANCE",
    lot="1",
    qty="75",
    price="2450.50",
    buysell=1,      # 1=BUY, 2=SELL
    ordtype=1,
    trigprice=0,
    exchange="NSE",
    validity="0",
    strategyname="example_strategy"
)
print(resp)

OHLC (TradingView-style, market-id based)

candles = api.ohlc_tdview(
    exchange_token=2885,
    exchange_segment="NFO",   # one of: NEQ, NFO, NCD, BEQ, BFO, BCD, NCOM, MCOM, NSLBM
    start_time=1787130900,    # unix timestamp
    end_time=1787217300,
    interval=5,               # minutes
)

exchange_segment is resolved through an internal market-id lookup table; an unrecognized value raises ValueError before any network call is made.

Bulk Order Placement

place_orders_batch() sends a list of order specs — same keyword shape as place_order() — automatically paced to the session's message-rate limit, which is discovered from the JWT session token with no configuration needed.

order_specs = [
    dict(tokenno="102000000", symbol="NIFTY", lot=1, qty=65, price=164.95,
         buysell=1, ordtype=2, trigprice=0, exchange="NFO", validity=0,
         strategyname="bulk_example"),
    dict(tokenno="102000001", symbol="NIFTY", lot=1, qty=65, price=128.80,
         buysell=1, ordtype=2, trigprice=0, exchange="NFO", validity=0,
         strategyname="bulk_example"),
]

results = api.place_orders_batch(order_specs)   # list of (spec, response)
for spec, resp in results:
    print(spec["symbol"], "->", resp)

api.get_message_rate_limit()     # orders/sec this session is allowed to send
api.get_session_token_claims()   # full decoded JWT payload, for diagnostics

Other common methods

api.Order_Trade_status("gorderid")
api.all_pending_order()
api.cancel_order("gorderid")
api.modify_order(price="100", lot="1", qty="75", ordtype=1, gorderid="gorderid")

api.Orderbook_All()
api.Orderbook_Traded()
api.Orderbook_Rejected()
api.Orderbook_lite("gorderid")

api.Net_Position_request()
api.Net_position_Detailed()
# Strategy-wise net position:
# type="EXP" -> previous-day position details
# type="" (empty string) -> today's net position details
api.Net_Position_Details_strategywise("EXP")
api.Net_Position_Details_strategywise("")

api.get_margin_details()
api.get_holding_details()
api.get_contract_data()
api.get_ohlc_data(token="102000000", date="20260115", interval=1)

api.script_details_full(exchange="NSE", asset_type="equity", symbol="RELIANCE")
api.update_password(old_password="old", new_password="new")

api.option_chain()

Subscription Controls

api.subscribe_token(["102000000", "102000001"])
api.unsubscribe_token("102000000")

Shutdown

api.close_connection()

Notes

  • Authorization is handled internally via session token, fetched fresh on each client construction.
  • If procli != "1", AccountNumber is not sent in order payload.
  • Current implementation keeps self.ssl_verify = False.
  • data_stream() and order_stream() are blocking generators backed by buffered deques.

License

MIT License (see LICENSE).

Metadata

Release files for greeksoft 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for greeksoft 0.1.2
File Size Uploaded Provenance
greeksoft-0.1.2.tar.gz 31.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for greeksoft 0.1.2
File Interpreter ABI Platform
greeksoft-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 56.4 kB

Release files / greeksoft-0.1.2.tar.gz

Download URL greeksoft-0.1.2.tar.gz
Size 31.0 kB
Tags Source
SHA-256 checksum
How to use checksums
310a99010b097fbb3c48717b688ecaaddde1934bf31e668a9ed03ec9e9c550bc
BLAKE2b-256 checksum
How to use checksums
6b7db026f030933d9344c399803e47143ff683fa7c797ed5a6a374593b91fabd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.0

Release files / greeksoft-0.1.2-py3-none-any.whl

Download URL greeksoft-0.1.2-py3-none-any.whl
Size 25.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
853f971dd3092f82398c6c6311a07f68385846548da3a64ba1389fc8c91e56d0
BLAKE2b-256 checksum
How to use checksums
8c00cf202847e688391327c6a55408376a88ca16f6d9a374125e87cdd8061b45
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.0

Security for greeksoft 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page