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

Upgrade to API 10.32.01 #227

Merged
merged 2 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions API_VersionNum.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_Version=10.32.01
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</div>
</div>

`@stoqey/ib` is an [Interactive Brokers](http://interactivebrokers.com/) TWS (or IB Gateway) Typescript API client library for [Node.js](http://nodejs.org/). It is a port of Interactive Brokers' Java Client Version 10.29.01 ("latest") from June 18th, 2024.
`@stoqey/ib` is an [Interactive Brokers](http://interactivebrokers.com/) TWS (or IB Gateway) Typescript API client library for [Node.js](http://nodejs.org/). It is a port of Interactive Brokers' Java Client Version 10.32.01 ("latest" relased on Oct 9, 2024).

Refer to the [Trader Workstation API](https://interactivebrokers.github.io/tws-api/) for the official documentation and the C#/Java/VB/C++/Python client.

Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "^18.19.41",
"@types/jest": "^29.5.14",
"@types/node": "^18.19.64",
"@types/source-map-support": "^0.5.10",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"ajv": "^8.17.1",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
"eslint": "^8.57.1",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-rxjs": "^5.0.3",
"jest": "^29.7.0",
"jest-environment-node": "^29.7.0",
"jest-junit": "^16.0.0",
"ts-jest": "^29.2.3",
"typedoc": "^0.26.4",
"typescript": "^5.5.3"
"ts-jest": "^29.2.5",
"typedoc": "^0.26.11",
"typescript": "^5.6.3"
},
"engines": {
"node": ">=18.0.0"
Expand Down
4 changes: 2 additions & 2 deletions ref/client/Builder.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
/* Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
* and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */

package com.ib.client;
Expand Down Expand Up @@ -111,7 +111,7 @@ static void intToBytes(int val, byte b[], int position) {
b[position+3] = (byte)(0xff & val);
}

private static boolean isAsciiPrintable(String str) {
static boolean isAsciiPrintable(String str) {
if (str == null) {
return false;
}
Expand Down
9 changes: 7 additions & 2 deletions ref/client/ContractDetails.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
/* Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
* and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */

package com.ib.client;
Expand Down Expand Up @@ -74,6 +74,7 @@ public class ContractDetails {
private String m_fundBlueSkyTerritories;
private FundDistributionPolicyIndicator m_fundDistributionPolicyIndicator;
private FundAssetType m_fundAssetType;
private List<IneligibilityReason> m_ineligibilityReasonList;

// Get
public int conid() { return m_contract.conid(); }
Expand Down Expand Up @@ -139,6 +140,7 @@ public class ContractDetails {
public String fundBlueSkyTerritories() { return m_fundBlueSkyTerritories; }
public FundDistributionPolicyIndicator fundDistributionPolicyIndicator() { return m_fundDistributionPolicyIndicator; }
public FundAssetType fundAssetType() { return m_fundAssetType; }
public List<IneligibilityReason> ineligibilityReasonList() { return m_ineligibilityReasonList; }

// Set
public void contract(Contract contract) { m_contract = contract; }
Expand Down Expand Up @@ -203,6 +205,7 @@ public class ContractDetails {
public void fundBlueSkyTerritories(String fundBlueSkyTerritories) { m_fundBlueSkyTerritories = fundBlueSkyTerritories; }
public void fundDistributionPolicyIndicator(FundDistributionPolicyIndicator fundDistributionPolicyIndicator) { m_fundDistributionPolicyIndicator = fundDistributionPolicyIndicator; }
public void fundAssetType(FundAssetType fundAssetType) { m_fundAssetType = fundAssetType; }
public void ineligibilityReasonList(List<IneligibilityReason> ineligibilityReasonList) { m_ineligibilityReasonList = ineligibilityReasonList; }

public ContractDetails() {
m_contract = new Contract();
Expand Down Expand Up @@ -276,7 +279,9 @@ public ContractDetails() {
add( sb, "fundDistributionPolicyIndicator", m_fundDistributionPolicyIndicator != null ? m_fundDistributionPolicyIndicator.name() : "");
add( sb, "fundAssetType", m_fundAssetType != null ? m_fundAssetType.name() : "");
}


add( sb, "ineligibilityReasonList", EWrapperMsgGenerator.contractDetailsIneligibilityReasonList(this));

return sb.toString();
}

Expand Down
6 changes: 3 additions & 3 deletions ref/client/DefaultEWrapper.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2019 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
/* Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
* and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */

package com.ib.client;
Expand Down Expand Up @@ -54,7 +54,7 @@ public void tickEFP(int tickerId, int tickType, double basisPoints,

@Override
public void orderStatus(int orderId, String status, Decimal filled,
Decimal remaining, double avgFillPrice, int permId, int parentId,
Decimal remaining, double avgFillPrice, long permId, int parentId,
double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
// TODO Auto-generated method stub

Expand Down Expand Up @@ -525,7 +525,7 @@ public void tickByTickMidPoint(int reqId, long time, double midPoint) {
}

@Override
public void orderBound(long orderId, int apiClientId, int apiOrderId) {
public void orderBound(long permId, int clientId, int orderId) {
// TODO Auto-generated method stub
}

Expand Down
118 changes: 106 additions & 12 deletions ref/client/EClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,18 @@ public abstract class EClient {
protected static final int MIN_SERVER_VER_LAST_TRADE_DATE = 182;
protected static final int MIN_SERVER_VER_CUSTOMER_ACCOUNT = 183;
protected static final int MIN_SERVER_VER_PROFESSIONAL_CUSTOMER = 184;
protected static final int MIN_SERVER_VER_BOND_ACCRUED_INTEREST = 185;
protected static final int MIN_SERVER_VER_INELIGIBILITY_REASONS = 186;
protected static final int MIN_SERVER_VER_RFQ_FIELDS = 187;
protected static final int MIN_SERVER_VER_BOND_TRADING_HOURS = 188;
protected static final int MIN_SERVER_VER_INCLUDE_OVERNIGHT = 189;
protected static final int MIN_SERVER_VER_UNDO_RFQ_FIELDS = 190;
protected static final int MIN_SERVER_VER_PERM_ID_AS_LONG = 191;
protected static final int MIN_SERVER_VER_CME_TAGGING_FIELDS = 192;
protected static final int MIN_SERVER_VER_CME_TAGGING_FIELDS_IN_OPEN_ORDER = 193;

public static final int MIN_VERSION = 100; // envelope encoding, applicable to useV100Plus mode only
public static final int MAX_VERSION = MIN_SERVER_VER_PROFESSIONAL_CUSTOMER; // ditto
public static final int MAX_VERSION = MIN_SERVER_VER_CME_TAGGING_FIELDS_IN_OPEN_ORDER; // ditto

protected EReaderSignal m_signal;
protected EWrapper m_eWrapper; // msg handler
Expand Down Expand Up @@ -1602,8 +1611,9 @@ public synchronized void placeOrder( int id, Contract contract, Order order) {
}

if (m_serverVersion < MIN_SERVER_VER_ALGO_ID && !IsEmpty(order.algoId()) ) {
error(id, EClientErrors.UPDATE_TWS, " It does not support algoId parameter");
}
error(id, EClientErrors.UPDATE_TWS, " It does not support algoId parameter");
return;
}

if (m_serverVersion < MIN_SERVER_VER_SCALE_TABLE) {
if (!IsEmpty(order.scaleTable()) || !IsEmpty(order.activeStartTime()) || !IsEmpty(order.activeStopTime())) {
Expand All @@ -1630,12 +1640,14 @@ public synchronized void placeOrder( int id, Contract contract, Order order) {
}

if (m_serverVersion < MIN_SERVER_VER_EXT_OPERATOR && !IsEmpty(order.extOperator()) ) {
error(id, EClientErrors.UPDATE_TWS, " It does not support ext operator");
error(id, EClientErrors.UPDATE_TWS, " It does not support ext operator");
return;
}

if (m_serverVersion < MIN_SERVER_VER_SOFT_DOLLAR_TIER &&
(!IsEmpty(order.softDollarTier().name()) || !IsEmpty(order.softDollarTier().value()))) {
error(id, EClientErrors.UPDATE_TWS, " It does not support soft dollar tier");
(!IsEmpty(order.softDollarTier().name()) || !IsEmpty(order.softDollarTier().value()))) {
error(id, EClientErrors.UPDATE_TWS, " It does not support soft dollar tier");
return;
}


Expand Down Expand Up @@ -1749,6 +1761,20 @@ public synchronized void placeOrder( int id, Contract contract, Order order) {
}
}

if (m_serverVersion < MIN_SERVER_VER_INCLUDE_OVERNIGHT) {
if (order.includeOvernight()) {
error(id, EClientErrors.UPDATE_TWS, " It does not support include overnight parameter");
return;
}
}

if (m_serverVersion < MIN_SERVER_VER_CME_TAGGING_FIELDS) {
if (order.manualOrderIndicator() != Integer.MAX_VALUE) {
error(id, EClientErrors.UPDATE_TWS, " It does not support manual order indicator parameter");
return;
}
}

int VERSION = (m_serverVersion < MIN_SERVER_VER_NOT_HELD) ? 27 : 45;

// send place order msg
Expand Down Expand Up @@ -2223,6 +2249,19 @@ public synchronized void placeOrder( int id, Contract contract, Order order) {
b.send(order.professionalCustomer());
}

if (m_serverVersion >= MIN_SERVER_VER_RFQ_FIELDS && m_serverVersion < MIN_SERVER_VER_UNDO_RFQ_FIELDS) {
b.send("");
b.send(Integer.MAX_VALUE);
}

if (m_serverVersion >= MIN_SERVER_VER_INCLUDE_OVERNIGHT) {
b.send(order.includeOvernight());
}

if (m_serverVersion >= MIN_SERVER_VER_CME_TAGGING_FIELDS) {
b.send(order.manualOrderIndicator());
}

closeAndSend(b);
}
catch(EClientException e) {
Expand Down Expand Up @@ -2309,36 +2348,59 @@ public synchronized void reqExecutions(int reqId, ExecutionFilter filter) {
}
}

public synchronized void cancelOrder( int id, String manualOrderCancelTime) {
public synchronized void cancelOrder( int id, OrderCancel orderCancel) {
// not connected?
if( !isConnected()) {
notConnected();
return;
}

if (m_serverVersion < MIN_SERVER_VER_MANUAL_ORDER_TIME) {
if (!IsEmpty(manualOrderCancelTime)) {
if (!IsEmpty(orderCancel.manualOrderCancelTime())) {
error(id, EClientErrors.UPDATE_TWS, " It does not support manual order cancel time attribute");
return;
}
}

if (m_serverVersion < MIN_SERVER_VER_CME_TAGGING_FIELDS) {
if (!IsEmpty(orderCancel.extOperator()) || orderCancel.manualOrderIndicator() != Integer.MAX_VALUE) {
error(id, EClientErrors.UPDATE_TWS, " It does not support ext operator and manual order indicator parameters");
return;
}
}

final int VERSION = 1;

// send cancel order msg
try {
Builder b = prepareBuffer();

b.send( CANCEL_ORDER);
b.send( VERSION);
if (m_serverVersion < MIN_SERVER_VER_CME_TAGGING_FIELDS) {
b.send( VERSION);
}
b.send( id);

if (m_serverVersion >= MIN_SERVER_VER_MANUAL_ORDER_TIME) {
b.send(manualOrderCancelTime);
b.send(orderCancel.manualOrderCancelTime());
}

if (m_serverVersion >= MIN_SERVER_VER_RFQ_FIELDS && m_serverVersion < MIN_SERVER_VER_UNDO_RFQ_FIELDS) {
b.send("");
b.send("");
b.send(Integer.MAX_VALUE);
}

if (m_serverVersion >= MIN_SERVER_VER_CME_TAGGING_FIELDS) {
b.send(orderCancel.extOperator());
b.send(orderCancel.manualOrderIndicator());
}

closeAndSend(b);
}
catch( EClientException e) {
error( id, e.error(), e.text());
}
catch( Exception e) {
error( id, EClientErrors.FAIL_SEND_CORDER, e.toString());
close();
Expand Down Expand Up @@ -2941,7 +3003,7 @@ public synchronized void cancelCalculateOptionPrice(int reqId) {
}
}

public synchronized void reqGlobalCancel() {
public synchronized void reqGlobalCancel(OrderCancel orderCancel) {
// not connected?
if( !isConnected()) {
notConnected();
Expand All @@ -2954,14 +3016,28 @@ public synchronized void reqGlobalCancel() {
return;
}

if (m_serverVersion < MIN_SERVER_VER_CME_TAGGING_FIELDS) {
if (!IsEmpty(orderCancel.extOperator()) || orderCancel.manualOrderIndicator() != Integer.MAX_VALUE) {
error(EClientErrors.NO_VALID_ID, EClientErrors.UPDATE_TWS, " It does not support ext operator and manual order indicator parameters");
return;
}
}

final int VERSION = 1;

// send request global cancel msg
try {
Builder b = prepareBuffer();

b.send( REQ_GLOBAL_CANCEL);
b.send( VERSION);
if (m_serverVersion < MIN_SERVER_VER_CME_TAGGING_FIELDS) {
b.send( VERSION);
}

if (m_serverVersion >= MIN_SERVER_VER_CME_TAGGING_FIELDS) {
b.send(orderCancel.extOperator());
b.send(orderCancel.manualOrderIndicator());
}

closeAndSend(b);
}
Expand Down Expand Up @@ -4231,6 +4307,9 @@ public synchronized void reqWshEventData(int reqId, WshEventData wshEventData) {

closeAndSend(b);
}
catch( EClientException e) {
error( reqId, e.error(), e.text());
}
catch( Exception e) {
error( EClientErrors.NO_VALID_ID,
EClientErrors.FAIL_SEND_REQ_WSH_EVENT_DATA, e.toString());
Expand Down Expand Up @@ -4318,6 +4397,21 @@ protected void error(int id, EClientErrors.CodeMsgPair pair, String tail) {

protected abstract void closeAndSend(Builder buf) throws IOException;


protected void validateInvalidSymbols(String host) throws EClientException {
if (host != null && !Builder.isAsciiPrintable(host)) {
throw new EClientException(EClientErrors.INVALID_SYMBOL, host);
}

if (m_connectOptions != null && !Builder.isAsciiPrintable(m_connectOptions)) {
throw new EClientException(EClientErrors.INVALID_SYMBOL, m_connectOptions);
}

if (m_optionalCapabilities != null && !Builder.isAsciiPrintable(m_optionalCapabilities)) {
throw new EClientException(EClientErrors.INVALID_SYMBOL, m_optionalCapabilities);
}
}

private void sendV100APIHeader() throws IOException {
try (Builder builder = new Builder(1024)) {
builder.send("API\0".getBytes(StandardCharsets.UTF_8));
Expand Down
3 changes: 2 additions & 1 deletion ref/client/EClientErrors.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
/* Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
* and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */

package com.ib.client;
Expand Down Expand Up @@ -94,6 +94,7 @@ public class EClientErrors {
static final CodeMsgPair FAIL_SEND_CAN_WSH_EVENT_DATA = new CodeMsgPair(583, "Cancel WSH Event Data Sending Error - ");
static final CodeMsgPair FAIL_SEND_REQ_USER_INFO = new CodeMsgPair(584, "Request User Info Sending Error - ");
static final CodeMsgPair FA_PROFILE_NOT_SUPPORTED = new CodeMsgPair(585, "FA Profile is not supported anymore, use FA Group instead - ");
static final CodeMsgPair FAIL_READ_MESSAGE = new CodeMsgPair(586, "Failed to read message because not connected");

public EClientErrors() {
}
Expand Down
16 changes: 15 additions & 1 deletion ref/client/EClientSocket.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2019 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
/* Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
* and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */

package com.ib.client;
Expand Down Expand Up @@ -84,6 +84,14 @@ public synchronized void eConnect( String host, int port, int clientId) {
}

public synchronized void eConnect( String host, int port, int clientId, boolean extraAuth) {
try {
validateInvalidSymbols(host);
}
catch(EClientException e) {
error(EClientErrors.NO_VALID_ID, e.error(), e.text());
return;
}

// already connected?
m_host = checkConnected(host);

Expand Down Expand Up @@ -229,10 +237,16 @@ private synchronized void eDisconnect( boolean resetState ) {
}

public int read(byte[] buf, int off, int len) throws IOException {
if (m_dis == null) {
throw new EClientException(EClientErrors.FAIL_READ_MESSAGE, "");
}
return m_dis.read(buf, off, len);
}

public int readInt() throws IOException {
if (m_dis == null) {
throw new EClientException(EClientErrors.FAIL_READ_MESSAGE, "");
}
return m_dis.readInt();
}

Expand Down
Loading