Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contract::futures does not return a valid contract for historical data requests. #102

Open
BrettForr opened this issue Aug 1, 2023 · 1 comment

Comments

@BrettForr
Copy link

Summary

To retrieve historical data for futures contracts, the Contract fields for exchange and last_trade_date_or_contract_month need to be filled in, but Contract::futures(symbol) only accepts one argument for the symbol, so the exchange and last_trade_date_or_contract_month fields use the defaults empty strings.

A futures contract without an exchange and a contract month is a valid contract for a contract_details request. Based on my limited testing, running a contract_details request without an exchange or contract month returns a Iterator of all active contracts, whereas running the same request with a futures contract with the exchange and contract month specified returns and Iterator with just the details for that single contract. Each version has it's use case, so would be good to support both.

I didn't do thorough testing of what other requests work for a futures Contract without an exchange or contract month. At a glance, it looks like requesting the head_timestamp returns a value but it appears to be nonsense. I'm seeing 1970-01-01 for a contract that first existed just a couple years ago.

Steps to reproduce

Running the following program

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

    let symbol = "ES";
    let contract = Contract::futures(symbol);

    let start_date = Some(datetime!(2023-07-26 16:00 UTC));

    let bid_ask_ticks = client
        .historical_ticks_bid_ask(&contract, start_date, None, 1000, true, false)
        .expect("Historical ticks request failed");

    println!("Here are the bids and asks");

    for tick in bid_ask_ticks {
        let time = tick.timestamp;
        let bid = tick.price_bid;
        let ask = tick.price_ask;
        println!("{time} | ${bid} | ${ask}");
    }
}

Returns the following (i.e. the ticks returned is an empty Iterator):

Finished dev [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/rust_ib_data`
Here are the bids and asks
[2104] Market data farm connection is OK:usfarm
[2106] HMDS data farm connection is OK:ushmds
[2158] Sec-def data farm connection is OK:secdefnj

The log from IB Gateway says:

22:36:57:756 -> ---54-2--1-2104-Market data farm connection is OK:usfarm-
22:36:57:756 -> ---34-2--1-2106-HMDS data farm connection is OK:ushmds-
22:36:57:756 -> ---84-2--1-2158-Sec-def data farm connection is OK:secdefnj-
22:36:57:799 <- 96-9000-0-ES-FUT--0-----USD---0-20230726 16:00:00 UTC--1000-BID_ASK-1-0--
22:36:57:799 -> ---Q4-2-9000-321-Error validating request.-'bV' : cause - Exchange must not be empty-

Environment

I'm using IB Gateway 10.19. These are my dependencies:

[dependencies]
time = "0.3.23"
ibapi = "0.3.0"

Working version

Switching the futures function to this results in a valid contract for historical data requests:

    /// Creates futures contract from specified symbol
    /// The contract's last trading day or contract month (for Options and Futures).
    /// Strings with format YYYYMM will be interpreted as the Contract Month whereas YYYYMMDD will be interpreted as Last Trading Day.
    pub fn futures(symbol: &str, exchange: &str, last_trade_date_or_contract_month: &str) -> Contract {
        Contract {
            symbol: symbol.to_string(),
            security_type: SecurityType::Future,
            currency: "USD".to_string(),
            exchange: exchange.to_string(),
            last_trade_date_or_contract_month: last_trade_date_or_contract_month.to_string(),
            include_expired: true,
            ..Default::default()
        }
    }

Running this program

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

    let symbol = "ES";
    let exchange = "CME";
    let expiry = "202309";
    let contract = Contract::futures(symbol, exchange, expiry);

    let start_date = Some(datetime!(2023-07-26 16:00 UTC));

    let bid_ask_ticks = client
        .historical_ticks_bid_ask(&contract, start_date, None, 1000, true, false)
        .expect("Historical ticks request failed");

    println!("Here are the bids and asks");

    for tick in bid_ask_ticks {
        let time = tick.timestamp;
        let bid = tick.price_bid;
        let ask = tick.price_ask;
        println!("{time} | ${bid} | ${ask}");
    }
}

results in the expected output:

Here are the bids and asks
[2104] Market data farm connection is OK:usfarm
[2106] HMDS data farm connection is OK:ushmds
[2158] Sec-def data farm connection is OK:secdefnj
2023-07-26 15:59:59.0 +00:00:00 | $4589.75 | $4590
2023-07-26 16:00:00.0 +00:00:00 | $4589.75 | $4590
2023-07-26 16:00:00.0 +00:00:00 | $4589.75 | $4590
...

Proposal

Since there's a valid use for both a futures product (retrieving contract details for all active contracts) as well as an individual futures contract (retrieving historical data or contract details for a single contract), I think it makes sense to keep the current contstructor and add another Contract constructor function for an individual futures contract with an exchange and contract month. Also, I think include_expired be set to true for the individual contract constructor and perhaps be an argument for the current constructor.

I can put together a pull request for this.

@wboayue
Copy link
Owner

wboayue commented Aug 1, 2023

There are several options here:

  1. Use the constructor as a starting point. (original design choice)
    let symbol = "ES";
    let exchange = "CME";
    let expiry = "202309";

    let mut contract = Contract::futures(symbol);
    contract.expiry = expiry
    contract.exchange = exchange
  1. Provide multiple constructors. Since Rust doesn't support method overloading or default arguments, I think this gets clunky.
    let symbol = "ES";
    let exchange = "CME";
    let expiry = "202309";

    let contract1 = Contract::futures(symbol);
    let contract2 = Contract::futures_with_exchange(symbol, exchange);
  1. Provide a builder for contracts. This is probably the most flexible but will take the most time to implement.
    let symbol = "ES";
    let exchange = "CME";
    let expiry = "202309";

    let contract = ContractBuilder::futures(symbol)
                                .with_exchange(exchange)
                                .with_expiry(expiry)
                                .build()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants