New: User-pluggable strategy interface for replay/backtest via the ReplayStrategy trait. Implement your own strategy struct and pass it to the replay engine for rapid testing.
pub struct MyStrategy { /* fields */ }
impl ReplayStrategy for MyStrategy {
fn on_candle(&mut self, candle: &Candle) -> Option<BreakoutSignal> {
// Your logic here
None
}
}See the main crate README for usage details.
Real gRPC + protobuf trading SDK in Rust with a broker-forwarding adapter server.
- Symbol metadata registry (
contract_size, min/max lot, step) - Lot-size-only volume quoting (
symbol + lot_size -> volume) - Auth flow (
register_user,login,logout) - Account flow (
fetch_balance,fetch_open_positions) - Trading flow (
place_market_order) - Protobuf contract and generated tonic server/client types
- Async SDK client wrapper (
OpenApiSdkClient)
backtest: local in-memory engine + gRPC service for simulation/replay flowslive: broker-forwarding gRPC adapter (BrokerGrpcAdapterServer)demo: Spotware demo adapter (SpotwareGrpcAdapterServer); implieslivefull: enables all modes above (default)
Build examples:
# backtest only
cargo check --no-default-features --features backtest
# live broker-proxy only
cargo check --no-default-features --features live
# spotware demo adapter (includes live)
cargo check --no-default-features --features demo
# all modes (default)
cargo check --features fullproto/openapi.proto: protobuf contractbuild.rs: tonic/prost code generation at build timesrc/lib.rs: domain engine, service logic, gRPC server adapter, SDK clientexamples/grpc_server.rs: runnable local serverexamples/grpc_client.rs: runnable local client
- Rust stable (edition 2024)
- Cargo
- Run tests:
cargo test- Start broker adapter server:
BROKER_OPENAPI_GRPC_URL=http://<broker-host>:<broker-port> cargo run --example grpc_server- In another terminal, run SDK client:
OPENAPI_GRPC_URL=http://127.0.0.1:50051 cargo run --example grpc_clientYou should see quote, order execution, open positions, and balance output.
use openapi_rs::{OpenApiSdkClient, OrderSide};
use rust_decimal::Decimal;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let grpc_url = std::env::var("OPENAPI_GRPC_URL")?;
let mut client = OpenApiSdkClient::connect(grpc_url).await?;
let token = client.login("alice", "secret").await?;
let quote = client.quote_volume("EURUSD", Decimal::new(15, 2)).await?;
println!("quoted volume: {}", quote.volume);
let execution = client
.place_market_order(token.clone(), "EURUSD", Decimal::new(1, 2), OrderSide::Sell)
.await?;
println!("order id: {}", execution.order_id);
let positions = client.fetch_open_positions(token.clone()).await?;
println!("open positions: {}", positions.len());
let balance = client.fetch_balance(token.clone()).await?;
println!("free margin: {}", balance.free_margin);
client.logout(token).await?;
Ok(())
}OpenApiEngine::register_symbolOpenApiEngine::volume_from_lot_sizeOpenApiEngine::quote_volume_proto
OpenApiService::register_userOpenApiService::loginOpenApiService::logoutOpenApiService::fetch_balanceOpenApiService::fetch_open_positionsOpenApiService::place_market_order
- Server adapter:
OpenApiGrpcServer - Broker proxy adapter:
BrokerGrpcAdapterServer - Generated tonic service trait:
pb::open_api_service_server::OpenApiService - SDK client wrapper:
OpenApiSdkClient
For each symbol, configure:
contract_sizemin_lotmax_lotlot_step
Validation rules:
lot_size > 0min_lot <= lot_size <= max_lot(lot_size - min_lot) % lot_step == 0
If symbol is missing, API returns not-found.
- Proto file:
proto/openapi.proto - Decimal numbers are represented as strings in protobuf messages
- Order side values:
ORDER_SIDE_UNSPECIFIEDORDER_SIDE_BUYORDER_SIDE_SELL
Domain errors map to gRPC status codes in server adapter:
SymbolNotFound->not_foundInvalidCredentials,SessionNotFound->unauthenticatedUserAlreadyExists,AccountAlreadyExists->already_existsInsufficientMargin->failed_precondition- validation failures ->
invalid_argument
Build Rust API docs:
cargo doc --no-deps --open- Ensure your crate name is available:
cargo search openapi-rs-sdk --limit 5- Package validation:
cargo package
cargo publish --dry-run- Login and publish:
cargo login <CRATES_IO_TOKEN>
cargo publish- Release updates:
- bump version in
Cargo.toml - tag in git (recommended)
- run
cargo publishagain
After publishing, in another Rust project:
cargo add openapi-rs-sdkor in Cargo.toml:
[dependencies]
openapi-rs-sdk = "0.1"[dependencies]
openapi-rs-sdk = { git = "https://github.com/elsadeny/openapi-rs", tag = "v0.1.0" }You can also pin a commit:
[dependencies]
openapi-rs-sdk = { git = "https://github.com/elsadeny/openapi-rs", rev = "<commit-sha>" }
Import in Rust code:
```rust
use openapi_rs::{OpenApiSdkClient, OrderSide};
## Production Notes
- `examples/grpc_server.rs` is a broker-forwarding adapter and does not run local simulator state
- Set `BROKER_OPENAPI_GRPC_URL` to your broker OpenAPI gRPC endpoint
- Set your bot/client `OPENAPI_GRPC_URL` to the adapter endpoint
## Tiny Broker Smoke Test
Run the live smoke test (ignored by default) against your broker-connected adapter:
```bash
OPENAPI_GRPC_URL=http://127.0.0.1:50051 \
OPENAPI_USERNAME=<broker-user> \
OPENAPI_PASSWORD=<broker-pass> \
OPENAPI_SMOKE_SYMBOL=EURUSD \
cargo test --test broker_smoke -- --ignored --nocapture
The smoke flow performs:
- login
- place market order (
0.01sell) - fetch balance
- fetch open positions
For broker platform confirmation, match the returned order_id/position on your broker UI or terminal.