#algo-trading #interactive-brokers #tws

ibapi

A synchronous implementation of the Interactive Brokers TWS API

9 unstable releases (3 breaking)

0.4.1 Sep 15, 2023
0.4.0 Aug 29, 2023
0.3.0 Jul 7, 2023
0.2.3 May 8, 2023
0.1.1 Apr 9, 2023

#21 in Finance

Download history 13/week @ 2023-06-09 15/week @ 2023-06-16 22/week @ 2023-06-23 21/week @ 2023-06-30 33/week @ 2023-07-07 15/week @ 2023-07-14 18/week @ 2023-07-21 18/week @ 2023-07-28 51/week @ 2023-08-04 39/week @ 2023-08-11 52/week @ 2023-08-18 70/week @ 2023-08-25 26/week @ 2023-09-01 38/week @ 2023-09-08 43/week @ 2023-09-15 12/week @ 2023-09-22

124 downloads per month

MIT license

465KB
8K SLoC

Build License:MIT crates.io Documentation Coverage Status

Introduction

An implementation of the Interactive Brokers TWS API for Rust. This implementation is not a direct port of the official TWS API. It provides a synchronous API that simplifies the development of trading strategies.

This is a work in progress and was tested using TWS 10.19. The primary reference for this implementation is the C# source code.

Open issues are tracked here. If you run into a problem or need a missing feature, check the issues list before reporting a new issue.

Contributions are welcome.

Example

The following example gives a flavor of the API style. It is not a trading strategy recommendation and not a complete implementation.

use std::collections::VecDeque;

use ibapi::contracts::Contract;
use ibapi::market_data::realtime::{BarSize, Bar, WhatToShow};
use ibapi::orders::{order_builder, Action, OrderNotification};
use ibapi::Client;

fn main() {
    let client = Client::connect("127.0.0.1:4002", 100).unwrap();

    let symbol = "TSLA";
    let contract = Contract::stock(symbol); // defaults to USD and SMART exchange.

    let bars = client.realtime_bars(&contract, BarSize::Sec5, WhatToShow::Trades, false).unwrap();

    let mut channel = BreakoutChannel::new(30);

    for bar in bars {
        channel.add_bar(&bar);

        // Ensure enough bars and no open positions.
        if !channel.ready() || has_position(&client, symbol) {
            continue;
        }

        let action = if bar.close > channel.high() {
            Action::Buy
        } else if bar.close < channel.low() {
            Action::Sell
        } else {
            continue;
        };

        let order_id = client.next_order_id();
        let order = order_builder::market_order(action, 100.0);

        let notices = client.place_order(order_id, &contract, &order).unwrap();
        for notice in notices {
            if let OrderNotification::ExecutionData(data) = notice {
                println!("{} {} shares of {}", data.execution.side, data.execution.shares, data.contract.symbol);
            } else {
                println!("{:?}", notice);
            }
        }
    }
}

fn has_position(client: &Client, symbol: &str) -> bool {
    if let Ok(mut positions) = client.positions() {
        positions.find(|p| p.contract.symbol == symbol).is_some()
    } else {
        false
    }
}

struct BreakoutChannel {
    ticks: VecDeque<(f64, f64)>,
    size: usize,
}

impl BreakoutChannel {
    fn new(size: usize) -> BreakoutChannel {
        BreakoutChannel {
            ticks: VecDeque::with_capacity(size + 1),
            size,
        }
    }

    fn ready(&self) -> bool {
        self.ticks.len() >= self.size
    }

    fn add_bar(&mut self, bar: &Bar) {
        self.ticks.push_back((bar.high, bar.low));

        if self.ticks.len() > self.size {
            self.ticks.pop_front();
        }
    }

    fn high(&self) -> f64 {
        self.ticks.iter().map(|x| x.0).max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap()
    }

    fn low(&self) -> f64 {
        self.ticks.iter().map(|x| x.1).max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap()
    }
}

Available APIs

Accounts

Contracts

Historical Market Data

Realtime Market Data

Orders

Dependencies

~1.6–2.3MB
~39K SLoC