How to Add a New Broker Integration to Fincept Terminal’s Trading Engine

To add a new broker integration to Fincept Terminal, create a C++ class that inherits from IBroker, implement the required virtual methods for authentication and trading operations, register the class in BrokerRegistry::register_all(), and optionally extend InstrumentService to support the broker’s symbol master.

Fincept Terminal is an open-source trading platform built on a modular plug-in architecture that abstracts broker connectivity through a common interface. If you want to add a new broker integration to Fincept Terminal, you will work within the fincept-qt/src/trading/ directory to implement a concrete broker class and wire it into the central registry. This guide walks through the complete implementation process using the actual source code structure from the Fincept-Corporation/FinceptTerminal repository.

1. Understand the Broker Interface (IBroker)

All broker implementations must conform to the pure-virtual API defined in fincept-qt/src/trading/BrokerInterface.h. The IBroker interface defines the contract for identity, authentication, order management, portfolio queries, and market data retrieval.

Key methods you must implement include:

  • BrokerId id() const – Returns the unique enum identifier from the BrokerId enumeration.
  • const char* name() const – Human-readable broker name for UI display.
  • const char* base_url() const – Base URL for the broker’s REST API endpoints.
  • BrokerProfile profile() const – Metadata struct defining credential fields, supported exchanges, product types, and UI defaults.
  • TokenExchangeResponse exchange_token(...) – Exchanges temporary OAuth codes or credentials for access tokens.
  • Order lifecycleplace_order(), modify_order(), and cancel_order() handle CRUD operations for orders.
  • Portfolio queriesget_orders(), get_positions(), get_holdings(), and get_funds() retrieve account state.
  • Market dataget_quotes() and get_history() fetch real-time and historical price data.
  • auth_headers() – Protected helper that constructs authentication headers for every HTTP request.

The interface also provides helper methods for secure credential storage via SecureStorage. Study existing implementations such as UpstoxBroker and ZerodhaBroker in fincept-qt/src/trading/brokers/ to understand concrete patterns for error handling and response parsing.

2. Create the Broker Class

Create a new header and source file pair under fincept-qt/src/trading/brokers/<yourbroker>/. The class must inherit from IBroker and implement all pure-virtual methods.

Below is a skeleton structure replacing <YourBroker> with your actual broker name:

#pragma once
#include "trading/BrokerInterface.h"
#include "trading/brokers/BrokerHttp.h"

namespace fincept::trading {

class <YourBroker> : public IBroker {
  public:
    // Identity
    BrokerId id() const override { return BrokerId::<YourBroker>; }
    const char* name() const override { return "<YourBrokerDisplayName>"; }
    const char* base_url() const override { return "https://api.<yourbroker>.com/v1"; }

    // UI profile metadata
    BrokerProfile profile() const override {
        return BrokerProfile{
            .id = "<yourbroker>",
            .display_name = "<YourBrokerDisplayName>",
            .region = "IN",
            .currency = "INR",
            .credential_fields = {
                {CredentialField::ApiKey, "API KEY", "Enter API Key...", false},
                {CredentialField::ApiSecret, "API SECRET", "Enter API Secret...", true},
                {CredentialField::AuthCode, "AUTH CODE", "Paste auth code...", false},
            },
            .exchanges = {"NSE", "BSE", "NFO"},
            .product_types = {
                {"Intraday (I)", ProductType::Intraday},
                {"Delivery (D)", ProductType::Delivery},
                {"Margin (MTF)", ProductType::Margin},
            },
            .supports_intraday = true,
            .supports_bracket_order = false,
            .supports_cover_order = false,
            .has_native_paper = false,
            .default_paper_balance = 1e6,
            .default_watchlist = {"RELIANCE", "TCS", "INFY"},
            .default_symbol = "RELIANCE",
            .default_exchange = "NSE",
            .brokerage_info = "₹20/order flat",
        };
    }

    // Authentication
    TokenExchangeResponse exchange_token(const QString& api_key,
                                         const QString& api_secret,
                                         const QString& auth_code) override;

    // Order lifecycle
    OrderPlaceResponse place_order(const BrokerCredentials& creds,
                                   const UnifiedOrder& order) override;
    ApiResponse<QJsonObject> modify_order(const BrokerCredentials& creds,
                                          const QString& order_id,
                                          const QJsonObject& mods) override;
    ApiResponse<QJsonObject> cancel_order(const BrokerCredentials& creds,
                                          const QString& order_id) override;

    // Portfolio
    ApiResponse<QVector<BrokerOrderInfo>> get_orders(const BrokerCredentials& creds) override;
    ApiResponse<QJsonObject> get_trade_book(const BrokerCredentials& creds) override;
    ApiResponse<QVector<BrokerPosition>> get_positions(const BrokerCredentials& creds) override;
    ApiResponse<QVector<BrokerHolding>> get_holdings(const BrokerCredentials& creds) override;
    ApiResponse<BrokerFunds> get_funds(const BrokerCredentials& creds) override;

    // Market data
    ApiResponse<QVector<BrokerQuote>> get_quotes(const BrokerCredentials& creds,
                                                 const QVector<QString>& symbols) override;
    ApiResponse<QVector<BrokerCandle>> get_history(const BrokerCredentials& creds,
                                                   const QString& symbol,
                                                   const QString& resolution,
                                                   const QString& from_date,
                                                   const QString& to_date) override;

  protected:
    QMap<QString, QString> auth_headers(const BrokerCredentials& creds) const override;
};

} // namespace fincept::trading

Implement the corresponding .cpp file following the patterns in UpstoxBroker.cpp. Use BrokerHttp::instance() for all network operations, and implement checked_error and is_token_expired helpers to handle API errors consistently.

3. Register the Broker in BrokerRegistry

After implementing the broker class, you must register it so the application can discover and instantiate it. Open fincept-qt/src/trading/BrokerRegistry.cpp and modify the register_all() method.

First, include your broker header:

#include "trading/brokers/<yourbroker>/<YourBroker>.h"

Then add the registration inside register_all():

brokers_["<yourbroker>"] = std::make_unique<<YourBroker>>();

The string key "<yourbroker>" must exactly match the id field returned by your BrokerProfile::id method. This key is used throughout the UI and when fetching instrument data.

4. Implement Instrument Download and Parsing (Optional)

If your broker provides an instrument master file (CSV, JSON, or proprietary format), you should extend InstrumentService to download and parse it. This enables symbol search and validation in the UI.

4.1 Add Download Logic

Locate fincept-qt/src/trading/instruments/InstrumentService.cpp and find the do_refresh() method. Add a branch for your broker:

} else if (broker_id == "<yourbroker>") {
    payload = download_<yourbroker>_instruments(creds);
}

Implement download_<yourbroker>_instruments as a static method or private function within InstrumentService. Use BrokerHttp for authenticated endpoints, or create a dedicated QNetworkAccessManager for large public files to avoid blocking the shared HTTP client.

4.2 Parse the Payload

Create a parser class (e.g., <YourBroker>InstrumentParser) that converts the raw payload into QVector<Instrument>. The Instrument struct is defined in fincept-qt/src/trading/instruments/InstrumentTypes.h and requires these fields:

  • symbol – The standard trading symbol
  • exchange – Exchange code (e.g., "NSE", "BSE")
  • instrument_token – Broker-specific unique identifier
  • brsymbol – Broker-formatted symbol
  • brexchange – Broker exchange code
  • broker_id – Your broker ID string

Integrate the parser in InstrumentService::do_refresh():

} else if (broker_id == "<yourbroker>") {
    instruments = <YourBroker>InstrumentParser::parse(payload);
}

If your broker does not provide an instrument list, skip this step. The UI will allow users to manually enter symbols.

5. Build and Test Your Integration

After implementing the code changes, you must rebuild the application and verify functionality.

5.1 Rebuild the Project

Fincept Terminal uses CMake presets. Run the following from the repository root:

cmake --preset default
cmake --build --preset default

5.2 Unit Testing

Locate existing broker tests under fincept-qt/tests/ and create a new test case for your broker. Mock HTTP responses using the BrokerHttp testing utilities to verify:

  • Token exchange parsing
  • Order placement request formatting
  • Error handling for expired tokens

5.3 Manual UI Testing

Launch the application and complete the following checklist:

  1. Verify your broker appears in the "Add Broker" dialog
  2. Enter test credentials and confirm successful authentication
  3. Test instrument refresh (if implemented) or manual symbol entry
  4. Place a test order and verify it appears in the order book
  5. Check that positions and funds display correctly

6. Complete Example: Minimal DemoBroker

Below is a minimal, functional implementation that demonstrates the required overrides without making external API calls. This is useful for prototyping or creating a paper-trading broker.

// fincept-qt/src/trading/brokers/demo/DemoBroker.h
#pragma once
#include "trading/BrokerInterface.h"
#include "trading/brokers/BrokerHttp.h"

namespace fincept::trading {

class DemoBroker : public IBroker {
  public:
    BrokerId id() const override { return BrokerId::Demo; }
    const char* name() const override { return "DemoBroker"; }
    const char* base_url() const override { return "https://api.demo.com/v1"; }

    BrokerProfile profile() const override {
        return BrokerProfile{
            .id = "demo",
            .display_name = "Demo Broker",
            .region = "IN",
            .currency = "INR",
            .credential_fields = {
                {CredentialField::ApiKey, "API KEY", "Enter API Key...", false},
                {CredentialField::ApiSecret, "API SECRET", "Enter API Secret...", true},
            },
            .exchanges = {"NSE"},
            .product_types = {
                {"Intraday (I)", ProductType::Intraday},
                {"Delivery (D)", ProductType::Delivery},
            },
            .supports_intraday = true,
            .has_native_paper = false,
            .default_watchlist = {"RELIANCE"},
            .default_symbol = "RELIANCE",
            .default_exchange = "NSE",
        };
    }

    TokenExchangeResponse exchange_token(const QString&, const QString&, const QString&) override {
        return {true, "demo-access-token", "", "demo-user", "", ""};
    }

    OrderPlaceResponse place_order(const BrokerCredentials&, const UnifiedOrder&) override {
        return {true, "DEMO12345", ""};
    }

    QMap<QString, QString> auth_headers(const BrokerCredentials& creds) const override {
        return {{"Authorization", "Bearer " + creds.access_token}};
    }
};

} // namespace fincept::trading

Register this demo broker in fincept-qt/src/trading/BrokerRegistry.cpp:

#include "trading/brokers/demo/DemoBroker.h"
// ...
brokers_["demo"] = std::make_unique<DemoBroker>();

7. Key Source Files to Reference

When implementing your broker, consult these files in the Fincept-Corporation/FinceptTerminal repository:

File Purpose
fincept-qt/src/trading/BrokerInterface.h Abstract broker contract defining all virtual methods and data structures
fincept-qt/src/trading/BrokerRegistry.cpp Central registry where broker instances are created and stored
fincept-qt/src/trading/brokers/upstox/UpstoxBroker.h and .cpp Full-featured reference implementation showing authentication, orders, and market data
fincept-qt/src/trading/brokers/zerodha/ZerodhaBroker.cpp Example of token expiry handling and CSV instrument download
fincept-qt/src/trading/instruments/InstrumentService.cpp Instrument download, parsing, caching, and database persistence logic
fincept-qt/src/trading/instruments/InstrumentTypes.h Data model for instruments including symbol, instrument_token, and broker_id

Summary

  • Subclass IBroker and implement identity (id, name, base_url), UI profile (profile), authentication (exchange_token), order lifecycle (place_order, modify_order, cancel_order), portfolio queries (get_orders, get_positions, get_holdings, get_funds), and market data (get_quotes, get_history).
  • Use BrokerHttp for all network requests and follow existing error-handling patterns using checked_error and is_token_expired helpers.
  • Register your broker in BrokerRegistry::register_all() by adding a std::make_unique call with a string key matching your BrokerProfile::id.
  • Extend InstrumentService if your broker provides a symbol master, implementing a download function and parser that populates the Instrument struct defined in InstrumentTypes.h.
  • Build with CMake presets and verify functionality through unit tests and manual UI validation.

Frequently Asked Questions

Do I need to modify the UI code to add a new broker integration to Fincept Terminal?

No. The Fincept Terminal UI dynamically populates broker lists, credential forms, and exchange selectors based on the BrokerProfile struct returned by your IBroker implementation. As long as you correctly populate the credential_fields, exchanges, and product_types vectors in profile(), the UI will render appropriate input fields and dropdowns automatically.

How do I handle OAuth 2.0 authentication flows?

Implement the exchange_token() method to exchange the temporary authorization code for an access token. Use BrokerHttp::instance() to POST to the broker’s token endpoint, passing the api_key and api_secret as headers or body parameters depending on the broker’s specification. Return a TokenExchangeResponse containing the access_token, refresh_token (if applicable), and user identifier. Store sensitive tokens using the SecureStorage helpers available in IBroker.

What if my broker doesn't provide an instrument list API?

If the broker does not expose a downloadable symbol master, you can skip the InstrumentService extension. The Fincept Terminal UI allows users to manually type symbols. Simply ensure your profile() method returns an empty default_watchlist or populates it with commonly traded symbols as hints. When get_quotes() or place_order() receives a symbol string, validate it against the broker’s API directly rather than a local cache.

How do I test my broker implementation without a live account?

Create a “DemoBroker” implementation that inherits IBroker but returns hardcoded responses instead of making HTTP calls. Override exchange_token() to return a fake token, place_order() to return a dummy order ID like "DEMO12345", and market data methods to return static quote structures. Register this implementation in BrokerRegistry under a "demo" key. This allows you to validate the UI flow, order ticket formatting, and position tracking without network latency or real credentials.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →