https://github.com/pooh555/webull-api
A C++ SDK for Webull OpenAPI
https://github.com/pooh555/webull-api
cpp cpp-library trading-deployment-kit webull webull-api
Last synced: about 7 hours ago
JSON representation
A C++ SDK for Webull OpenAPI
- Host: GitHub
- URL: https://github.com/pooh555/webull-api
- Owner: Pooh555
- License: mit
- Created: 2026-06-17T14:14:09.000Z (7 days ago)
- Default Branch: main
- Last Pushed: 2026-06-23T09:22:14.000Z (1 day ago)
- Last Synced: 2026-06-23T12:58:05.581Z (1 day ago)
- Topics: cpp, cpp-library, trading-deployment-kit, webull, webull-api
- Language: C++
- Homepage: https://github.com/Pooh555/Webull-SDK-CPP
- Size: 116 MB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# WDK — Webull Developer Kit
### A Modern C++23 Client for the Webull OpenAPI
**Asynchronous · Thread-Safe · Header-Light · Cryptographically Signed**
Built on `libcurl` · `nlohmann/json` · `OpenSSL` · `spdlog`
[](https://en.cppreference.com/w/cpp/23)
[](https://cmake.org/)
[](#license)
---
## Overview
WDK (**Webull Developer Kit**) is a native C++23 client library for the Webull OpenAPI. It provides fully typed, asynchronous access to market data and trading operations through a three-layer architecture that separates transport, signing, and business logic.
The library was designed with the following principles:
- **Zero dynamic dispatch in the hot path.** All client objects are instantiated by the caller; no virtual tables are used in the core I/O path.
- **Cooperative ownership.** All heavyweight resources (`CurlPool`, `ThreadPool`, `Token`) are managed through `std::unique_ptr` by the consuming application. Clients receive non-owning references.
- **Symmetric async/sync surface.** Every client method has both a blocking synchronous variant and a `std::future`-returning asynchronous variant. The caller selects the scheduling model.
- **Request signing encapsulated from business logic.** HMAC-SHA1 signing, nonce generation, and UTC timestamping are performed inside `wdk::utilities::execute_request` and are never visible to the calling code.
## Documentation
Visit the full documentation here: [Documentation](docs/documentation.md)
Visit the documentation for LLMs here: [Documentation for AI Agents](docs/llms.txt)
## Prerequisites
### Compiler
| Requirement | Minimum Version |
|---|---|
| GCC | 13.0 |
| Clang | 17.0 |
| C++ Standard | C++23 (`-std=c++23`) |
### Build Tools
| Tool | Purpose |
|---|---|
| CMake | Build system generator |
| Ninja | Recommended build backend |
### Runtime Dependencies
| Library | Purpose | Notes |
|---|---|---|
| `libcurl` | HTTP transport | With HTTPS / HTTP/2 support |
| `OpenSSL` | HMAC-SHA1, MD5, Base64 | libcrypto is used |
| `spdlog` | Structured logging | Header-only mode supported |
| `nlohmann/json` | JSON serialization | Bundled under `lib/` |
All dependencies except `nlohmann/json` must be installed system-wide and discoverable by CMake's `find_package`.
On a Debian/Ubuntu system:
```sh
sudo apt install libcurl4-openssl-dev libssl-dev libspdlog-dev cmake ninja-build
```
On Arch Linux:
```sh
sudo pacman -S curl openssl spdlog cmake ninja
```
## Installation
### Quick Start
Cloning the main repository.
### Building from Source
Clone the repository and use the provided build script:
```sh
git clone https://github.com/Pooh555/Webull-SDK.git
cd Webull-SDK
./build.sh
```
The build script accepts the following flags:
| Flag | Description | Default |
|---|---|---|
| `-b, --build-type ` | CMake build type: `Debug`, `Release`, `RelWithDebInfo` | `Debug` |
| `-c, --clean` | Remove the existing build directory before building | Off |
| `-h, --help` | Print the help menu and exit | — |
**Example: release build**
```sh
./build.sh --build-type Release
```
**Example: clean debug build**
```sh
./build.sh --clean
```
Upon successful completion, the following artifacts are produced:
| Artifact | Location |
|---|---|
| Static library | `out/build//libWebull-SDK.a` |
| Demo binary | `examples/bin/Webull-SDK-Demo` |
To execute the bundled demo:
```sh
./run.sh
```
### Integration as a Third-Party Library
WDK exposes a CMake target `Webull::SDK` for downstream consumption.
**Step 1.** Add the repository as a Git submodule:
```sh
git submodule add https://github.com/Pooh555/Webull-SDK.git third-party/Webull-SDK
git submodule update --init --recursive
```
**Step 2.** Configure your project's `CMakeLists.txt`:
```cmake
add_subdirectory(third-party/Webull-SDK)
add_executable(MyApplication
src/main.cpp
)
target_link_libraries(MyApplication
PRIVATE
Webull::SDK
)
```
**Step 3.** Call `cmake --build` as usual. The `Webull::SDK` target propagates all required include directories and compile options transitively.
## Usage Guide
### Initialization
The following objects must be constructed before any client can be used. They are typically held by the application's top-level class.
```cpp
#include
#include
#include
#include
static constexpr std::string_view HOST { "api.webull.co.th" };
static constexpr std::string_view TOKEN_PATH { "examples/res/token.json" };
static constexpr std::string_view CREDENTIALS_PATH { "examples/res/credentials.json" };
// Allocate infrastructure (order matters: pool and credentials before token)
auto curl_pool = std::make_unique(10uz);
auto thread_pool = std::make_unique();
auto credentials = std::make_unique(CREDENTIALS_PATH);
// Token constructor blocks until the session is active.
// The user may need to approve the login in the Webull mobile app.
auto token = std::make_unique(
TOKEN_PATH, *curl_pool, *credentials, HOST
);
```
### Market Data
#### Tick Data
```cpp
wdk::client::MarketClient market_client(
*curl_pool, *thread_pool, *credentials, HOST, token->get_handle()
);
```cpp
std::future future = market_client.fetch_historical_bars_data_async({
.symbol { "AAPL" },
.category { "US_STOCK" },
.timespan { "M5" },
.count { 100uz },
.real_time_required { false },
.trading_sessions { "CORE" }
});
wdk::utilities::Response response = future.get();
if (response.http_code == 200L) {
std::vector history =
wdk::data::convert_response_to_historical_bars_vector(response);
for (const auto& symbol_data : history) {
for (const auto& bar : symbol_data.bars) {
// Access bar.time, bar.open, bar.high, bar.low, bar.close, bar.volume
}
}
}
```
### Trading Operations
> Before placing orders, always call `preview_order` first to validate the request and review estimated costs.
#### Order Lifecycle Example
```cpp
wdk::client::TradingClient trading_client(
*curl_pool, *thread_pool, *credentials, HOST, token->get_handle()
);
const std::string account_id = trading_client.get_account_id();
const std::string client_order_id = wdk::utilities::generate_nonce();
// Step 1: Preview the order
wdk::utilities::Response preview = trading_client.preview_order({
.account_id { account_id },
.combo_type { "NORMAL" },
.client_order_id { client_order_id },
.instrument_type { "EQUITY" },
.market { "US" },
.symbol { "NVDA" },
.order_type { "LIMIT" },
.entrust_type { "QTY" },
.trading_session { "CORE" },
.time_in_force { "DAY" },
.side { "BUY" },
.quantity { 1.0 },
.limit_price { 135.00 },
.stop_price { std::nullopt }
});
// Step 2: Place the order (only if preview confirms acceptable terms)
if (preview.http_code == 200L) {
wdk::utilities::Response placed = trading_client.place_order({
.account_id { account_id },
.combo_type { "NORMAL" },
.client_order_id { client_order_id },
.instrument_type { "EQUITY" },
.market { "US" },
.symbol { "NVDA" },
.order_type { "LIMIT" },
.entrust_type { "QTY" },
.trading_session { "CORE" },
.time_in_force { "DAY" },
.side { "BUY" },
.quantity { 1.0 },
.limit_price { 135.00 },
.stop_price { std::nullopt }
});
}
// Step 3: Modify the order price
wdk::utilities::Response modified = trading_client.modify_order({
.account_id { account_id },
.client_order_id { client_order_id },
.time_in_force { "DAY" },
.quantity { 1.0 },
.limit_price { 134.50 },
.stop_price { std::nullopt }
});
// Step 4: Cancel the order
wdk::utilities::Response cancelled = trading_client.cancel_order({
.account_id { account_id },
.client_order_id { client_order_id }
});
```
---
### Account Management
```cpp
// Fetch account list (and resolve the primary account ID in one call)
const std::string account_id = trading_client.get_account_id();
// Account balance
std::future balance_future =
trading_client.fetch_account_balance_async(account_id);
wdk::utilities::Response balance = balance_future.get();
// Open positions
std::future position_future =
trading_client.fetch_account_position_async(account_id);
wdk::utilities::Response positions = position_future.get();
// Order history (paginated)
std::future history_future =
trading_client.fetch_order_history_async({
.account_id { account_id },
.start_date { "2026-01-01" },
.page_size { 50uz },
.last_client_id { "" }
});
wdk::utilities::Response history = history_future.get();
```
---
_Given Python's dubious appeal and the questionable maintenance of the Java SDK, C++ was an inevitability._