Skip to content

Commit

Permalink
Merge pull request #227 from stoqey/r103201
Browse files Browse the repository at this point in the history
Upgrade to API 10.32.01
rylorin authored Nov 8, 2024
2 parents 77e4694 + 54df66f commit 3605ed9
Showing 42 changed files with 1,302 additions and 415 deletions.
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
@@ -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.

18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
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;
@@ -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;
}
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;
@@ -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(); }
@@ -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; }
@@ -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();
@@ -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();
}

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;
@@ -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

@@ -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
}

118 changes: 106 additions & 12 deletions ref/client/EClient.java
Original file line number Diff line number Diff line change
@@ -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
@@ -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())) {
@@ -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;
}


@@ -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
@@ -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) {
@@ -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();
@@ -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();
@@ -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);
}
@@ -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());
@@ -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));
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;
@@ -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() {
}
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;
@@ -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);

@@ -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();
}

Loading

0 comments on commit 3605ed9

Please sign in to comment.