summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2018-08-25 08:50:35 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2018-08-25 08:50:35 +0200
commit8a2dde1d318ae342ec0505f9d825fe397ffdbc01 (patch)
treedad6d9d0fe9f81e668b20a66b5a9c92f9e4b595d
parent62a60ab511369a998b635d95625ea1e51b623127 (diff)
downloadtwstest-8a2dde1d318ae342ec0505f9d825fe397ffdbc01.tar.gz
twstest-8a2dde1d318ae342ec0505f9d825fe397ffdbc01.tar.bz2
mavenized demo is working
-rwxr-xr-xdemo/demo3
-rwxr-xr-xdemo/pom.xml57
-rwxr-xr-xdemo/src/main/java/apidemo/AccountInfoPanel.java446
-rwxr-xr-xdemo/src/main/java/apidemo/AccountPositionsMultiPanel.java419
-rwxr-xr-xdemo/src/main/java/apidemo/AccountSummaryPanel.java156
-rwxr-xr-xdemo/src/main/java/apidemo/AdjustedPanel.java58
-rwxr-xr-xdemo/src/main/java/apidemo/AdvisorPanel.java362
-rwxr-xr-xdemo/src/main/java/apidemo/ApiDemo.java304
-rwxr-xr-xdemo/src/main/java/apidemo/Chart.java90
-rwxr-xr-xdemo/src/main/java/apidemo/ComboPanel.java620
-rwxr-xr-xdemo/src/main/java/apidemo/ConditionDlg.java260
-rwxr-xr-xdemo/src/main/java/apidemo/ConditionsModel.java76
-rwxr-xr-xdemo/src/main/java/apidemo/ConditionsPanel.java117
-rwxr-xr-xdemo/src/main/java/apidemo/ContractConditionPanel.java23
-rwxr-xr-xdemo/src/main/java/apidemo/ContractDlg.java58
-rwxr-xr-xdemo/src/main/java/apidemo/ContractInfoPanel.java266
-rwxr-xr-xdemo/src/main/java/apidemo/ContractLookupButton.java35
-rwxr-xr-xdemo/src/main/java/apidemo/ContractPanel.java103
-rwxr-xr-xdemo/src/main/java/apidemo/ContractSearchDlg.java146
-rwxr-xr-xdemo/src/main/java/apidemo/ExercisePanel.java118
-rwxr-xr-xdemo/src/main/java/apidemo/FamilyCodesPanel.java122
-rwxr-xr-xdemo/src/main/java/apidemo/Histogram.java57
-rwxr-xr-xdemo/src/main/java/apidemo/HistoricalTickBidAskModel.java60
-rwxr-xr-xdemo/src/main/java/apidemo/HistoricalTickLastModel.java58
-rwxr-xr-xdemo/src/main/java/apidemo/HistoricalTickModel.java54
-rwxr-xr-xdemo/src/main/java/apidemo/HistoricalTickResultsPanel.java94
-rwxr-xr-xdemo/src/main/java/apidemo/MarginConditionPanel.java25
-rwxr-xr-xdemo/src/main/java/apidemo/MarketDataPanel.java1279
-rwxr-xr-xdemo/src/main/java/apidemo/MarketValueSummaryPanel.java74
-rwxr-xr-xdemo/src/main/java/apidemo/MktDepthExchangesPanel.java136
-rwxr-xr-xdemo/src/main/java/apidemo/NewsPanel.java557
-rwxr-xr-xdemo/src/main/java/apidemo/OnOKPanel.java12
-rwxr-xr-xdemo/src/main/java/apidemo/OperatorConditionPanel.java31
-rwxr-xr-xdemo/src/main/java/apidemo/OptParamsModel.java57
-rwxr-xr-xdemo/src/main/java/apidemo/OptionChainsPanel.java310
-rwxr-xr-xdemo/src/main/java/apidemo/OptionsPanel.java17
-rwxr-xr-xdemo/src/main/java/apidemo/OrdersPanel.java288
-rwxr-xr-xdemo/src/main/java/apidemo/PegBenchPanel.java62
-rwxr-xr-xdemo/src/main/java/apidemo/PercentConditionPanel.java27
-rwxr-xr-xdemo/src/main/java/apidemo/PnLModel.java89
-rwxr-xr-xdemo/src/main/java/apidemo/PnLSingleModel.java85
-rwxr-xr-xdemo/src/main/java/apidemo/PositionsPanel.java174
-rwxr-xr-xdemo/src/main/java/apidemo/PriceConditionPanel.java73
-rwxr-xr-xdemo/src/main/java/apidemo/StratPanel.java205
-rwxr-xr-xdemo/src/main/java/apidemo/Test.java431
-rwxr-xr-xdemo/src/main/java/apidemo/TickByTickModel.java125
-rwxr-xr-xdemo/src/main/java/apidemo/TickByTickResultsPanel.java134
-rwxr-xr-xdemo/src/main/java/apidemo/TicketDlg.java730
-rwxr-xr-xdemo/src/main/java/apidemo/TimeConditionPanel.java26
-rwxr-xr-xdemo/src/main/java/apidemo/TopModel.java300
-rwxr-xr-xdemo/src/main/java/apidemo/TradeConditionPanel.java36
-rwxr-xr-xdemo/src/main/java/apidemo/TradesPanel.java136
-rwxr-xr-xdemo/src/main/java/apidemo/TradingPanel.java35
-rwxr-xr-xdemo/src/main/java/apidemo/VolumeConditionPanel.java28
-rwxr-xr-xdemo/src/main/java/apidemo/util/HtmlButton.java137
-rwxr-xr-xdemo/src/main/java/apidemo/util/IConnectionConfiguration.java20
-rwxr-xr-xdemo/src/main/java/apidemo/util/NewLookAndFeel.java108
-rwxr-xr-xdemo/src/main/java/apidemo/util/NewTabbedPanel.java313
-rwxr-xr-xdemo/src/main/java/apidemo/util/TCombo.java23
-rwxr-xr-xdemo/src/main/java/apidemo/util/UpperField.java80
-rwxr-xr-xdemo/src/main/java/apidemo/util/Util.java75
-rwxr-xr-xdemo/src/main/java/apidemo/util/VerticalPanel.java160
-rw-r--r--pom.xml1
63 files changed, 10561 insertions, 0 deletions
diff --git a/demo/demo b/demo/demo
new file mode 100755
index 0000000..fc4655e
--- /dev/null
+++ b/demo/demo
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+java -jar target/tws-demo-9.73.01-SNAPSHOT-jar-with-dependencies.jar $*
diff --git a/demo/pom.xml b/demo/pom.xml
new file mode 100755
index 0000000..75bc1ec
--- /dev/null
+++ b/demo/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.interactivebrokers</groupId>
+ <artifactId>tws-demo</artifactId>
+ <version>9.73.01-SNAPSHOT</version>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <java.source.version>1.8</java.source.version>
+ <java.target.version>1.8</java.target.version>
+ <maven.plugin.compiler.version>3.7.0</maven.plugin.compiler.version>
+ <maven.plugin.assembly.version>3.1.0</maven.plugin.assembly.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${java.source.version}</source>
+ <target>${java.target.version}</target>
+ <compilerArgument>-Xlint:deprecation</compilerArgument>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${maven.plugin.assembly.version}</version>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <archive>
+ <manifest>
+ <mainClass>apidemo.ApiDemo</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>com.interactivebrokers</groupId>
+ <artifactId>tws-api</artifactId>
+ <version>${version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/demo/src/main/java/apidemo/AccountInfoPanel.java b/demo/src/main/java/apidemo/AccountInfoPanel.java
new file mode 100755
index 0000000..9cad9f2
--- /dev/null
+++ b/demo/src/main/java/apidemo/AccountInfoPanel.java
@@ -0,0 +1,446 @@
+/* Copyright (C) 2018 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 apidemo;
+
+ import static com.ib.controller.Formats.fmt0;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.*;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+
+import com.ib.client.Types.SecType;
+import com.ib.controller.ApiController.IAccountHandler;
+import com.ib.controller.MarketValueTag;
+import com.ib.controller.Position;
+
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.INewTab;
+
+public class AccountInfoPanel extends JPanel implements INewTab, IAccountHandler {
+ private DefaultListModel<String> m_acctList = new DefaultListModel<>();
+ private JList<String> m_accounts = new JList<>( m_acctList);
+ private String m_selAcct = "";
+ private MarginModel m_marginModel = new MarginModel();
+ private PortfolioModel m_portfolioModel = new PortfolioModel();
+ private MktValModel m_mktValModel = new MktValModel();
+ private JLabel m_lastUpdated = new JLabel();
+
+ AccountInfoPanel() {
+ m_lastUpdated.setHorizontalAlignment( SwingConstants.RIGHT);
+
+ m_accounts.setPreferredSize( new Dimension( 10000, 100) );
+ JScrollPane acctScroll = new JScrollPane( m_accounts);
+ acctScroll.setBorder( new TitledBorder( "Select Account"));
+
+ JScrollPane marginScroll = new JScrollPane(new Table(m_marginModel));
+ JScrollPane mvScroll = new JScrollPane(new Table(m_mktValModel, 2));
+ JScrollPane portScroll = new JScrollPane(new Table(m_portfolioModel));
+
+ NewTabbedPanel tabbedPanel = new NewTabbedPanel();
+ tabbedPanel.addTab( "Balances and Margin", marginScroll);
+ tabbedPanel.addTab( "Market Value", mvScroll);
+ tabbedPanel.addTab( "Portfolio", portScroll);
+ tabbedPanel.addTab( "Account Summary", new AccountSummaryPanel() );
+ tabbedPanel.addTab( "Market Value Summary", new MarketValueSummaryPanel() );
+ tabbedPanel.addTab( "Positions (all accounts)", new PositionsPanel() );
+ tabbedPanel.addTab( "Family Codes", new FamilyCodesPanel() );
+
+ setLayout( new BorderLayout() );
+ add( acctScroll, BorderLayout.NORTH);
+ add( tabbedPanel);
+ add( m_lastUpdated, BorderLayout.SOUTH);
+
+ m_accounts.addListSelectionListener(e -> onChanged());
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ for (String account : ApiDemo.INSTANCE.accountList() ) {
+ m_acctList.addElement( account);
+ }
+
+ if (ApiDemo.INSTANCE.accountList().size() == 1) {
+ m_accounts.setSelectedIndex( 0);
+ }
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+
+ protected synchronized void onChanged() {
+ int i = m_accounts.getSelectedIndex();
+ if (i != -1) {
+ String selAcct = m_acctList.get( i);
+ if (!selAcct.equals( m_selAcct) ) {
+ m_selAcct = selAcct;
+ m_marginModel.clear();
+ m_mktValModel.clear();
+ m_portfolioModel.clear();
+ ApiDemo.INSTANCE.controller().reqAccountUpdates(true, m_selAcct, this);
+ }
+ }
+ }
+
+ /** Receive account value. */
+ public synchronized void accountValue(String account, String tag, String value, String currency) {
+ if (account.equals( m_selAcct) ) {
+ try {
+ MarketValueTag mvTag = MarketValueTag.valueOf( tag);
+ m_mktValModel.handle( account, currency, mvTag, value);
+ }
+ catch( Exception e) {
+ m_marginModel.handle( tag, value, currency, account);
+ }
+ }
+ }
+
+ /** Receive position. */
+ public synchronized void updatePortfolio( Position position) {
+ if (position.account().equals( m_selAcct)) {
+ m_portfolioModel.update( position);
+ }
+ }
+
+ /** Receive time of last update. */
+ public void accountTime(String timeStamp) {
+ m_lastUpdated.setText( "Last updated: " + timeStamp + " ");
+ }
+
+ public void accountDownloadEnd(String account) {
+ }
+
+ private static class MarginModel extends AbstractTableModel {
+ Map<MarginRowKey,MarginRow> m_map = new HashMap<>();
+ List<MarginRow> m_list = new ArrayList<>();
+
+ void clear() {
+ m_map.clear();
+ m_list.clear();
+ }
+
+ public void handle(String tag, String value, String currency, String account) {
+ // useless
+ if (tag.equals( "Currency")) {
+ return;
+ }
+
+ int type = 0; // 0=whole acct; 1=securities; 2=commodities
+
+ // "Securities" segment?
+ if (tag.endsWith( "-S") ) {
+ tag = tag.substring( 0, tag.length() - 2);
+ type = 1;
+ }
+
+ // "Commodities" segment?
+ else if (tag.endsWith( "-C") ) {
+ tag = tag.substring( 0, tag.length() - 2);
+ type = 2;
+ }
+
+ MarginRowKey key = new MarginRowKey( tag, currency);
+ MarginRow row = m_map.get( key);
+
+ if (row == null) {
+ // don't add new rows with a value of zero
+ if (isZero( value) ) {
+ return;
+ }
+
+ row = new MarginRow(tag, currency);
+ m_map.put( key, row);
+ m_list.add( row);
+ Collections.sort( m_list);
+ }
+
+ switch( type) {
+ case 0:
+ row.m_val = value;
+ break;
+ case 1:
+ row.m_secVal = value;
+ break;
+ case 2:
+ row.m_comVal = value;
+ break;
+ default:
+ row.m_val = value;
+ break;
+ }
+
+ SwingUtilities.invokeLater(this::fireTableDataChanged);
+ }
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 4;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Tag";
+ case 1: return "Account Value";
+ case 2: return "Securities Value";
+ case 3: return "Commodities Value";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ MarginRow row = m_list.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_tag;
+ case 1: return format( row.m_val, row.m_currency);
+ case 2: return format( row.m_secVal, row.m_currency);
+ case 3: return format( row.m_comVal, row.m_currency);
+ default: return null;
+ }
+ }
+ }
+
+ private static class MarginRow implements Comparable<MarginRow> {
+ String m_tag;
+ String m_currency;
+ String m_val;
+ String m_secVal;
+ String m_comVal;
+
+ MarginRow( String tag, String cur) {
+ m_tag = tag;
+ m_currency = cur;
+ }
+
+ @Override public int compareTo(MarginRow o) {
+ return m_tag.compareTo( o.m_tag);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj || (obj instanceof MarginRow && compareTo((MarginRow) obj) == 0);
+ }
+
+ @Override
+ public int hashCode() {
+ return m_tag != null ? m_tag.hashCode() : 0;
+ }
+ }
+
+ private static class MarginRowKey {
+ String m_tag;
+ String m_currency;
+
+ MarginRowKey(String key, String currency) {
+ m_tag = key;
+ m_currency = currency;
+ }
+
+ @Override public int hashCode() {
+ int cur = m_currency != null ? m_currency.hashCode() : 0;
+ return m_tag.hashCode() + cur;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof MarginRowKey)) {
+ return false;
+ }
+ MarginRowKey other = (MarginRowKey)obj;
+ return m_tag.equals( other.m_tag) && Objects.equals(m_currency, other.m_currency);
+ }
+ }
+
+ static class MktValModel extends AbstractTableModel {
+ private Map<String,MktValRow> m_map = new HashMap<>();
+ private List<MktValRow> m_list = new ArrayList<>();
+
+ void handle(String account, String currency, MarketValueTag mvTag, String value) {
+ String key = account + currency;
+ MktValRow row = m_map.get( key);
+ if (row == null) {
+ row = new MktValRow( account, currency);
+ m_map.put( key, row);
+ m_list.add( row);
+ }
+ row.set( mvTag, value);
+ fireTableDataChanged();
+ }
+
+ void clear() {
+ m_map.clear();
+ m_list.clear();
+ fireTableDataChanged();
+ }
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ @Override public int getColumnCount() {
+ return MarketValueTag.values().length + 2;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Account";
+ case 1: return "Currency";
+ default: return MarketValueTag.get( col - 2).toString();
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ MktValRow row = m_list.get( rowIn);
+ switch( col) {
+ case 0: return row.m_account;
+ case 1: return row.m_currency;
+ default: return format( row.get( MarketValueTag.get( col - 2) ), null);
+ }
+ }
+ }
+
+ private static class MktValRow {
+ String m_account;
+ String m_currency;
+ Map<MarketValueTag,String> m_map = new HashMap<>();
+
+ MktValRow(String account, String currency) {
+ m_account = account;
+ m_currency = currency;
+ }
+
+ public String get(MarketValueTag tag) {
+ return m_map.get( tag);
+ }
+
+ public void set(MarketValueTag tag, String value) {
+ m_map.put( tag, value);
+ }
+ }
+
+ /** Shared with ExercisePanel. */
+ static class PortfolioModel extends AbstractTableModel {
+ private Map<Integer,Position> m_portfolioMap = new HashMap<>();
+ private List<Integer> m_positions = new ArrayList<>(); // must store key because Position is overwritten
+
+ void clear() {
+ m_positions.clear();
+ m_portfolioMap.clear();
+ }
+
+ Position getPosition( int i) {
+ return m_portfolioMap.get( m_positions.get( i) );
+ }
+
+ public void update( Position position) {
+ // skip fake FX positions
+ if (position.contract().secType() == SecType.CASH) {
+ return;
+ }
+
+ if (!m_portfolioMap.containsKey( position.conid() ) && position.position() != 0) {
+ m_positions.add( position.conid() );
+ }
+ m_portfolioMap.put( position.conid(), position);
+ fireTableDataChanged();
+ }
+
+ @Override public int getRowCount() {
+ return m_positions.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 7;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Description";
+ case 1: return "Position";
+ case 2: return "Price";
+ case 3: return "Value";
+ case 4: return "Avg Cost";
+ case 5: return "Unreal Pnl";
+ case 6: return "Real Pnl";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int row, int col) {
+ Position pos = getPosition( row);
+ switch( col) {
+ case 0: return pos.contract().description();
+ case 1: return pos.position();
+ case 2: return pos.marketPrice();
+ case 3: return format( "" + pos.marketValue(), null);
+ case 4: return pos.averageCost();
+ case 5: return pos.unrealPnl();
+ case 6: return pos.realPnl();
+ default: return null;
+ }
+ }
+ }
+
+ private static boolean isZero(String value) {
+ try {
+ return Double.parseDouble( value) == 0;
+ }
+ catch( Exception e) {
+ return false;
+ }
+ }
+
+ /** If val is a number, format it with commas and no decimals. */
+ static String format(String val, String currency) {
+ if (val == null || val.length() == 0) {
+ return null;
+ }
+
+ try {
+ double dub = Double.parseDouble( val);
+ val = fmt0( dub);
+ } catch (Exception ignored) {
+ }
+
+ return currency != null && currency.length() > 0
+ ? val + " " + currency : val;
+ }
+
+ /** Table where first n columns are left-justified, all other columns are right-justified. */
+ static class Table extends JTable {
+ private int m_n;
+
+ public Table(AbstractTableModel model) {
+ this( model, 1);
+ }
+
+ public Table(AbstractTableModel model, int n) {
+ super( model);
+ m_n = n;
+ }
+
+ @Override public TableCellRenderer getCellRenderer(int row, int col) {
+ TableCellRenderer rend = super.getCellRenderer(row, col);
+ ((JLabel)rend).setHorizontalAlignment( col < m_n ? SwingConstants.LEFT : SwingConstants.RIGHT);
+ return rend;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/AccountPositionsMultiPanel.java b/demo/src/main/java/apidemo/AccountPositionsMultiPanel.java
new file mode 100755
index 0000000..15c0798
--- /dev/null
+++ b/demo/src/main/java/apidemo/AccountPositionsMultiPanel.java
@@ -0,0 +1,419 @@
+/* Copyright (C) 2018 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 apidemo;
+
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.controller.ApiController.IAccountUpdateMultiHandler;
+import com.ib.controller.ApiController.IPositionMultiHandler;
+import com.ib.controller.Formats;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+class AccountPositionsMultiPanel extends JPanel {
+ private final NewTabbedPanel m_requestPanel = new NewTabbedPanel();
+ private final NewTabbedPanel m_resultsPanel = new NewTabbedPanel();
+
+ AccountPositionsMultiPanel() {
+ m_requestPanel.addTab("Positions Multi", new PositionsMultiPanel());
+ m_requestPanel.addTab("Account Updates Multi", new AccountUpdatesMultiPanel());
+
+ setLayout(new BorderLayout());
+ add(m_requestPanel, BorderLayout.NORTH);
+ add(m_resultsPanel);
+ }
+
+ private static class RequestPanel extends JPanel {
+ protected JTextField m_account = new JTextField();
+ protected JTextField m_modelCode = new JTextField();
+ final JCheckBox m_ledgerAndNLV = new JCheckBox();
+
+ RequestPanel() {
+ VerticalPanel p = new VerticalPanel();
+ p.add("Account", m_account);
+ m_modelCode.setColumns(7);
+ p.add("Model Code", m_modelCode);
+ p.add("LedgerAndNLV", m_ledgerAndNLV);
+
+ setLayout(new BorderLayout());
+ add(p);
+ }
+
+ void enableLedgerAndNLV(boolean enable) {
+ m_ledgerAndNLV.setEnabled(enable);
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+ }
+
+ private class PositionsMultiPanel extends JPanel {
+ final RequestPanel m_requestPanel = new RequestPanel();
+
+ PositionsMultiPanel() {
+
+ HtmlButton requestPositionsMultiButton = new HtmlButton("Request Positions Multi") {
+ protected void actionPerformed() {
+ onRequestPositionsMulti();
+ }
+ };
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add(requestPositionsMultiButton);
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ m_requestPanel.enableLedgerAndNLV(false);
+ add(m_requestPanel);
+ add(Box.createHorizontalStrut(20));
+ add(butPanel);
+ }
+
+ void onRequestPositionsMulti() {
+ PositionsResultsPanel panel = new PositionsResultsPanel();
+ String account = m_requestPanel.m_account.getText();
+ if (account != null) {
+ account = account.trim();
+ }
+ String modelCode = m_requestPanel.m_modelCode.getText();
+ if (modelCode != null) {
+ modelCode = modelCode.trim();
+ }
+ ApiDemo.INSTANCE.controller().reqPositionsMulti(account, modelCode, panel);
+ m_resultsPanel.addTab("Positions "
+ + (!(account == null || account.isEmpty()) ? (" A:" + account) : "")
+ + (!(modelCode == null || modelCode.isEmpty()) ? (" M :" + modelCode) : ":"),
+ panel, true, true);
+ }
+
+ private class PositionsResultsPanel extends NewTabPanel implements IPositionMultiHandler {
+ Map<String, PositionRow> m_map = new HashMap<>();
+ List<PositionRow> m_list = new ArrayList<>();
+ PositionsModel m_model = new PositionsModel();
+
+ boolean m_complete;
+
+ PositionsResultsPanel() {
+ JTable tab = new JTable(m_model);
+ JScrollPane scroll = new JScrollPane(tab);
+ setLayout(new BorderLayout());
+ add(scroll, BorderLayout.WEST);
+ }
+
+ @Override
+ public void positionMulti(String account, String modelCode, Contract contract, double pos, double avgCost) {
+ String key = contract.conid() + "_" + account + "_" + modelCode;
+ PositionRow row = m_map.get(key);
+ if (row == null) {
+ row = new PositionRow();
+ m_map.put(key, row);
+ m_list.add(row);
+ }
+ row.update(account, modelCode, contract, pos, avgCost);
+
+ if (m_complete) {
+ m_model.fireTableDataChanged();
+ }
+
+ }
+
+ @Override
+ public void positionMultiEnd() {
+ m_model.fireTableDataChanged();
+ m_complete = true;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Called when the tab is first visited.
+ */
+ @Override
+ public void activated() {
+ }
+
+ /**
+ * Called when the tab is closed by clicking the X.
+ */
+ @Override
+ public void closed() {
+ ApiDemo.INSTANCE.controller().cancelPositionsMulti(this);
+ }
+
+ class PositionsModel extends AbstractTableModel {
+ @Override
+ public int getRowCount() {
+ return m_map.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 5;
+ }
+
+ @Override
+ public String getColumnName(int col) {
+ switch (col) {
+ case 0:
+ return "Account";
+ case 1:
+ return "ModelCode";
+ case 2:
+ return "Contract";
+ case 3:
+ return "Position";
+ case 4:
+ return "Avg Cost";
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public Object getValueAt(int rowIn, int col) {
+ PositionRow row = m_list.get(rowIn);
+
+ switch (col) {
+ case 0:
+ return row.m_account;
+ case 1:
+ return row.m_modelCode;
+ case 2:
+ return row.m_contract.description();
+ case 3:
+ return row.m_position;
+ case 4:
+ return Formats.fmt(row.m_avgCost);
+ default:
+ return null;
+ }
+ }
+
+ }
+ }
+ }
+
+ private class AccountUpdatesMultiPanel extends JPanel {
+ final RequestPanel m_requestPanel = new RequestPanel();
+
+ AccountUpdatesMultiPanel() {
+
+ HtmlButton requestAccountUpdatesMultiButton = new HtmlButton("Request Account Updates Multi") {
+ protected void actionPerformed() {
+ onRequestAccountUpdatesMulti();
+ }
+ };
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add(requestAccountUpdatesMultiButton);
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ m_requestPanel.enableLedgerAndNLV(true);
+ add(m_requestPanel);
+ add(Box.createHorizontalStrut(20));
+ add(butPanel);
+ }
+
+ void onRequestAccountUpdatesMulti() {
+ AccountUpdatesResultsPanel panel = new AccountUpdatesResultsPanel();
+ String account = m_requestPanel.m_account.getText();
+ if (account != null) {
+ account = account.trim();
+ }
+ String modelCode = m_requestPanel.m_modelCode.getText();
+ if (modelCode != null) {
+ modelCode = modelCode.trim();
+ }
+ boolean ledgerAndNLV = m_requestPanel.m_ledgerAndNLV.isSelected();
+ ApiDemo.INSTANCE.controller().reqAccountUpdatesMulti(account, modelCode, ledgerAndNLV, panel);
+ m_resultsPanel.addTab("Acc Updates "
+ + (!(account == null || account.isEmpty()) ? (" A:" + account) : "")
+ + (!(modelCode == null || modelCode.isEmpty()) ? (" M :" + modelCode) : "") + (ledgerAndNLV ? " - LW" : ""),
+ panel, true, true);
+ }
+
+ private class AccountUpdatesResultsPanel extends NewTabPanel implements IAccountUpdateMultiHandler {
+ Map<AccountUpdateKey, AccountUpdateRow> m_map = new HashMap<>();
+ List<AccountUpdateRow> m_list = new ArrayList<>();
+ AccountUpdatesModel m_model = new AccountUpdatesModel();
+
+ boolean m_complete;
+
+ AccountUpdatesResultsPanel() {
+ JTable tab = new JTable(m_model);
+ JScrollPane scroll = new JScrollPane(tab);
+ setLayout(new BorderLayout());
+ add(scroll, BorderLayout.CENTER);
+ }
+
+ @Override
+ public void accountUpdateMulti(String account, String modelCode, String key, String value, String currency) {
+ AccountUpdateKey acctUpdateKey = new AccountUpdateKey(key, currency);
+ AccountUpdateRow row = m_map.get(acctUpdateKey);
+ if (row == null) {
+ row = new AccountUpdateRow();
+ m_map.put(acctUpdateKey, row);
+ m_list.add(row);
+ }
+ row.update(account, modelCode, key, value, currency);
+
+ if (m_complete) {
+ m_model.fireTableDataChanged();
+ }
+
+ }
+
+ @Override
+ public void accountUpdateMultiEnd() {
+ m_model.fireTableDataChanged();
+ m_complete = true;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Called when the tab is first visited.
+ */
+ @Override
+ public void activated() {
+ }
+
+ /**
+ * Called when the tab is closed by clicking the X.
+ */
+ @Override
+ public void closed() {
+ ApiDemo.INSTANCE.controller().cancelAccountUpdatesMulti(this);
+ }
+
+ class AccountUpdatesModel extends AbstractTableModel {
+ @Override
+ public int getRowCount() {
+ return m_map.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 5;
+ }
+
+ @Override
+ public String getColumnName(int col) {
+ switch (col) {
+ case 0:
+ return "Account";
+ case 1:
+ return "ModelCode";
+ case 2:
+ return "Key";
+ case 3:
+ return "Value";
+ case 4:
+ return "Currency";
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public Object getValueAt(int rowIn, int col) {
+ AccountUpdateRow row = m_list.get(rowIn);
+
+ switch (col) {
+ case 0:
+ return row.m_account;
+ case 1:
+ return row.m_modelCode;
+ case 2:
+ return row.m_key;
+ case 3:
+ return row.m_value;
+ case 4:
+ return row.m_currency;
+ default:
+ return null;
+ }
+ }
+
+ }
+ }
+ }
+
+ private static class AccountUpdateRow {
+ String m_account;
+ String m_modelCode;
+ String m_key;
+ String m_value;
+ String m_currency;
+
+ void update(String account, String modelCode, String key, String value, String currency) {
+ m_account = account;
+ m_modelCode = modelCode;
+ m_key = key;
+ m_value = value;
+ m_currency = currency;
+ }
+ }
+
+ private static class AccountUpdateKey {
+ String m_key;
+ String m_currency;
+
+ AccountUpdateKey(String key, String currency) {
+ m_key = key;
+ m_currency = currency == null ? "" : currency;
+ }
+
+ @Override
+ public int hashCode() {
+ return m_key.hashCode() + m_currency.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AccountUpdateKey)) {
+ return false;
+ }
+ AccountUpdateKey other = (AccountUpdateKey) obj;
+ return m_key.equals(other.m_key) && m_currency.equals(other.m_currency);
+ }
+ }
+
+ private static class PositionRow {
+ String m_account;
+ String m_modelCode;
+ Contract m_contract;
+ double m_position;
+ double m_avgCost;
+
+ void update(String account, String modelCode, Contract contract, double position, double avgCost) {
+ m_account = account;
+ m_modelCode = modelCode;
+ m_contract = contract;
+ m_position = position;
+ m_avgCost = avgCost;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/AccountSummaryPanel.java b/demo/src/main/java/apidemo/AccountSummaryPanel.java
new file mode 100755
index 0000000..51f94a3
--- /dev/null
+++ b/demo/src/main/java/apidemo/AccountSummaryPanel.java
@@ -0,0 +1,156 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.controller.AccountSummaryTag;
+import com.ib.controller.ApiController.IAccountSummaryHandler;
+import com.ib.controller.Formats;
+
+import apidemo.AccountInfoPanel.Table;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+
+public class AccountSummaryPanel extends NewTabPanel {
+ private SummaryModel m_model = new SummaryModel();
+
+ AccountSummaryPanel() {
+ HtmlButton sub = new HtmlButton( "Subscribe") {
+ protected void actionPerformed() {
+ subscribe();
+ }
+ };
+
+ HtmlButton desub = new HtmlButton( "Desubscribe") {
+ protected void actionPerformed() {
+ desubscribe();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( sub);
+ buts.add( desub);
+
+ JTable table = new Table( m_model);
+ JScrollPane scroll = new JScrollPane( table);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ subscribe();
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ desubscribe();
+ }
+
+ private void subscribe() {
+ ApiDemo.INSTANCE.controller().reqAccountSummary( "All", AccountSummaryTag.values(), m_model);
+ }
+
+ private void desubscribe() {
+ ApiDemo.INSTANCE.controller().cancelAccountSummary( m_model);
+ m_model.clear();
+ }
+
+ private static class SummaryModel extends AbstractTableModel implements IAccountSummaryHandler {
+ List<SummaryRow> m_rows = new ArrayList<>();
+ Map<String,SummaryRow> m_map = new HashMap<>();
+ boolean m_complete;
+
+ public void clear() {
+ ApiDemo.INSTANCE.controller().cancelAccountSummary( this);
+ m_rows.clear();
+ m_map.clear();
+ m_complete = false;
+ fireTableDataChanged();
+ }
+
+ @Override public void accountSummary(String account, AccountSummaryTag tag, String value, String currency) {
+ SummaryRow row = m_map.get( account);
+ if (row == null) {
+ row = new SummaryRow();
+ m_map.put( account, row);
+ m_rows.add( row);
+ }
+ row.update( account, tag, value);
+
+ if (m_complete) {
+ fireTableDataChanged();
+ }
+ }
+
+ @Override public void accountSummaryEnd() {
+ fireTableDataChanged();
+ m_complete = true;
+ }
+
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return AccountSummaryTag.values().length + 1; // add one for Account column
+ }
+
+ @Override public String getColumnName(int col) {
+ if (col == 0) {
+ return "Account";
+ }
+ return AccountSummaryTag.values()[col - 1].toString();
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ SummaryRow row = m_rows.get( rowIn);
+
+ if (col == 0) {
+ return row.m_account;
+ }
+
+ AccountSummaryTag tag = AccountSummaryTag.values()[col - 1];
+ String val = row.m_map.get( tag);
+
+ switch( tag) {
+ case Cushion: return fmtPct( val);
+ case LookAheadNextChange: return fmtTime( val);
+ default: return AccountInfoPanel.format( val, null);
+ }
+ }
+
+ String fmtPct(String val) {
+ return val == null || val.length() == 0 ? null : Formats.fmtPct( Double.parseDouble( val) );
+ }
+
+ String fmtTime(String val) {
+ return val == null || val.length() == 0 || val.equals( "0") ? null : Formats.fmtDate( Long.parseLong( val) * 1000);
+ }
+ }
+
+ private static class SummaryRow {
+ String m_account;
+ Map<AccountSummaryTag,String> m_map = new HashMap<>();
+
+ public void update(String account, AccountSummaryTag tag, String value) {
+ m_account = account;
+ m_map.put( tag, value);
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/AdjustedPanel.java b/demo/src/main/java/apidemo/AdjustedPanel.java
new file mode 100755
index 0000000..b9ab6f1
--- /dev/null
+++ b/demo/src/main/java/apidemo/AdjustedPanel.java
@@ -0,0 +1,58 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import javax.swing.JDialog;
+
+import com.ib.client.Order;
+import com.ib.client.OrderCondition;
+import com.ib.client.OrderType;
+
+import apidemo.TicketDlg.AmntUnit;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+
+public class AdjustedPanel extends OnOKPanel {
+ /**
+ *
+ */
+ private final JDialog m_parentDlg;
+ private final Order m_order;
+ private final TCombo<OrderType> m_adjustedOrderType = new TCombo<>(OrderType.None, OrderType.STP, OrderType.STP_LMT, OrderType.TRAIL, OrderType.TRAIL_LIMIT);
+ private final UpperField m_triggerPrice = new UpperField();
+ private final UpperField m_adjustedStopPrice = new UpperField();
+ private final UpperField m_adjustedStopLimitPrice = new UpperField();
+ private final UpperField m_adjustedTrailingAmount = new UpperField();
+ private final TCombo<AmntUnit> m_adjustedTrailingAmountUnit = new TCombo<>(AmntUnit.values());
+
+ public AdjustedPanel(JDialog parentDlg, Order order) {
+ m_parentDlg = parentDlg;
+ m_order = order;
+ m_adjustedOrderType.setSelectedItem(m_order.adjustedOrderType());
+
+ m_triggerPrice.setText(m_order.triggerPrice());
+ m_adjustedStopPrice.setText(m_order.adjustedStopPrice());
+ m_adjustedStopLimitPrice.setText(m_order.adjustedStopLimitPrice());
+ m_adjustedTrailingAmount.setText(m_order.adjustedTrailingAmount());
+ m_adjustedTrailingAmountUnit.setSelectedItem(AmntUnit.fromInt(m_order.adjustableTrailingUnit()));
+
+ add("Adjust to order type", m_adjustedOrderType);
+ add("Trigger price", m_triggerPrice);
+ add("Adjusted stop price", m_adjustedStopPrice);
+ add("Adjusted stop limit price", m_adjustedStopLimitPrice);
+ add("Adjusted trailing amount", m_adjustedTrailingAmount);
+ add("Adjusted trailing amount unit", m_adjustedTrailingAmountUnit);
+ }
+
+ public OrderCondition onOK() {
+ m_order.adjustedOrderType( m_adjustedOrderType.getSelectedItem() );
+ m_order.triggerPrice(m_triggerPrice.getDouble());
+ m_order.adjustedStopPrice(m_adjustedStopPrice.getDouble());
+ m_order.adjustedStopLimitPrice(m_adjustedStopLimitPrice.getDouble());
+ m_order.adjustedTrailingAmount(m_adjustedTrailingAmount.getDouble());
+ m_order.adjustableTrailingUnit(m_adjustedTrailingAmountUnit.getSelectedItem().m_val);
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/AdvisorPanel.java b/demo/src/main/java/apidemo/AdvisorPanel.java
new file mode 100755
index 0000000..368a79e
--- /dev/null
+++ b/demo/src/main/java/apidemo/AdvisorPanel.java
@@ -0,0 +1,362 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultCellEditor;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellEditor;
+
+import com.ib.client.Types.FADataType;
+import com.ib.client.Types.Method;
+import com.ib.controller.Alias;
+import com.ib.controller.ApiController.IAdvisorHandler;
+import com.ib.controller.Group;
+import com.ib.controller.Profile;
+import com.ib.controller.Profile.Type;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.TCombo;
+import apidemo.util.VerticalPanel;
+
+public class AdvisorPanel extends NewTabPanel implements IAdvisorHandler {
+ static DefaultCellEditor DEF_CELL_EDITOR = new DefaultCellEditor( new JTextField() );
+ static {
+ DEF_CELL_EDITOR.setClickCountToStart( 1);
+ }
+
+ private final GroupModel m_groupModel = new GroupModel();
+ private final ProfileModel m_profileModel = new ProfileModel();
+ private final AliasModel m_aliasModel = new AliasModel();
+
+ private final JTable m_groupTable = new JTable( m_groupModel) {
+ public TableCellEditor getCellEditor(int row, int col) {
+ return m_groupModel.getCellEditor(row, col);
+ }
+ };
+
+ private final JTable m_profileTable = new JTable( m_profileModel) {
+ public TableCellEditor getCellEditor(int row, int col) {
+ return m_profileModel.getCellEditor(row, col);
+ }
+ };
+
+ AdvisorPanel() {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout( new BoxLayout( mainPanel, BoxLayout.Y_AXIS));
+ mainPanel.setBorder( new EmptyBorder( 0, 10, 0, 0) );
+ mainPanel.add( new GroupsPanel() );
+ mainPanel.add( Box.createVerticalStrut(10));
+ mainPanel.add( new ProfilesPanel() );
+
+ JScrollPane aliasScroll = new JScrollPane(new JTable( m_aliasModel));
+ aliasScroll.setBorder( new TitledBorder( "Aliases"));
+ aliasScroll.setPreferredSize(new Dimension( 300, 2000));
+
+ setLayout( new BorderLayout() );
+ add( aliasScroll, BorderLayout.WEST);
+ add( mainPanel);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ ApiDemo.INSTANCE.controller().reqAdvisorData( FADataType.GROUPS, this);
+ ApiDemo.INSTANCE.controller().reqAdvisorData( FADataType.PROFILES, this );
+ ApiDemo.INSTANCE.controller().reqAdvisorData( FADataType.ALIASES, this );
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+
+ public void groups(List<Group> groups) {
+ m_groupModel.update( groups);
+ }
+
+ public void profiles(List<Profile> profiles) {
+ m_profileModel.update( profiles);
+ }
+
+ public void aliases(List<Alias> aliases) {
+ m_aliasModel.update( aliases);
+ }
+
+ private static class AliasModel extends AbstractTableModel {
+ List<Alias> m_list = new ArrayList<>();
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ public void update(List<Alias> aliases) {
+ m_list.clear();
+ m_list.addAll( aliases);
+ fireTableDataChanged();
+ }
+
+ @Override public int getColumnCount() {
+ return 2;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Account";
+ case 1: return "Alias";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ Alias row = m_list.get( rowIn);
+ switch( col) {
+ case 0: return row.account();
+ case 1: return row.alias();
+ default: return null;
+ }
+ }
+ }
+
+ private class GroupsPanel extends JPanel {
+ GroupsPanel() {
+ JScrollPane groupScroll = new JScrollPane( m_groupTable);
+ groupScroll.setBorder( new TitledBorder( "Groups"));
+
+ HtmlButton create = new HtmlButton( "Create Group") {
+ @Override protected void actionPerformed() {
+ onCreateGroup();
+ }
+ };
+
+ HtmlButton update = new HtmlButton( "Update") {
+ @Override protected void actionPerformed() {
+ onTransmit();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( create);
+ buts.add( update);
+
+ setLayout( new BorderLayout() );
+ add( groupScroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ void onCreateGroup() {
+ String name = JOptionPane.showInputDialog( this, "Enter group name");
+ if (name != null) {
+ m_groupModel.add( name);
+ }
+ }
+
+ void onTransmit() {
+ int rc = JOptionPane.showConfirmDialog( this, "This will replace all Groups in TWS with the ones shown here.\nAre you sure you want to do that?", "Confirm", JOptionPane.YES_NO_OPTION);
+ if (rc == 0) {
+ m_groupModel.transmit();
+ JOptionPane.showMessageDialog(this, "The groups have been updated");
+ }
+ }
+ }
+
+ private static class GroupModel extends AbstractTableModel {
+ TCombo<Method> combo = new TCombo<>(Method.values());
+ DefaultCellEditor EDITOR = new DefaultCellEditor( combo);
+ List<Group> m_groups = new ArrayList<>();
+
+ GroupModel() {
+ EDITOR.setClickCountToStart( 1);
+ }
+
+ void update(List<Group> groups) {
+ m_groups.clear();
+ m_groups.addAll( groups);
+ fireTableDataChanged();
+ }
+
+ void add(String name) {
+ Group group = new Group();
+ group.name( name);
+ m_groups.add( group);
+ fireTableDataChanged();
+ }
+
+ public void transmit() {
+ ApiDemo.INSTANCE.controller().updateGroups(m_groups);
+ }
+
+ @Override public int getRowCount() {
+ return m_groups.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 3;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Name";
+ case 1: return "Default Method";
+ case 2: return "Accounts";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ Group row = m_groups.get( rowIn);
+ switch( col) {
+ case 0: return row.name();
+ case 1: return row.defaultMethod();
+ case 2: return row.accounts().toString().substring( 1, row.accounts().toString().length() - 1);
+ default: return null;
+ }
+ }
+
+ @Override public boolean isCellEditable(int rowIndex, int col) {
+ return true;
+ }
+
+ TableCellEditor getCellEditor(int row, int col) {
+ return col == 1 ? EDITOR : DEF_CELL_EDITOR;
+ }
+
+ @Override public void setValueAt(Object val, int rowIn, int col) {
+ Group row = m_groups.get( rowIn);
+ switch( col) {
+ case 0: row.name( (String)val); break;
+ case 1: row.defaultMethod( (Method)val); break;
+ case 2: row.setAllAccounts( (String)val); break;
+ default: break;
+ }
+ }
+ }
+
+ private class ProfilesPanel extends JPanel {
+ ProfilesPanel() {
+ JScrollPane profileScroll = new JScrollPane( m_profileTable);
+ profileScroll.setBorder( new TitledBorder( "Profiles"));
+
+ HtmlButton create = new HtmlButton( "Create Profile") {
+ @Override protected void actionPerformed() {
+ onCreateProfile();
+ }
+ };
+
+ HtmlButton update = new HtmlButton( "Update") {
+ @Override protected void actionPerformed() {
+ onTransmit();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( create);
+ buts.add( update);
+
+ setLayout( new BorderLayout() );
+ add( profileScroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ void onCreateProfile() {
+ String name = JOptionPane.showInputDialog( this, "Enter profile name");
+ if (name != null) {
+ m_profileModel.add( name);
+ }
+ }
+
+ void onTransmit() {
+ int rc = JOptionPane.showConfirmDialog( this, "This will replace all Profiles in TWS with the ones shown here.\nAre you sure you want to do that?", "Confirm", JOptionPane.YES_NO_OPTION);
+ if (rc == 0) {
+ m_profileModel.transmit();
+ JOptionPane.showMessageDialog(this, "The Profiles have been updated");
+ }
+ }
+ }
+
+ private static class ProfileModel extends AbstractTableModel {
+ TCombo<Type> combo = new TCombo<>(Type.values());
+ DefaultCellEditor EDITOR = new DefaultCellEditor( combo);
+ List<Profile> m_profiles = new ArrayList<>();
+
+ ProfileModel() {
+ EDITOR.setClickCountToStart( 1);
+ combo.removeItemAt( 0);
+ }
+
+ public void update(List<Profile> profiles) {
+ m_profiles.clear();
+ m_profiles.addAll( profiles);
+ fireTableDataChanged();
+ }
+
+ public void add(String name) {
+ Profile profile = new Profile();
+ profile.name( name);
+ m_profiles.add( profile);
+ fireTableDataChanged();
+ }
+
+ public void transmit() {
+ ApiDemo.INSTANCE.controller().updateProfiles( m_profiles);
+ }
+
+ @Override public int getRowCount() {
+ return m_profiles.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 3;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Name";
+ case 1: return "Type";
+ case 2: return "Allocations";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ Profile row = m_profiles.get( rowIn);
+ switch( col) {
+ case 0: return row.name();
+ case 1: return row.type();
+ case 2: return row.allocations().toString().substring( 1, row.allocations().toString().length() - 1);
+ default: return null;
+ }
+ }
+
+ @Override public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return true;
+ }
+
+ TableCellEditor getCellEditor(int row, int col) {
+ return col == 1 ? EDITOR : DEF_CELL_EDITOR;
+ }
+
+ @Override public void setValueAt(Object val, int rowIn, int col) {
+ Profile row = m_profiles.get( rowIn);
+ switch( col) {
+ case 0: row.name( (String)val); break;
+ case 1: row.type( (Type)val); break;
+ case 2: row.setAllocations( (String)val); break;
+ default: break;
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/ApiDemo.java b/demo/src/main/java/apidemo/ApiDemo.java
new file mode 100755
index 0000000..9ceec89
--- /dev/null
+++ b/demo/src/main/java/apidemo/ApiDemo.java
@@ -0,0 +1,304 @@
+/* Copyright (C) 2018 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 apidemo;
+
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+
+import com.ib.controller.ApiConnection.ILogger;
+import com.ib.controller.ApiController;
+import com.ib.controller.ApiController.IConnectionHandler;
+import com.ib.controller.Formats;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.IConnectionConfiguration;
+import apidemo.util.IConnectionConfiguration.DefaultConnectionConfiguration;
+import apidemo.util.NewLookAndFeel;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.VerticalPanel;
+
+public class ApiDemo implements IConnectionHandler {
+ static { NewLookAndFeel.register(); }
+ public static ApiDemo INSTANCE;
+
+ private final IConnectionConfiguration m_connectionConfiguration;
+ private final JTextArea m_inLog = new JTextArea();
+ private final JTextArea m_outLog = new JTextArea();
+ private final Logger m_inLogger = new Logger( m_inLog);
+ private final Logger m_outLogger = new Logger( m_outLog);
+ private ApiController m_controller;
+ private final List<String> m_acctList = new ArrayList<>();
+ private final JFrame m_frame = new JFrame();
+ private final NewTabbedPanel m_tabbedPanel = new NewTabbedPanel(true);
+ private final ConnectionPanel m_connectionPanel;
+ private final MarketDataPanel m_mktDataPanel = new MarketDataPanel();
+ private final ContractInfoPanel m_contractInfoPanel = new ContractInfoPanel();
+ private final TradingPanel m_tradingPanel = new TradingPanel();
+ private final AccountInfoPanel m_acctInfoPanel = new AccountInfoPanel();
+ private final AccountPositionsMultiPanel m_acctPosMultiPanel = new AccountPositionsMultiPanel();
+ private final OptionsPanel m_optionsPanel = new OptionsPanel();
+ private final AdvisorPanel m_advisorPanel = new AdvisorPanel();
+ private final ComboPanel m_comboPanel = new ComboPanel(m_mktDataPanel);
+ private final StratPanel m_stratPanel = new StratPanel();
+ private final NewsPanel m_newsPanel = new NewsPanel();
+ private final JTextArea m_msg = new JTextArea();
+
+ // getter methods
+ List<String> accountList() { return m_acctList; }
+ JFrame frame() { return m_frame; }
+ ILogger getInLogger() { return m_inLogger; }
+ ILogger getOutLogger() { return m_outLogger; }
+
+ public static void main(String[] args) {
+ start( new ApiDemo( new DefaultConnectionConfiguration() ) );
+ }
+
+ public static void start( ApiDemo apiDemo ) {
+ INSTANCE = apiDemo;
+ INSTANCE.run();
+ }
+
+ public ApiDemo( IConnectionConfiguration connectionConfig ) {
+ m_connectionConfiguration = connectionConfig;
+ m_connectionPanel = new ConnectionPanel(); // must be done after connection config is set
+ }
+
+ public ApiController controller() {
+ if ( m_controller == null ) {
+ m_controller = new ApiController( this, getInLogger(), getOutLogger() );
+ }
+ return m_controller;
+ }
+
+ private void run() {
+ m_tabbedPanel.addTab( "Connection", m_connectionPanel);
+ m_tabbedPanel.addTab( "Market Data", m_mktDataPanel);
+ m_tabbedPanel.addTab( "Trading", m_tradingPanel);
+ m_tabbedPanel.addTab( "Account Info", m_acctInfoPanel);
+ m_tabbedPanel.addTab( "Acct/Pos Multi", m_acctPosMultiPanel);
+ m_tabbedPanel.addTab( "Options", m_optionsPanel);
+ m_tabbedPanel.addTab( "Combos", m_comboPanel);
+ m_tabbedPanel.addTab( "Contract Info", m_contractInfoPanel);
+ m_tabbedPanel.addTab( "Advisor", m_advisorPanel);
+ // m_tabbedPanel.addTab( "Strategy", m_stratPanel); in progress
+ m_tabbedPanel.addTab( "News", m_newsPanel);
+
+ m_msg.setEditable( false);
+ m_msg.setLineWrap( true);
+ JScrollPane msgScroll = new JScrollPane( m_msg);
+ msgScroll.setPreferredSize( new Dimension( 10000, 120) );
+
+ JScrollPane outLogScroll = new JScrollPane( m_outLog);
+ outLogScroll.setPreferredSize( new Dimension( 10000, 120) );
+
+ JScrollPane inLogScroll = new JScrollPane( m_inLog);
+ inLogScroll.setPreferredSize( new Dimension( 10000, 120) );
+
+ NewTabbedPanel bot = new NewTabbedPanel();
+ bot.addTab( "Messages", msgScroll);
+ bot.addTab( "Log (out)", outLogScroll);
+ bot.addTab( "Log (in)", inLogScroll);
+
+ m_frame.add( m_tabbedPanel);
+ m_frame.add( bot, BorderLayout.SOUTH);
+ m_frame.setSize( 1024, 768);
+ m_frame.setVisible( true);
+ m_frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ // make initial connection to local host, port 7496, client id 0, no connection options
+ controller().connect( "127.0.0.1", 7496, 0, m_connectionConfiguration.getDefaultConnectOptions() != null ? "" : null );
+ }
+
+ @Override public void connected() {
+ show( "connected");
+ m_connectionPanel.m_status.setText( "connected");
+
+ controller().reqCurrentTime(time -> show( "Server date/time is " + Formats.fmtDate(time * 1000) ));
+
+ controller().reqBulletins( true, (msgId, newsType, message, exchange) -> {
+ String str = String.format( "Received bulletin: type=%s exchange=%s", newsType, exchange);
+ show( str);
+ show( message);
+ });
+ }
+
+ @Override public void disconnected() {
+ show( "disconnected");
+ m_connectionPanel.m_status.setText( "disconnected");
+ }
+
+ @Override public void accountList(List<String> list) {
+ show( "Received account list");
+ m_acctList.clear();
+ m_acctList.addAll( list);
+ }
+
+ @Override public void show( final String str) {
+ SwingUtilities.invokeLater(() -> {
+ m_msg.append(str);
+ m_msg.append( "\n\n");
+
+ Dimension d = m_msg.getSize();
+ m_msg.scrollRectToVisible( new Rectangle( 0, d.height, 1, 1) );
+ });
+ }
+
+ @Override public void error(Exception e) {
+ show( e.toString() );
+ }
+
+ @Override public void message(int id, int errorCode, String errorMsg) {
+ show( id + " " + errorCode + " " + errorMsg);
+ }
+
+ private class ConnectionPanel extends JPanel {
+ private final JTextField m_host = new JTextField( m_connectionConfiguration.getDefaultHost(), 10);
+ private final JTextField m_port = new JTextField( m_connectionConfiguration.getDefaultPort(), 7);
+ private final JTextField m_connectOptionsTF = new JTextField( m_connectionConfiguration.getDefaultConnectOptions(), 30);
+ private final JTextField m_clientId = new JTextField("0", 7);
+ private final JLabel m_status = new JLabel("Disconnected");
+ private final JLabel m_defaultPortNumberLabel = new JLabel("<html>Live Trading ports:<b> TWS: 7496; IB Gateway: 4001.</b><br>"
+ + "Simulated Trading ports for new installations of "
+ + "version 954.1 or newer: "
+ + "<b>TWS: 7497; IB Gateway: 4002</b></html>");
+
+ ConnectionPanel() {
+ HtmlButton connect = new HtmlButton("Connect") {
+ @Override public void actionPerformed() {
+ onConnect();
+ }
+ };
+
+ HtmlButton disconnect = new HtmlButton("Disconnect") {
+ @Override public void actionPerformed() {
+ controller().disconnect();
+ }
+ };
+
+ JPanel p1 = new VerticalPanel();
+ p1.add( "Host", m_host);
+ p1.add( "Port", m_port);
+ p1.add( "Client ID", m_clientId);
+ if ( m_connectionConfiguration.getDefaultConnectOptions() != null ) {
+ p1.add( "Connect options", m_connectOptionsTF);
+ }
+ p1.add( "", m_defaultPortNumberLabel);
+
+ JPanel p2 = new VerticalPanel();
+ p2.add( connect);
+ p2.add( disconnect);
+ p2.add( Box.createVerticalStrut(20));
+
+ JPanel p3 = new VerticalPanel();
+ p3.setBorder( new EmptyBorder( 20, 0, 0, 0));
+ p3.add( "Connection status: ", m_status);
+
+ JPanel p4 = new JPanel( new BorderLayout() );
+ p4.add( p1, BorderLayout.WEST);
+ p4.add( p2);
+ p4.add( p3, BorderLayout.SOUTH);
+
+ setLayout( new BorderLayout() );
+ add( p4, BorderLayout.NORTH);
+ }
+
+ void onConnect() {
+ int port = Integer.parseInt( m_port.getText() );
+ int clientId = Integer.parseInt( m_clientId.getText() );
+ controller().connect( m_host.getText(), port, clientId, m_connectOptionsTF.getText());
+ }
+ }
+
+ private static class Logger implements ILogger {
+ final private JTextArea m_area;
+
+ Logger( JTextArea area) {
+ m_area = area;
+ }
+
+ @Override public void log(final String str) {
+ SwingUtilities.invokeLater(() -> {
+// m_area.append(str);
+//
+// Dimension d = m_area.getSize();
+// m_area.scrollRectToVisible( new Rectangle( 0, d.height, 1, 1) );
+ });
+ }
+ }
+}
+
+// do clearing support
+// change from "New" to something else
+// more dn work, e.g. deltaNeutralValidation
+// add a "newAPI" signature
+// probably should not send F..A position updates to listeners, at least not to API; also probably not send FX positions; or maybe we should support that?; filter out models or include it
+// finish or remove strat panel
+// check all ps
+// must allow normal summary and ledger at the same time
+// you could create an enum for normal account events and pass segment as a separate field
+// check pacing violation
+// newticktype should break into price, size, and string?
+// give "already subscribed" message if appropriate
+
+// BUGS
+// When API sends multiple snapshot requests, TWS sends error "Snapshot exceeds 100/sec" even when it doesn't
+// When API requests SSF contracts, TWS sends both dividend protected and non-dividend protected contracts. They are indistinguishable except for having different conids.
+// Fundamentals financial summary works from TWS but not from API
+// When requesting fundamental data for IBM, the data is returned but also an error
+// The "Request option computation" method seems to have no effect; no data is ever returned
+// When an order is submitted with the "End time" field, it seems to be ignored; it is not submitted but also no error is returned to API.
+// Most error messages from TWS contain the class name where the error occurred which gets garbled to gibberish during obfuscation; the class names should be removed from the error message
+// If you exercise option from API after 4:30, TWS pops up a message; TWS should never popup a message due to an API request
+// TWS did not desubscribe option vol computation after API disconnect
+// Several error message are misleading or completely wrong, such as when upperRange field is < lowerRange
+// Submit a child stop with no stop price; you get no error, no rejection
+// When a child order is transmitted with a different contract than the parent but no hedge params it sort of works but not really, e.g. contract does not display at TWS, but order is working
+// Switch between frozen and real-time quotes is broken; e.g.: request frozen quotes, then realtime, then request option market data; you don't get bid/ask; request frozen, then an option; you don't get anything
+// TWS pops up mkt data warning message in response to api order
+
+// API/TWS Changes
+// we should add underConid for sec def request sent API to TWS so option chains can be requested properly
+// reqContractDetails needs primary exchange, currently only takes currency which is wrong; all requests taking Contract should be updated
+// reqMktDepth and reqContractDetails does not take primary exchange but it needs to; ideally we could also pass underConid in request
+// scanner results should return primary exchange
+// the check margin does not give nearly as much info as in TWS
+// need clear way to distinguish between order reject and warning
+
+// API Improvements
+// add logging support
+// we need to add dividendProt field to contract description
+// smart live orders should be getting primary exchange sent down
+
+// TWS changes
+// TWS sends acct update time after every value; not necessary
+// support stop price for trailing stop order (currently only for trailing stop-limit)
+// let TWS come with 127.0.0.1 enabled, same is IBG
+// we should default to auto-updating client zero with new trades and open orders
+
+// NOTES TO USERS
+// you can get all orders and trades by setting "master id" in the TWS API config
+// reqManagedAccts() is not needed because managed accounts are sent down on login
+// TickType.LAST_TIME comes for all top mkt data requests
+// all option ticker requests trigger option model calc and response
+// DEV: All Box layouts have max size same as pref size; but center border layout ignores this
+// DEV: Box layout grows items proportionally to the difference between max and pref sizes, and never more than max size
+
+//TWS sends error "Snapshot exceeds 100/sec" even when it doesn't; maybe try flush? or maybe send 100 then pause 1 second? this will take forever; i think the limit needs to be increased
+
+//req open orders can only be done by client 0 it seems; give a message
+//somehow group is defaulting to EqualQuantity if not set; seems wrong
+//i frequently see order canceled - reason: with no text
+//Missing or invalid NonGuaranteed value. error should be split into two messages
+//Rejected API order is downloaded as Inactive open order; rejected orders should never be sen
+//Submitting an initial stop price for trailing stop orders is supported only for trailing stop-limit orders; should be supported for plain trailing stop orders as well
+//EMsgReqOptCalcPrice probably doesn't work since mkt data code was re-enabled
+//barrier price for trail stop lmt orders why not for trail stop too?
+//All API orders default to "All" for F; that's not good
diff --git a/demo/src/main/java/apidemo/Chart.java b/demo/src/main/java/apidemo/Chart.java
new file mode 100755
index 0000000..9273290
--- /dev/null
+++ b/demo/src/main/java/apidemo/Chart.java
@@ -0,0 +1,90 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+import com.ib.controller.Bar;
+
+public class Chart extends JComponent {
+ private static final int width = 5;
+ private int height;
+ private double min;
+ private double max;
+ private final List<Bar> m_rows;
+ private double m_current = 118;
+
+ public void current( double v) { m_current = v; }
+
+ public Chart(List<Bar> rows) {
+ m_rows = rows;
+ }
+
+ @Override protected void paintComponent(Graphics g) {
+ height = getHeight();
+ min = getMin();
+ max = getMax();
+
+ int x = 1;
+ for (Bar bar : m_rows) {
+ int high = getY( bar.high() );
+ int low = getY( bar.low() );
+ int open = getY( bar.open() );
+ int close = getY( bar.close() );
+
+ // draw high/low line
+ g.setColor( Color.black);
+ g.drawLine(x + 1, high, x + 1, low);
+
+ if (bar.close() > bar.open() ) {
+ g.setColor( Color.green);
+ g.fillRect(x, close, 3, open - close);
+ }
+ else {
+ g.setColor( Color.red);
+ g.fillRect(x, open, 3, close - open);
+ }
+
+ x += width;
+ }
+
+ // draw price line
+ g.setColor( Color.black);
+ int y = getY( m_current);
+ g.drawLine( 0, y, m_rows.size() * width, y);
+ }
+
+ /** Convert bar value to y coordinate. */
+ private int getY( double v) {
+ double span = max - min;
+ double pct = (v - min) / span;
+ double val = pct * height + .5;
+ return height - (int)val;
+ }
+
+ @Override public Dimension getPreferredSize() {// why on main screen 1 is okay but not here?
+ return new Dimension( m_rows.size() * width, 100);
+ }
+
+ private double getMin() {
+ double min = Double.MAX_VALUE;
+ for( Bar bar : m_rows) {
+ min = Math.min( min, bar.low() );
+ }
+ return min;
+ }
+
+ private double getMax() {
+ double max = 0;
+ for( Bar bar : m_rows) {
+ max = Math.max( max, bar.high() );
+ }
+ return max;
+ }
+}
diff --git a/demo/src/main/java/apidemo/ComboPanel.java b/demo/src/main/java/apidemo/ComboPanel.java
new file mode 100755
index 0000000..8fba7e2
--- /dev/null
+++ b/demo/src/main/java/apidemo/ComboPanel.java
@@ -0,0 +1,620 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.ComboLeg;
+import com.ib.client.Contract;
+import com.ib.client.ContractDetails;
+import com.ib.client.DeltaNeutralContract;
+import com.ib.client.Order;
+import com.ib.client.OrderState;
+import com.ib.client.Types.Action;
+import com.ib.client.Types.SecType;
+import com.ib.controller.ApiController.IContractDetailsHandler;
+import com.ib.controller.ApiController.IEfpHandler;
+
+import apidemo.OrdersPanel.OrderRow;
+import apidemo.OrdersPanel.OrdersModel;
+import apidemo.TopModel.TopRow;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.INewTab;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel;
+import apidemo.util.VerticalPanel.HorzPanel;
+
+
+public class ComboPanel extends JPanel implements INewTab {
+ private final OrdersModel m_ordersModel = new OrdersModel() {
+ @Override protected boolean shouldAdd(Contract contract, Order order, OrderState orderState) {
+ return contract.isCombo();
+ }
+ };
+
+ ComboPanel(MarketDataPanel parentPanel) {
+ NewTabbedPanel tabs = new NewTabbedPanel();
+ tabs.addTab( "Spreads", new SpreadsPanel(parentPanel) );
+ tabs.addTab( "EFP's", new EfpPanel(parentPanel) );
+
+ final JTable ordersTable = new JTable( m_ordersModel);
+ JScrollPane ordersScroll = new JScrollPane( ordersTable);
+ ordersScroll.setBorder( new TitledBorder( "Live Combo Orders"));
+
+ ordersTable.addMouseListener( new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ onDoubleClick( ordersTable.getSelectedRow() );
+ }
+ }
+ });
+
+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS) );
+ add( tabs);
+ add( ordersScroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ ApiDemo.INSTANCE.controller().reqLiveOrders( m_ordersModel);
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+
+ protected void onDoubleClick(int row) {
+ if (row != -1) {
+ OrderRow order = m_ordersModel.get( row);
+ TicketDlg dlg = new TicketDlg( order.m_contract, order.m_order);
+ dlg.setVisible( true);
+ }
+ }
+
+ static class SpreadsPanel extends JPanel {
+ private final Contract m_contract = new Contract();
+ private final TCombo<Action> m_action = new TCombo<>( Action.values() );
+ private final UpperField m_ratio = new UpperField( "1");
+ private final ContractPanel m_contractPanel = new ComboContractPanel();
+ private final List<LegRow> m_legRows = new ArrayList<>();
+ private final LegModel m_legsModel = new LegModel( m_legRows);
+ private final JTable m_legsTable = new JTable( m_legsModel);
+ private final TopModel m_mktDataModel;
+ private final JTable m_mktDataTable;
+ private DeltaNeutralContract m_dnContract;
+ private final DnPanel m_dnPanel = new DnPanel();
+ private final JLabel m_dnText = new JLabel();
+
+ SpreadsPanel(MarketDataPanel parentPanel) {
+ m_mktDataModel = new TopModel(parentPanel);
+ m_mktDataTable = new JTable( m_mktDataModel);
+
+ HtmlButton addLeg = new HtmlButton( "Add Leg") {
+ @Override protected void actionPerformed() {
+ onAddLeg();
+ }
+ };
+
+ HtmlButton removeLeg = new HtmlButton( "Remove Selected Leg") {
+ @Override protected void actionPerformed() {
+ onRemoveLeg();
+ }
+ };
+
+ HtmlButton removeLegs = new HtmlButton( "Clear All Legs") {
+ @Override protected void actionPerformed() {
+ onRemoveLegs();
+ }
+ };
+
+ HtmlButton mktData = new HtmlButton( "Request Market Data") {
+ @Override protected void actionPerformed() {
+ onReqMktData();
+ }
+ };
+
+ HtmlButton placeOrder = new HtmlButton( "Place Order") {
+ @Override protected void actionPerformed() {
+ onPlaceOrder();
+ }
+ };
+
+ VerticalPanel buts = new VerticalPanel();
+ buts.add( addLeg);
+ buts.add( removeLeg);
+ buts.add( mktData);
+ buts.add( placeOrder);
+ buts.add( Box.createVerticalStrut(10));
+ buts.add( removeLegs);
+
+ JScrollPane legsScroll = new JScrollPane( m_legsTable);
+ legsScroll.setPreferredSize( new Dimension( 100, 1));
+ legsScroll.setBorder( new EmptyBorder( 0, 0, 0, 0));
+
+ JPanel legsPanel = new JPanel();
+ legsPanel.setBorder( new TitledBorder( "Combo Legs"));
+ legsPanel.setLayout( new BorderLayout() );
+ legsPanel.add( legsScroll);
+ legsPanel.add( m_dnText, BorderLayout.SOUTH);
+
+ HorzPanel horzPanel = new HorzPanel();
+ horzPanel.setBorder( new TitledBorder( "Build Combo"));
+ horzPanel.add( m_contractPanel);
+ horzPanel.add( buts);
+ horzPanel.add( legsPanel);
+ horzPanel.add( m_dnPanel);
+
+ JScrollPane mktDataScroll = new JScrollPane( m_mktDataTable);
+ mktDataScroll.setBorder( new TitledBorder( "Combo Market Data"));
+
+ setLayout( new BorderLayout() );
+ add( horzPanel, BorderLayout.NORTH);
+ add( mktDataScroll);
+ }
+
+ protected void onAddLeg() {
+ m_contractPanel.onOK();
+ ApiDemo.INSTANCE.controller().reqContractDetails( m_contract, list -> {
+ for (ContractDetails details : list) {
+ addLeg( details);
+ }
+ });
+ }
+
+ protected void onRemoveLeg() {
+ int[] indexes = m_legsTable.getSelectedRows();
+ for (int i = indexes.length - 1; i >= 0; i--) {
+ int index = indexes[i];
+ m_legRows.remove( index);
+ }
+ m_legsModel.fireTableDataChanged();
+ }
+
+ protected void onRemoveLegs() {
+ m_legRows.clear();
+ m_legsModel.fireTableDataChanged();
+ }
+
+ protected void addLeg(ContractDetails contractDetails) {
+ Contract c = contractDetails.contract();
+ ComboLeg leg = new ComboLeg();
+ leg.action( m_action.getSelectedItem() );
+ leg.ratio( m_ratio.getInt() );
+ leg.conid( c.conid() );
+ leg.exchange( c.exchange() );
+
+ LegRow row = new LegRow( c, leg);
+ m_legRows.add( row);
+ m_legsModel.fireTableDataChanged();
+ }
+
+ protected void onReqMktData() {
+ Contract combo = getComboContractFromLegs();
+ if (combo != null) {
+ m_mktDataModel.addRow( getComboContractFromLegs() );
+ }
+ }
+
+ private Contract getComboContractFromLegs() {
+ if (m_legRows.size() < 2) {
+ return null;
+ }
+
+ LegRow leg = m_legRows.get( 0);
+
+ Contract comboContract = new Contract();
+ comboContract.secType( SecType.BAG);
+ comboContract.currency( leg.m_contract.currency() );
+ comboContract.exchange( leg.m_contract.exchange() );
+ comboContract.symbol( comboContract.exchange().equals( "SMART")
+ ? leg.m_contract.currency()
+ : leg.m_contract.symbol() );
+
+ for (LegRow row : m_legRows) {
+ comboContract.comboLegs().add( row.m_leg);
+ }
+
+ if (m_dnContract != null) {
+ comboContract.deltaNeutralContract( m_dnContract);
+ }
+
+ return comboContract;
+ }
+
+ protected void onPlaceOrder() {
+ Order o = new Order();
+ o.totalQuantity( 1);
+
+ Contract c = getComboContractFromLegs();
+ TicketDlg dlg = new TicketDlg( c, o);
+ dlg.setVisible( true);
+ }
+
+ class ComboContractPanel extends ContractPanel {
+ ComboContractPanel() {
+ super( m_contract);
+ removeAll();
+
+ VerticalPanel p1 = new VerticalPanel();
+ p1.setAlignmentY(0);
+ p1.add( "Action", m_action);
+ p1.add( "Ratio", m_ratio);
+ p1.add( "Symbol", m_symbol);
+ p1.add( "Sec type", m_secType);
+ p1.add( "Last trade date", m_lastTradeDateOrContractMonth);
+ p1.add( "Strike", m_strike);
+
+ VerticalPanel p2 = new VerticalPanel();
+ p2.setAlignmentY(0);
+ p2.add( "Put/call", m_right);
+ p2.add( "Multiplier", m_multiplier);
+ p2.add( "Exchange", m_exchange);
+ p2.add( "Comp. Exch.", m_compExch);
+ p2.add( "Currency", m_currency);
+ p2.add( "Local symbol", m_localSymbol);
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( p1);
+ add( p2);
+ }
+ }
+
+ class DnPanel extends VerticalPanel {
+ UpperField m_symbol = new UpperField();
+ TCombo<SecType> m_secType = new TCombo<>( SecType.values() );
+ UpperField m_lastTradeDateOrContractMonth = new UpperField();
+ UpperField m_exchange = new UpperField();
+ UpperField m_currency = new UpperField();
+ UpperField m_delta = new UpperField();
+ UpperField m_price = new UpperField();
+
+ DnPanel() {
+ HtmlButton but = new HtmlButton( "Set") {
+ @Override protected void actionPerformed() {
+ onAdd();
+ }
+ };
+
+ setBorder( new TitledBorder( "Delta-Neutral"));
+ add( "Symbol", m_symbol);
+ add( "Sec type", m_secType);
+ add( "Last trade date or contract month", m_lastTradeDateOrContractMonth);
+ add( "Exchange", m_exchange, but);
+ add( "Currency", m_currency);
+ add( "Delta", m_delta);
+ add( "Price", m_price);
+ }
+
+ protected void onAdd() {
+ Contract dn = new Contract();
+ dn.symbol( m_symbol.getText().toUpperCase() );
+ dn.secType( m_secType.getSelectedItem() );
+ dn.lastTradeDateOrContractMonth( m_lastTradeDateOrContractMonth.getText() );
+ dn.exchange( m_exchange.getText().toUpperCase() );
+ dn.currency( m_currency.getText().toUpperCase() );
+
+ ApiDemo.INSTANCE.controller().reqContractDetails(dn, list -> {
+ if (list.size() == 1) {
+ Contract c = list.get( 0).contract();
+ m_dnContract = new DeltaNeutralContract( c.conid(), m_delta.getDouble(), m_price.getDouble() );
+ m_dnText.setText( String.format( "Delta-neutral: %s Delta: %s Price: %s", c.description(), m_delta.getText(), m_price.getText() ) );
+ }
+ else {
+ ApiDemo.INSTANCE.show( "DN description does not define a uniqe contract");
+ m_dnContract = null;
+ m_dnText.setText( null);
+ }
+ });
+ }
+ }
+ }
+
+ static class EfpPanel extends JPanel {
+ private final UpperField m_symbol = new UpperField( "IBM");
+ private final UpperField m_futExch = new UpperField( "ONE");
+ private final UpperField m_lastTradeDate = new UpperField( "201309");
+ private final UpperField m_stkExch = new UpperField( "SMART");
+ private final List<LegRow> m_legRows = new ArrayList<>();
+ private final LegModel m_legsModel = new LegModel( m_legRows);
+ private final JTable m_legsTable = new JTable( m_legsModel);
+ private final EfpModel m_efpModel;
+ private final JCheckBox m_divProt = new JCheckBox();
+
+ EfpPanel(MarketDataPanel parentPanel) {
+ m_efpModel = new EfpModel(parentPanel);
+
+ HtmlButton addLeg = new HtmlButton( "Create EFP") {
+ @Override protected void actionPerformed() {
+ onCreateEfp();
+ }
+ };
+
+ HtmlButton mktData = new HtmlButton( "Request Market Data") {
+ @Override protected void actionPerformed() {
+ onReqMktData();
+ }
+ };
+
+ HtmlButton placeOrder = new HtmlButton( "Place Order") {
+ @Override protected void actionPerformed() {
+ onPlaceOrder();
+ }
+ };
+
+ VerticalPanel params = new VerticalPanel();
+ params.add( "Symbol", m_symbol);
+ params.add( "Futures exchange", m_futExch);
+ params.add( "Last trade date or contract month", m_lastTradeDate);
+ params.add( "Stock exchange", m_stkExch);
+ params.add( "Dividend protected", m_divProt);
+
+ VerticalPanel buts = new VerticalPanel();
+ buts.add( addLeg);
+ buts.add( mktData);
+ buts.add( placeOrder);
+
+ JScrollPane legsScroll = new JScrollPane( m_legsTable);
+ legsScroll.setPreferredSize( new Dimension( 100, 1));
+ legsScroll.setBorder( new TitledBorder( "Combo Legs"));
+
+ HorzPanel horzPanel = new HorzPanel();
+ horzPanel.setBorder( new TitledBorder( "Build Combo"));
+ horzPanel.add( params);
+ horzPanel.add( buts);
+ horzPanel.add( legsScroll);
+
+ JTable efpTable = new JTable( m_efpModel);
+ JScrollPane efpScroll = new JScrollPane( efpTable);
+ efpScroll.setBorder( new TitledBorder( "EFP Market Data"));
+
+ setLayout( new BorderLayout() );
+ add( horzPanel, BorderLayout.NORTH);
+ add( efpScroll);
+ }
+
+ protected void onCreateEfp() {
+ m_legRows.clear();
+ m_legsModel.fireTableDataChanged();
+
+ Contract fut = new Contract();
+ fut.symbol( m_symbol.getText() );
+ fut.secType( SecType.FUT);
+ fut.exchange( m_futExch.getText() );
+ fut.lastTradeDateOrContractMonth( m_lastTradeDate.getText() );
+ fut.currency( "USD");
+
+ ApiDemo.INSTANCE.controller().reqContractDetails( fut, new IContractDetailsHandler() {
+ @Override public void contractDetails(List<ContractDetails> list) {
+ // if two futures are returned, assume that the first is is no div prot and the
+ // second one is div prot; unfortunately TWS does not send down the div prot flag
+ if (list.size() == 2) {
+ int i = m_divProt.isSelected() ? 1 : 0;
+ addFutLeg( list.get( i) );
+ }
+ else if (list.size() != 1) {
+ ApiDemo.INSTANCE.show( "Request does not define a valid unique futures contract");
+ }
+ else {
+ addFutLeg( list.get( 0) );
+ }
+ }
+ void addFutLeg(ContractDetails details) {
+ addLeg( details.contract(), Action.BUY, 1);
+ }
+ });
+
+ Contract stk = new Contract();
+ stk.symbol( m_symbol.getText() );
+ stk.secType( SecType.STK);
+ stk.exchange( m_stkExch.getText() );
+ stk.currency( "USD");
+
+ ApiDemo.INSTANCE.controller().reqContractDetails( stk, list -> {
+ for (ContractDetails data : list) {
+ addLeg( data.contract(), Action.SELL, 100);
+ }
+ });
+ }
+
+ protected void addLeg(Contract contract, Action action, int ratio) {
+ ComboLeg leg = new ComboLeg();
+ leg.action( action);
+ leg.ratio( ratio);
+ leg.conid( contract.conid() );
+ leg.exchange( contract.exchange() );
+
+ LegRow row = new LegRow( contract, leg);
+ m_legRows.add( row);
+ m_legsModel.fireTableDataChanged();
+ }
+
+ protected void onRemoveLeg() {
+ int i = m_legsTable.getSelectedRow();
+ if (i != -1) {
+ m_legRows.remove( i);
+ m_legsModel.fireTableDataChanged();
+ }
+ }
+
+ protected void onReqMktData() {
+ m_efpModel.addRow( getComboContractFromLegs() );
+ }
+
+ private Contract getComboContractFromLegs() {
+ if (m_legRows.size() < 2) {
+ return null;
+ }
+
+ LegRow leg = m_legRows.get( 0);
+
+ Contract comboContract = new Contract();
+ comboContract.secType( SecType.BAG);
+ comboContract.currency( leg.m_contract.currency() );
+ comboContract.exchange( "SMART");
+ comboContract.symbol( "USD");
+
+ for (LegRow row : m_legRows) {
+ comboContract.comboLegs().add( row.m_leg);
+ }
+
+ return comboContract;
+ }
+
+ protected void onPlaceOrder() {
+ Order o = new Order();
+ o.totalQuantity( 1);
+
+ Contract c = getComboContractFromLegs();
+ TicketDlg dlg = new TicketDlg( c, o);
+ dlg.setVisible( true);
+ }
+
+ static class EfpModel extends AbstractTableModel {
+ List<EfpRow> m_rows = new ArrayList<>();
+ MarketDataPanel m_parentPanel;
+
+ EfpModel(MarketDataPanel parentPanel) {
+ m_parentPanel = parentPanel;
+ }
+
+ void addRow(Contract contract) {
+ EfpRow row = new EfpRow( this, contract.description(), m_parentPanel );
+ m_rows.add( row);
+ ApiDemo.INSTANCE.controller().reqEfpMktData( contract, "", false, false, row);
+ fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ }
+
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 10;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Description";
+ case 1: return "Bid";
+ case 2: return "Ask";
+ case 3: return "Basis Points";
+ case 4: return "Formatted";
+ case 5: return "Implied Future";
+ case 6: return "Hold Days";
+ case 7: return "Future Expiry";
+ case 8: return "Dividend Impact";
+ case 9: return "Dividends to Expiry";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ EfpRow row = m_rows.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_description;
+ case 1: return row.m_bid;
+ case 2: return row.m_ask;
+ case 3: return row.m_basisPoints;
+ case 4: return row.m_formattedBasisPoints;
+ case 5: return row.m_impliedFuture;
+ case 6: return row.m_holdDays;
+ case 7: return row.m_futureLastTradeDate;
+ case 8: return row.m_dividendImpact;
+ case 9: return row.m_dividendsToLastTradeDate;
+ default: return null;
+ }
+ }
+
+ class EfpRow extends TopRow implements IEfpHandler {
+ double m_basisPoints;
+ String m_formattedBasisPoints;
+ double m_impliedFuture;
+ int m_holdDays;
+ String m_futureLastTradeDate;
+ double m_dividendImpact;
+ double m_dividendsToLastTradeDate;
+
+ EfpRow(AbstractTableModel model, String description, MarketDataPanel parentPanel) {
+ super(model, description, parentPanel);
+ }
+
+ @Override public void tickEFP(int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureLastTradeDate, double dividendImpact, double dividendsToLastTradeDate) {
+ m_basisPoints = basisPoints;
+ m_formattedBasisPoints = formattedBasisPoints;
+ m_impliedFuture = impliedFuture;
+ m_holdDays = holdDays;
+ m_futureLastTradeDate = futureLastTradeDate;
+ m_dividendImpact = dividendImpact;
+ m_dividendsToLastTradeDate = dividendsToLastTradeDate;
+
+ m_model.fireTableDataChanged();
+ }
+ }
+ }
+ }
+
+ static class LegRow {
+ Contract m_contract;
+ ComboLeg m_leg = new ComboLeg();
+
+ LegRow(Contract c, ComboLeg leg) {
+ m_contract = c;
+ m_leg = leg;
+ }
+ }
+
+ static class LegModel extends AbstractTableModel {
+ List<LegRow> m_legRows;
+
+ LegModel( List<LegRow> legRows) {
+ m_legRows = legRows;
+ }
+
+ @Override public int getRowCount() {
+ return m_legRows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 3;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Action";
+ case 1: return "Ratio";
+ case 2: return "Description";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ LegRow row = m_legRows.get( rowIn);
+ switch( col) {
+ case 0: return row.m_leg.action();
+ case 1: return row.m_leg.ratio();
+ case 2: return row.m_contract.description();
+ default: return null;
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/ConditionDlg.java b/demo/src/main/java/apidemo/ConditionDlg.java
new file mode 100755
index 0000000..5a1bd45
--- /dev/null
+++ b/demo/src/main/java/apidemo/ConditionDlg.java
@@ -0,0 +1,260 @@
+/* Copyright (C) 2018 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. */
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.ButtonGroup;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTabbedPane;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.ExecutionCondition;
+import com.ib.client.MarginCondition;
+import com.ib.client.OrderCondition;
+import com.ib.client.OrderConditionType;
+import com.ib.client.PercentChangeCondition;
+import com.ib.client.PriceCondition;
+import com.ib.client.TimeCondition;
+import com.ib.client.VolumeCondition;
+
+import apidemo.util.HtmlButton;
+
+/**
+ *
+ * @author mvorobyev
+ */
+public class ConditionDlg extends JDialog implements ChangeListener, ActionListener {
+
+ private JPanel m_conditionPanel;
+ private JPanel m_conditionTypePanel;
+ private JRadioButton m_rbMargin;
+ private JRadioButton m_rbPercent;
+ private JRadioButton m_rbPrice;
+ private JRadioButton m_rbTime;
+ private JRadioButton m_rbTrade;
+ private JRadioButton m_rbVolume;
+ private OrderCondition m_condition;
+ private OnOKPanel m_conditionSubPanel;
+
+ private ContractLookuper m_lookuper;
+
+ ConditionDlg(OrderCondition condition, ContractLookuper lookuper) {
+ initComponents();
+
+ m_condition = condition;
+ m_lookuper = lookuper;
+
+ switch (m_condition.type()) {
+ case Execution:
+ m_rbTrade.setSelected(true);
+ break;
+
+ case Margin:
+ m_rbMargin.setSelected(true);
+ break;
+
+ case PercentChange:
+ m_rbPercent.setSelected(true);
+ break;
+
+ case Price:
+ m_rbPrice.setSelected(true);
+ break;
+
+ case Time:
+ m_rbTime.setSelected(true);
+ break;
+
+ case Volume:
+ m_rbVolume.setSelected(true);
+ break;
+ }
+ }
+
+ private boolean m_isCanceled;
+
+ public boolean isCanceled() {
+ return m_isCanceled;
+ }
+
+ @Override
+ public void setVisible(boolean arg0) {
+ m_isCanceled = true;
+
+ super.setVisible(arg0);
+ }
+
+ private void initComponents() {
+
+ JTabbedPane tabbedPane = new JTabbedPane();
+ m_conditionTypePanel = new JPanel();
+ m_rbPrice = new JRadioButton();
+ m_rbMargin = new JRadioButton();
+ m_rbTrade = new JRadioButton();
+ m_rbTime = new JRadioButton();
+ m_rbVolume = new JRadioButton();
+ m_rbPercent = new JRadioButton();
+ m_conditionPanel = new JPanel();
+
+ ButtonGroup group = new ButtonGroup();
+
+ group.add(m_rbMargin);
+ group.add(m_rbPrice);
+ group.add(m_rbPercent);
+ group.add(m_rbTime);
+ group.add(m_rbTrade);
+ group.add(m_rbVolume);
+
+ m_rbPrice.setText("Price");
+ m_rbPrice.addChangeListener(this);
+
+ m_rbMargin.setText("Margin Cushion");
+ m_rbMargin.addChangeListener(this);
+
+ m_rbTrade.setText("Trade");
+ m_rbTrade.addChangeListener(this);
+
+ m_rbTime.setText("Time");
+ m_rbTime.addChangeListener(this);
+
+ m_rbVolume.setText("Volume");
+ m_rbVolume.addChangeListener(this);
+
+ m_rbPercent.setText("Percent Change");
+ m_rbPercent.addChangeListener(this);
+
+ GroupLayout jConditionTypePanelLayout = new GroupLayout(m_conditionTypePanel);
+ m_conditionTypePanel.setLayout(jConditionTypePanelLayout);
+ jConditionTypePanelLayout.setHorizontalGroup(
+ jConditionTypePanelLayout.createParallelGroup(Alignment.LEADING)
+ .addGroup(jConditionTypePanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jConditionTypePanelLayout.createParallelGroup(Alignment.LEADING)
+ .addComponent(m_rbTrade)
+ .addComponent(m_rbMargin)
+ .addComponent(m_rbPrice))
+ .addPreferredGap(ComponentPlacement.RELATED, 31, Short.MAX_VALUE)
+ .addGroup(jConditionTypePanelLayout.createParallelGroup(Alignment.LEADING)
+ .addComponent(m_rbVolume)
+ .addComponent(m_rbPercent)
+ .addComponent(m_rbTime))
+ .addContainerGap())
+ );
+ jConditionTypePanelLayout.setVerticalGroup(
+ jConditionTypePanelLayout.createParallelGroup(Alignment.LEADING)
+ .addGroup(jConditionTypePanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jConditionTypePanelLayout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(m_rbPrice)
+ .addComponent(m_rbTime))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(jConditionTypePanelLayout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(m_rbMargin)
+ .addComponent(m_rbVolume))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(jConditionTypePanelLayout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(m_rbTrade)
+ .addComponent(m_rbPercent))
+ .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ tabbedPane.addTab("Condition type", m_conditionTypePanel);
+ tabbedPane.addTab("Condition", m_conditionPanel);
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ GroupLayout layout = new GroupLayout(getContentPane());
+
+ mainPanel.add(tabbedPane);
+ buttons.add(
+ new HtmlButton("Apply") {
+ protected void actionPerformed() {
+ m_isCanceled = false;
+
+ m_conditionSubPanel.onOK();
+ dispose();
+ }
+ });
+ mainPanel.add(buttons, BorderLayout.SOUTH);
+
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(mainPanel)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(mainPanel)
+ );
+
+ pack();
+ setModalityType(ModalityType.APPLICATION_MODAL);
+ }
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ m_conditionPanel.removeAll();
+
+ if (m_rbMargin.isSelected()) {
+ m_conditionSubPanel = new MarginConditionPanel((MarginCondition) instantiateCondition(MarginCondition.conditionType));
+ }
+
+ if (m_rbPercent.isSelected()) {
+ m_conditionSubPanel = new PercentConditionPanel((PercentChangeCondition) instantiateCondition(PercentChangeCondition.conditionType), m_lookuper);
+ }
+
+ if (m_rbPrice.isSelected()) {
+ m_conditionSubPanel = new PriceConditionPanel((PriceCondition) instantiateCondition(PriceCondition.conditionType), m_lookuper);
+ }
+
+ if (m_rbTime.isSelected()) {
+ m_conditionSubPanel = new TimeConditionPanel((TimeCondition) instantiateCondition(TimeCondition.conditionType));
+ }
+
+ if (m_rbTrade.isSelected()) {
+ m_conditionSubPanel = new TradeConditionPanel((ExecutionCondition) instantiateCondition(ExecutionCondition.conditionType));
+ }
+
+ if (m_rbVolume.isSelected()) {
+ m_conditionSubPanel = new VolumeConditionPanel((VolumeCondition) instantiateCondition(VolumeCondition.conditionType), m_lookuper);
+ }
+
+ m_conditionPanel.add(m_conditionSubPanel);
+ pack();
+ }
+
+ private OrderCondition instantiateCondition(OrderConditionType type) {
+ if (m_condition.type() != type) {
+ m_condition = OrderCondition.create(type);
+ }
+ return m_condition;
+ }
+
+ public OrderCondition condition() {
+ return m_condition;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/ConditionsModel.java b/demo/src/main/java/apidemo/ConditionsModel.java
new file mode 100755
index 0000000..5dcffd0
--- /dev/null
+++ b/demo/src/main/java/apidemo/ConditionsModel.java
@@ -0,0 +1,76 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.ContractCondition;
+import com.ib.client.ContractLookuper;
+import com.ib.client.OrderCondition;
+
+public class ConditionsModel extends AbstractTableModel {
+
+ @Override
+ public void setValueAt(Object val, int row, int col) {
+ if (col == 0) {
+ super.setValueAt(val, row, col);
+
+ return;
+ }
+
+ m_conditions.get(row).conjunctionConnection("and".equals(val.toString()));
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int col) {
+ return col == 1;
+ }
+
+ List<OrderCondition> m_conditions;
+ ContractLookuper m_lookuper;
+
+ public ConditionsModel(List<OrderCondition> conditions, ContractLookuper lookuper) {
+ m_conditions = conditions;
+ m_lookuper = lookuper;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_conditions.size();
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ if (columnIndex == 0) {
+ OrderCondition orderCondition = m_conditions.get(rowIndex);
+
+ return orderCondition instanceof ContractCondition ?
+ ((ContractCondition)orderCondition).toString(m_lookuper) :
+ orderCondition.toString();
+ }
+
+ return m_conditions.get(rowIndex).conjunctionConnection() ? "and" : "or";
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return "Description";
+
+ case 1:
+ return "Logic";
+ }
+
+ return null;
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/ConditionsPanel.java b/demo/src/main/java/apidemo/ConditionsPanel.java
new file mode 100755
index 0000000..cc58dd0
--- /dev/null
+++ b/demo/src/main/java/apidemo/ConditionsPanel.java
@@ -0,0 +1,117 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.FlowLayout;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.Order;
+import com.ib.client.OrderCondition;
+import com.ib.client.OrderConditionType;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.TCombo;
+
+public class ConditionsPanel extends OnOKPanel {
+ /**
+ *
+ */
+ private final JDialog parentDlg;
+ private final Order m_order;
+ private final ConditionsModel m_conditionList;
+ private final JTable m_conditions;
+ private final TCombo<String> m_cancelOrder = new TCombo<>("Submit order", "Cancel order");
+ private final JCheckBox m_ignoreRth = new JCheckBox("Allow condition to be satisfied and activate order outside of regular trading hours");
+
+ public ConditionsPanel(JDialog parentDlg, Order order, final ContractLookuper lookuper) {
+ this.parentDlg = parentDlg;
+ this.m_order = order;
+ m_conditionList = new ConditionsModel(m_order.conditions(), lookuper);
+ m_conditions = new JTable(m_conditionList);
+ m_conditions.addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 1) {
+ return;
+ }
+
+ if (m_conditions.getSelectedColumn() == 0) {
+ ConditionDlg dlg = new ConditionDlg(m_order.conditions().get(m_conditions.getSelectedRow()), lookuper);
+
+ dlg.setLocationRelativeTo(e.getComponent());
+ dlg.setVisible(true);
+ }
+ }
+
+ @Override public void mousePressed(MouseEvent e) { }
+
+ @Override public void mouseReleased(MouseEvent e) { }
+
+ @Override public void mouseEntered(MouseEvent e) { }
+
+ @Override public void mouseExited(MouseEvent e) { }
+ });
+
+ TCombo<String> comboBox = new TCombo<>("and", "or");
+ DefaultCellEditor editor = new DefaultCellEditor(comboBox);
+
+ m_conditions.getColumnModel().getColumn(1).setCellEditor(editor);
+
+ add(new JScrollPane(m_conditions));
+
+ JPanel buttons = new JPanel( new FlowLayout( FlowLayout.CENTER, 15, 5));
+
+ m_cancelOrder.setSelectedIndex(m_order.conditionsCancelOrder() ? 1 : 0);
+ m_ignoreRth.setSelected(m_order.conditionsIgnoreRth());
+
+ buttons.add(m_cancelOrder);
+ buttons.add(m_ignoreRth);
+
+ buttons.add(new HtmlButton("Add") { @Override
+ protected void actionPerformed() {
+ ConditionDlg dlg = new ConditionDlg(OrderCondition.create(OrderConditionType.Price), lookuper);
+
+ dlg.setLocationRelativeTo(this.getParent());
+ dlg.pack();
+ dlg.setVisible(true);
+
+ if (!dlg.isCanceled()) {
+ m_order.conditions().add(dlg.condition());
+ m_conditionList.fireTableDataChanged();
+ }
+ }
+ });
+
+ buttons.add(new HtmlButton("Remove") { @Override
+ protected void actionPerformed() {
+ int iRemove = m_conditions.getSelectedRow();
+
+ if (iRemove >= 0) {
+ m_order.conditions().remove(m_order.conditions().get(iRemove));
+ m_conditionList.fireTableDataChanged();
+ }
+ }
+ });
+
+ add(buttons);
+ this.parentDlg.pack();
+ }
+
+ public OrderCondition onOK() {
+ m_order.conditionsCancelOrder(m_cancelOrder.getSelectedIndex() == 1);
+ m_order.conditionsIgnoreRth(m_ignoreRth.isSelected());
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/ContractConditionPanel.java b/demo/src/main/java/apidemo/ContractConditionPanel.java
new file mode 100755
index 0000000..2a627f5
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractConditionPanel.java
@@ -0,0 +1,23 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.ContractCondition;
+import com.ib.client.ContractLookuper;
+
+public class ContractConditionPanel<T extends ContractCondition> extends OperatorConditionPanel<T> {
+ ContractConditionPanel(T c, ContractLookuper lookuper) {
+ super(c);
+
+ final ContractCondition condition = m_condition;
+
+ add(new ContractLookupButton(condition.conId(), condition.exchange(), lookuper) {
+ protected void actionPerformed(int refConId, String refExchId) {
+ condition.conId(refConId);
+ condition.exchange(refExchId);
+ }
+ });
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/ContractDlg.java b/demo/src/main/java/apidemo/ContractDlg.java
new file mode 100755
index 0000000..6c840c6
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractDlg.java
@@ -0,0 +1,58 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.SwingConstants;
+
+import com.ib.client.Contract;
+
+import apidemo.util.HtmlButton;
+
+
+class ContractDlg extends JDialog {
+ ContractPanel m_contractPanel;
+
+ ContractDlg( JFrame f, Contract c) {
+ super( f, true);
+
+ m_contractPanel = new ContractPanel( c);
+
+ setLayout( new BorderLayout() );
+
+
+ HtmlButton ok = new HtmlButton( "OK") {
+ @Override public void actionPerformed() {
+ onOK();
+ }
+ };
+ ok.setHorizontalAlignment(SwingConstants.CENTER);
+
+ m_contractPanel.addKeyListener( new KeyListener() {
+ @Override public void keyTyped(KeyEvent e) {
+ System.out.println( "lkj");
+ }
+
+ @Override public void keyReleased(KeyEvent e) {
+ }
+
+ @Override public void keyPressed(KeyEvent e) {
+ }
+ });
+
+ add( m_contractPanel);
+ add( ok, BorderLayout.SOUTH);
+ pack();
+ }
+
+ public void onOK() {
+ m_contractPanel.onOK();
+ setVisible( false);
+ }
+}
diff --git a/demo/src/main/java/apidemo/ContractInfoPanel.java b/demo/src/main/java/apidemo/ContractInfoPanel.java
new file mode 100755
index 0000000..ef0f5d4
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractInfoPanel.java
@@ -0,0 +1,266 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.DecimalFormat;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+import com.ib.client.Contract;
+import com.ib.client.ContractDetails;
+import com.ib.client.PriceIncrement;
+import com.ib.client.Types.FundamentalType;
+import com.ib.controller.ApiController.IContractDetailsHandler;
+import com.ib.controller.ApiController.IFundamentalsHandler;
+import com.ib.controller.ApiController.IMarketRuleHandler;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.INewTab;
+import apidemo.util.TCombo;
+import apidemo.util.VerticalPanel;
+
+class ContractInfoPanel extends JPanel {
+ private final Contract m_contract = new Contract();
+ private final NewTabbedPanel m_resultsPanels = new NewTabbedPanel();
+ private static Set<Integer> m_marketRuleIds = new HashSet<>();
+ private final MarketRuleRequestPanel m_marketRuleRequestPanel = new MarketRuleRequestPanel();
+
+ ContractInfoPanel() {
+ NewTabbedPanel m_requestPanels = new NewTabbedPanel();
+ m_requestPanels.addTab( "Contract details", new DetailsRequestPanel() );
+ m_requestPanels.addTab( "Fundamentals", new FundaRequestPanel() );
+ m_requestPanels.addTab( "Market Rules", m_marketRuleRequestPanel );
+
+ setLayout( new BorderLayout() );
+ add( m_requestPanels, BorderLayout.NORTH);
+ add( m_resultsPanels);
+ }
+
+ class DetailsRequestPanel extends JPanel {
+ ContractPanel m_contractPanel = new ContractPanel( m_contract);
+
+ DetailsRequestPanel() {
+ HtmlButton but = new HtmlButton( "Query") {
+ @Override protected void actionPerformed() {
+ onQuery();
+ }
+ };
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( m_contractPanel);
+ add( Box.createHorizontalStrut(20));
+ add( but);
+ }
+
+ void onQuery() {
+ m_contractPanel.onOK();
+
+ DetailsResultsPanel panel = new DetailsResultsPanel();
+ m_resultsPanels.addTab( m_contract.symbol() + " " + "Description", panel, true, true);
+ ApiDemo.INSTANCE.controller().reqContractDetails(m_contract, panel);
+ }
+ }
+
+ class DetailsResultsPanel extends JPanel implements IContractDetailsHandler {
+ JLabel m_label = new JLabel();
+ JTextArea m_text = new JTextArea();
+
+ DetailsResultsPanel() {
+ JScrollPane scroll = new JScrollPane( m_text);
+
+ setLayout( new BorderLayout() );
+ add( m_label, BorderLayout.NORTH);
+ add( scroll);
+ }
+
+ @Override public void contractDetails(List<ContractDetails> list) {
+ // set label
+ if (list.size() == 0) {
+ m_label.setText( "No matching contracts were found");
+ }
+ else if (list.size() > 1) {
+ m_label.setText( list.size() + " contracts returned; showing first contract only");
+ }
+ else {
+ m_label.setText( null);
+ }
+
+ // set text
+ if (list.size() == 0) {
+ m_text.setText( null);
+ }
+ else {
+ m_text.setText( list.get( 0).toString() );
+ }
+ if (list.size() > 0 && list.get( 0).marketRuleIds() != null) {
+ for (String s : list.get( 0).marketRuleIds().split(",")){
+ m_marketRuleIds.add(Integer.parseInt(s));
+ }
+ m_marketRuleRequestPanel.m_marketRuleIdCombo.setModel(new DefaultComboBoxModel<>(m_marketRuleIds.toArray(new Integer[m_marketRuleIds.size()])));
+ }
+ }
+ }
+
+ public class FundaRequestPanel extends JPanel {
+ ContractPanel m_contractPanel = new ContractPanel( m_contract);
+ TCombo<FundamentalType> m_type = new TCombo<>( FundamentalType.values() );
+
+ FundaRequestPanel() {
+ HtmlButton but = new HtmlButton( "Query") {
+ @Override protected void actionPerformed() {
+ onQuery();
+ }
+ };
+
+ VerticalPanel rightPanel = new VerticalPanel();
+ rightPanel.add( "Report type", m_type);
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS));
+ add( m_contractPanel);
+ add( Box.createHorizontalStrut(20));
+ add( rightPanel);
+ add( Box.createHorizontalStrut(10));
+ add( but);
+ }
+
+ void onQuery() {
+ m_contractPanel.onOK();
+ FundaResultPanel panel = new FundaResultPanel();
+ FundamentalType type = m_type.getSelectedItem();
+ m_resultsPanels.addTab( m_contract.symbol() + " " + type, panel, true, true);
+ ApiDemo.INSTANCE.controller().reqFundamentals( m_contract, type, panel);
+ }
+ }
+
+ class FundaResultPanel extends JPanel implements INewTab, IFundamentalsHandler {
+ String m_data;
+ JTextArea m_text = new JTextArea();
+
+ FundaResultPanel() {
+ HtmlButton b = new HtmlButton( "View in browser") {
+ @Override protected void actionPerformed() {
+ onView();
+ }
+ };
+
+ JScrollPane scroll = new JScrollPane( m_text);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( b, BorderLayout.EAST);
+ }
+
+ void onView() {
+ try {
+ File file = File.createTempFile( "tws", ".xml");
+ try (PrintStream ps = new PrintStream( file, "UTF-8")) {
+ ps.println(m_text.getText());
+ }
+ Desktop.getDesktop().open( file);
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ ApiDemo.INSTANCE.controller().reqFundamentals(m_contract, FundamentalType.ReportRatios, this);
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+
+ @Override public void fundamentals(String str) {
+ m_data = str;
+ m_text.setText( str);
+ }
+ }
+
+ class MarketRuleRequestPanel extends JPanel {
+ JComboBox<Integer> m_marketRuleIdCombo = new JComboBox<>();
+
+ MarketRuleRequestPanel() {
+ m_marketRuleIdCombo.setPreferredSize(new Dimension(130, 20));
+ m_marketRuleIdCombo.setEditable(true);
+
+ HtmlButton but = new HtmlButton( "Request Market Rule") {
+ @Override protected void actionPerformed() {
+ onRequestMarketRule();
+ }
+ };
+
+ VerticalPanel paramsPanel = new VerticalPanel();
+ paramsPanel.add( "Market Rule Id", m_marketRuleIdCombo, Box.createHorizontalStrut(100), but);
+ setLayout( new BorderLayout() );
+ add( paramsPanel, BorderLayout.NORTH);
+ }
+
+ void onRequestMarketRule() {
+ MarketRuleResultsPanel panel = new MarketRuleResultsPanel();
+ final Object item = m_marketRuleIdCombo.getEditor().getItem();
+ if (item != null) {
+ final String itemString = item.toString();
+ if (!itemString.isEmpty()) {
+ try {
+ int marketRuleId = Integer.parseInt(itemString);
+ m_resultsPanels.addTab("Market Rule Id: " + itemString, panel, true, true);
+ ApiDemo.INSTANCE.controller().reqMarketRule(marketRuleId, panel);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ static class MarketRuleResultsPanel extends JPanel implements IMarketRuleHandler {
+ JLabel m_label = new JLabel();
+ JTextArea m_text = new JTextArea();
+
+ MarketRuleResultsPanel() {
+ JScrollPane scroll = new JScrollPane( m_text);
+
+ setLayout( new BorderLayout() );
+ add( m_label, BorderLayout.NORTH);
+ add( scroll);
+ }
+
+ @Override
+ public void marketRule(int marketRuleId, PriceIncrement[] priceIncrements) {
+ // set text
+ if (priceIncrements.length == 0) {
+ m_text.setText( null);
+ }
+ else {
+ StringBuilder sb = new StringBuilder(256);
+ DecimalFormat df = new DecimalFormat("#.#");
+ df.setMaximumFractionDigits(340);
+
+ sb.append("Market Rule Id: ").append(marketRuleId).append("\n");
+ for (PriceIncrement priceIncrement : priceIncrements) {
+ sb.append("Low Edge: ").append(df.format(priceIncrement.lowEdge())).append(", ")
+ .append("Increment: ").append(df.format(priceIncrement.increment())).append("\n");
+ }
+ m_text.setText( sb.toString());
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/ContractLookupButton.java b/demo/src/main/java/apidemo/ContractLookupButton.java
new file mode 100755
index 0000000..b781007
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractLookupButton.java
@@ -0,0 +1,35 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.event.MouseEvent;
+
+import com.ib.client.ContractLookuper;
+
+import apidemo.util.HtmlButton;
+
+public abstract class ContractLookupButton extends HtmlButton {
+
+ final ContractSearchDlg m_contractSearchdlg;
+
+ public ContractLookupButton(int conId, String exchange, ContractLookuper lookuper) {
+ super("");
+
+ m_contractSearchdlg = new ContractSearchDlg(conId, exchange, lookuper);
+
+ setText(m_contractSearchdlg.refContract() == null ? "search..." : m_contractSearchdlg.refContract());
+ }
+
+ @Override
+ protected void onClicked(MouseEvent e) {
+ m_contractSearchdlg.setLocationRelativeTo(this.getParent());
+ m_contractSearchdlg.setVisible(true);
+
+ setText(m_contractSearchdlg.refContract() == null ? "search..." : m_contractSearchdlg.refContract());
+ actionPerformed();
+ actionPerformed(m_contractSearchdlg.refConId(), m_contractSearchdlg.refExchId());
+ }
+
+ protected abstract void actionPerformed(int refConId, String refExchId);
+}
diff --git a/demo/src/main/java/apidemo/ContractPanel.java b/demo/src/main/java/apidemo/ContractPanel.java
new file mode 100755
index 0000000..115a697
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractPanel.java
@@ -0,0 +1,103 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import javax.swing.JPanel;
+
+import com.ib.client.Contract;
+import com.ib.client.Types.Right;
+import com.ib.client.Types.SecType;
+
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel;
+
+public class ContractPanel extends JPanel {
+ protected UpperField m_conId = new UpperField();
+ protected UpperField m_symbol = new UpperField();
+ protected TCombo<SecType> m_secType = new TCombo<>( SecType.values() );
+ protected UpperField m_lastTradeDateOrContractMonth = new UpperField();
+ protected UpperField m_strike = new UpperField();
+ protected TCombo<Right> m_right = new TCombo<>( Right.values() );
+ protected UpperField m_multiplier = new UpperField();
+ protected UpperField m_exchange = new UpperField();
+ protected UpperField m_compExch = new UpperField();
+ protected UpperField m_currency = new UpperField();
+ protected UpperField m_localSymbol = new UpperField();
+ protected UpperField m_tradingClass = new UpperField();
+
+ private Contract m_contract;
+
+ ContractPanel(Contract c) {
+ m_contract = c;
+
+ if (c.secType() == SecType.None) {
+ m_symbol.setText( "IBM");
+ m_secType.setSelectedItem( SecType.STK);
+ m_exchange.setText( "SMART");
+ m_compExch.setText( "ISLAND");
+ m_currency.setText( "USD");
+ }
+ else {
+ m_symbol.setText( m_contract.symbol());
+ m_secType.setSelectedItem( m_contract.secType() );
+ m_lastTradeDateOrContractMonth.setText( m_contract.lastTradeDateOrContractMonth());
+ m_strike.setText( "" + m_contract.strike() );
+ m_right.setSelectedItem( m_contract.right() );
+ m_multiplier.setText( m_contract.multiplier() );
+ m_exchange.setText( m_contract.exchange());
+ m_compExch.setText( m_contract.primaryExch() );
+ m_currency.setText( m_contract.currency());
+ m_localSymbol.setText( m_contract.localSymbol());
+ m_tradingClass.setText( m_contract.tradingClass() );
+ }
+
+ VerticalPanel p = new VerticalPanel();
+ p.add( "ConId", m_conId);
+ p.add( "Symbol", m_symbol);
+ p.add( "Sec type", m_secType);
+ p.add( "Last trade date or contract month", m_lastTradeDateOrContractMonth);
+ p.add( "Strike", m_strike);
+ p.add( "Put/call", m_right);
+ p.add( "Multiplier", m_multiplier);
+ p.add( "Exchange", m_exchange);
+ p.add( "Comp. Exch.", m_compExch);
+ p.add( "Currency", m_currency);
+ p.add( "Local symbol", m_localSymbol);
+ p.add( "Trading class", m_tradingClass);
+
+ setLayout( new BorderLayout() );
+ add( p);
+ }
+
+ @Override public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+
+ public void onOK() {
+ if (m_contract.isCombo() ) {
+ return;
+ }
+
+ // component exchange is only relevant if exchange is SMART or BEST
+ String exch = m_exchange.getText().toUpperCase();
+ String compExch = exch.equals( "SMART") || exch.equals( "BEST") ? m_compExch.getText().toUpperCase() : null;
+
+ m_contract.conid( m_conId.getInt() );
+ m_contract.symbol( m_symbol.getText().toUpperCase() );
+ m_contract.secType( m_secType.getSelectedItem() );
+ m_contract.lastTradeDateOrContractMonth( m_lastTradeDateOrContractMonth.getText() );
+ m_contract.strike( m_strike.getDouble() );
+ m_contract.right( m_right.getSelectedItem() );
+ m_contract.multiplier( m_multiplier.getText() );
+ m_contract.exchange( exch);
+ m_contract.primaryExch( compExch);
+ m_contract.currency( m_currency.getText().toUpperCase() );
+ m_contract.localSymbol( m_localSymbol.getText().toUpperCase() );
+ m_contract.tradingClass( m_tradingClass.getText().toUpperCase() );
+ }
+}
diff --git a/demo/src/main/java/apidemo/ContractSearchDlg.java b/demo/src/main/java/apidemo/ContractSearchDlg.java
new file mode 100755
index 0000000..6a8d613
--- /dev/null
+++ b/demo/src/main/java/apidemo/ContractSearchDlg.java
@@ -0,0 +1,146 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+
+import com.ib.client.Contract;
+import com.ib.client.ContractDetails;
+import com.ib.client.ContractLookuper;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.Util;
+
+public class ContractSearchDlg extends JDialog {
+
+ private final ContractPanel m_contractPanel;
+ private Contract m_contract = new Contract();
+ private final DefaultListModel<Contract> m_contractList = new DefaultListModel<>();
+ private final JList<Contract> m_contracts = new JList<>(m_contractList);
+ private final ContractLookuper m_lookuper;
+
+ private List<ContractDetails> lookupContract() {
+ //return com.ib.client.Util.lookupContract(ApiDemo.INSTANCE.controller(), m_contract);
+ return m_lookuper.lookupContract(m_contract);
+ }
+
+ ContractSearchDlg(int conId, String exchange, ContractLookuper lookuper) {
+ m_lookuper = lookuper;
+
+ if (conId > 0 && !exchange.isEmpty()) {
+ m_contract.conid(conId);
+ m_contract.exchange(exchange);
+
+ List<ContractDetails> list = lookupContract();
+
+ if (!list.isEmpty()) {
+ m_contract = list.get(0).contract();
+ }
+ }
+
+ m_contractPanel = new ContractPanel(m_contract);
+
+ JPanel buttons = new JPanel( new FlowLayout( FlowLayout.CENTER, 15, 5));
+
+ buttons.add(new HtmlButton("search") {
+ @Override protected void actionPerformed() {
+ onSearch();
+ }
+ });
+
+ m_contracts.setCellRenderer((list, value, index, isSelected, cellHasFocus) -> onGetListCellRenderer(value, isSelected));
+
+ m_contracts.addMouseListener(new MouseListener() {
+
+ @Override public void mouseReleased(MouseEvent e) { }
+ @Override public void mousePressed(MouseEvent e) { }
+ @Override public void mouseExited(MouseEvent e) { }
+ @Override public void mouseEntered(MouseEvent e) { }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() > 1) {
+ dispose();
+ }
+ }
+ });
+
+ add(m_contractPanel, BorderLayout.NORTH);
+ add(buttons, BorderLayout.CENTER);
+ add(m_contracts, BorderLayout.SOUTH);
+
+ pack();
+ setModalityType(ModalityType.APPLICATION_MODAL);
+ Util.closeOnEsc(this);
+ }
+
+ public boolean isSucceeded() { return m_contractList.size() > 0 && m_contracts.getSelectedIndex() >= 0; }
+
+ private Contract selectedContract() {
+ if (m_contractList.isEmpty() || m_contracts.getSelectedIndex() < 0)
+ return m_contract;
+
+ return (m_contractList.get(m_contracts.getSelectedIndex()));
+ }
+
+ int refConId() {
+ return selectedContract().conid();
+ }
+
+ String refExchId() {
+ return selectedContract().exchange();
+ }
+
+ String refContract() {
+ if (selectedContract().conid() == 0 || selectedContract().exchange().isEmpty())
+ return null;
+
+ return selectedContract().symbol() + " " + selectedContract().exchange() + " " + selectedContract().secType() + " " + selectedContract().currency();
+ }
+
+ private void onSearch() {
+ m_contractPanel.onOK();
+
+ m_contract.conid(0);
+ m_contract.tradingClass(null);
+
+ List<ContractDetails> list = lookupContract();
+
+ m_contractList.clear();
+
+ for (ContractDetails el : list) {
+ m_contractList.addElement(el.contract());
+ }
+
+ m_contracts.updateUI();
+ pack();
+ }
+
+ private Component onGetListCellRenderer(Contract value, boolean isSelected) {
+ JPanel panel = new JPanel();
+
+ panel.add(new JLabel(value.symbol()));
+ panel.add(new JLabel(value.exchange()));
+ panel.add(new JLabel(value.secType().toString()));
+ panel.add(new JLabel(value.currency()));
+
+ if (isSelected) {
+ panel.setBackground(Color.LIGHT_GRAY);
+ }
+
+ return panel;
+ }
+}
diff --git a/demo/src/main/java/apidemo/ExercisePanel.java b/demo/src/main/java/apidemo/ExercisePanel.java
new file mode 100755
index 0000000..2152640
--- /dev/null
+++ b/demo/src/main/java/apidemo/ExercisePanel.java
@@ -0,0 +1,118 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JCheckBox;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.border.TitledBorder;
+
+import com.ib.client.Types.ExerciseType;
+import com.ib.client.Types.SecType;
+import com.ib.controller.ApiController.IAccountHandler;
+import com.ib.controller.Position;
+
+import apidemo.AccountInfoPanel.PortfolioModel;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.INewTab;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel;
+import apidemo.util.VerticalPanel.HorzPanel;
+
+
+public class ExercisePanel extends HorzPanel implements INewTab, IAccountHandler {
+ private DefaultListModel<String> m_acctList = new DefaultListModel<>();
+ private JList<String> m_accounts = new JList<>( m_acctList);
+ private String m_selAcct = "";
+ private PortfolioModel m_portfolioModel = new PortfolioModel();
+ private JTable m_portTable = new JTable( m_portfolioModel);
+
+ ExercisePanel() {
+ JScrollPane acctsScroll = new JScrollPane( m_accounts);
+ acctsScroll.setBorder( new TitledBorder( "Select account"));
+
+ JScrollPane portScroll = new JScrollPane( m_portTable);
+ portScroll.setBorder( new TitledBorder( "Select long option position"));
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( acctsScroll);
+ add( portScroll);
+ add( new ExPanel() );
+
+ m_accounts.addListSelectionListener(e -> onAcctChanged());
+ }
+
+ private void onAcctChanged() {
+ int i = m_accounts.getSelectedIndex();
+ if (i != -1) {
+ String selAcct = m_acctList.get( i);
+ if (!selAcct.equals( m_selAcct) ) {
+ m_selAcct = selAcct;
+ m_portfolioModel.clear();
+ ApiDemo.INSTANCE.controller().reqAccountUpdates(true, m_selAcct, this);
+ }
+ }
+ }
+
+ class ExPanel extends VerticalPanel {
+ TCombo<ExerciseType> m_combo = new TCombo<>( ExerciseType.values() );
+ UpperField m_qty = new UpperField( "1");
+ JCheckBox m_override = new JCheckBox();
+
+ ExPanel() {
+ HtmlButton but = new HtmlButton( "Go") {
+ protected void actionPerformed() {
+ onExercise();
+ }
+ };
+
+ m_combo.removeItem( ExerciseType.None);
+
+ add( "Action", m_combo);
+ add( "Quantity", m_qty);
+ add( "Override", m_override);
+ add( but);
+ }
+
+ void onExercise() {
+ String account = m_accounts.getSelectedValue();
+ int i = m_portTable.getSelectedRow();
+ if (i != -1 && account != null) {
+ Position position = m_portfolioModel.getPosition( i);
+ ApiDemo.INSTANCE.controller().exerciseOption(account, position.contract(), m_combo.getSelectedItem(), m_qty.getInt(), m_override.isSelected() );
+ }
+ }
+ }
+
+ /** Show long option positions only. */
+ @Override public void updatePortfolio(Position position) {
+ if (position.account().equals( m_selAcct) && position.contract().secType() == SecType.OPT) {
+ m_portfolioModel.update( position);
+ }
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ for (String account : ApiDemo.INSTANCE.accountList() ) {
+ m_acctList.addElement( account);
+ }
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+
+ @Override public void accountValue(String account, String key, String value, String currency) {
+ }
+
+ @Override public void accountTime(String timeStamp) {
+ }
+
+ @Override public void accountDownloadEnd(String account) {
+ }
+}
diff --git a/demo/src/main/java/apidemo/FamilyCodesPanel.java b/demo/src/main/java/apidemo/FamilyCodesPanel.java
new file mode 100755
index 0000000..70ee326
--- /dev/null
+++ b/demo/src/main/java/apidemo/FamilyCodesPanel.java
@@ -0,0 +1,122 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.FamilyCode;
+import com.ib.controller.ApiController.IFamilyCodesHandler;
+
+import apidemo.AccountInfoPanel.Table;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+
+public class FamilyCodesPanel extends NewTabPanel {
+ private FamilyCodesModel m_model = new FamilyCodesModel();
+
+ FamilyCodesPanel() {
+ HtmlButton requestFamilyCodesButton = new HtmlButton( "Request Family Codes") {
+ protected void actionPerformed() {
+ requestFamilyCodes();
+ }
+ };
+
+ HtmlButton clearFamilyCodesButton = new HtmlButton( "Clear Family Codes") {
+ protected void actionPerformed() {
+ clearFamilyCodes();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( requestFamilyCodesButton);
+ buts.add( clearFamilyCodesButton);
+
+ JTable table = new Table( m_model, 2);
+ JScrollPane scroll = new JScrollPane( table);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ clearFamilyCodes();
+ }
+
+ private void requestFamilyCodes() {
+ ApiDemo.INSTANCE.controller().reqFamilyCodes( m_model);
+ }
+
+ private void clearFamilyCodes() {
+ m_model.clear();
+ }
+
+ private class FamilyCodesModel extends AbstractTableModel implements IFamilyCodesHandler {
+ List<FamilyCodeRow> m_list = new ArrayList<>();
+
+ @Override public void familyCodes(FamilyCode[] familyCodes) {
+ for (FamilyCode familyCode : familyCodes){
+ FamilyCodeRow row = new FamilyCodeRow();
+ m_list.add( row);
+ row.update(familyCode.accountID(), familyCode.familyCodeStr());
+ }
+ m_model.fireTableDataChanged();
+ }
+
+ public void clear() {
+ m_list.clear();
+ fireTableDataChanged();
+ }
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 2;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Account ID";
+ case 1: return "Family Code";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ FamilyCodeRow row = m_list.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_accountID;
+ case 1: return row.m_familyCodeStr;
+ default: return null;
+ }
+ }
+
+ }
+
+ private static class FamilyCodeRow {
+ String m_accountID;
+ String m_familyCodeStr;
+
+ void update(String accountID, String familyCodeStr) {
+ m_accountID = accountID;
+ m_familyCodeStr = familyCodeStr;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/Histogram.java b/demo/src/main/java/apidemo/Histogram.java
new file mode 100755
index 0000000..7bdc8ea
--- /dev/null
+++ b/demo/src/main/java/apidemo/Histogram.java
@@ -0,0 +1,57 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.HistogramEntry;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+public class Histogram extends JComponent {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private static final int m_barHeight = 15;
+ private final List<HistogramEntry> m_rows;
+ private static final int m_x0 = 40;
+
+ public Histogram(List<HistogramEntry> rows) {
+ m_rows = rows;
+ }
+
+ @Override protected void paintComponent(Graphics g) {
+ int y = 0;
+ long max = getMax();
+
+ int width = getWidth() - m_x0;
+
+ for (HistogramEntry bar : m_rows) {
+ int x1 = (int)((bar.size * width) / max);
+
+ String label = bar.price + "";
+
+ g.setColor(Color.red);
+ g.fillRect(m_x0, y, x1, m_barHeight);
+ g.setColor(Color.black);
+ g.drawString(label, 0, y + m_barHeight - 3);
+ g.drawRect(m_x0, y, x1, m_barHeight);
+
+ y += m_barHeight;
+ }
+ }
+
+ long getMax() {
+ return m_rows.stream().map(entry -> entry.size).max(Long::compare).orElse((long) -1);
+ }
+
+ @Override public Dimension getPreferredSize() {// why on main screen 1 is okay but not here?
+ return new Dimension(100, m_rows.size() * m_barHeight);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/HistoricalTickBidAskModel.java b/demo/src/main/java/apidemo/HistoricalTickBidAskModel.java
new file mode 100755
index 0000000..4387266
--- /dev/null
+++ b/demo/src/main/java/apidemo/HistoricalTickBidAskModel.java
@@ -0,0 +1,60 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.HistoricalTickBidAsk;
+
+class HistoricalTickBidAskModel extends AbstractTableModel {
+
+ private List<HistoricalTickBidAsk> m_rows;
+
+ public HistoricalTickBidAskModel(List<HistoricalTickBidAsk> rows) {
+ m_rows = rows;
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 6;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ HistoricalTickBidAsk row = m_rows.get(rowIndex);
+
+ switch (columnIndex) {
+ case 0: return row.time();
+ case 1: return row.mask();
+ case 2: return row.priceBid();
+ case 3: return row.priceAsk();
+ case 4: return row.sizeBid();
+ case 5: return row.sizeAsk();
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Mask";
+ case 2: return "Price Bid";
+ case 3: return "Price Ask";
+ case 4: return "Size Bid";
+ case 5: return "Size Ask";
+ }
+
+ return super.getColumnName(column);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/HistoricalTickLastModel.java b/demo/src/main/java/apidemo/HistoricalTickLastModel.java
new file mode 100755
index 0000000..f907361
--- /dev/null
+++ b/demo/src/main/java/apidemo/HistoricalTickLastModel.java
@@ -0,0 +1,58 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.HistoricalTickLast;
+
+class HistoricalTickLastModel extends AbstractTableModel {
+
+ private List<HistoricalTickLast> m_rows;
+
+ public HistoricalTickLastModel(List<HistoricalTickLast> rows) {
+ m_rows = rows;
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 6;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ HistoricalTickLast row = m_rows.get(rowIndex);
+
+ switch (columnIndex) {
+ case 0: return row.time();
+ case 1: return row.mask();
+ case 2: return row.price();
+ case 3: return row.size();
+ case 4: return row.exchange();
+ case 5: return row.specialConditions();
+ }
+ return null;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Mask";
+ case 2: return "Price";
+ case 3: return "Size";
+ case 4: return "Exchange";
+ case 5: return "Special conditions";
+ }
+ return super.getColumnName(column);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/HistoricalTickModel.java b/demo/src/main/java/apidemo/HistoricalTickModel.java
new file mode 100755
index 0000000..ac416fe
--- /dev/null
+++ b/demo/src/main/java/apidemo/HistoricalTickModel.java
@@ -0,0 +1,54 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.HistoricalTick;
+
+class HistoricalTickModel extends AbstractTableModel {
+
+ private List<HistoricalTick> m_rows;
+
+ public HistoricalTickModel(List<HistoricalTick> rows) {
+ m_rows = rows;
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 3;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ HistoricalTick row = m_rows.get(rowIndex);
+
+ switch (columnIndex) {
+ case 0: return row.time();
+ case 1: return row.price();
+ case 2: return row.size();
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Price";
+ case 2: return "Size";
+ }
+
+ return super.getColumnName(column);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/HistoricalTickResultsPanel.java b/demo/src/main/java/apidemo/HistoricalTickResultsPanel.java
new file mode 100755
index 0000000..3258bf7
--- /dev/null
+++ b/demo/src/main/java/apidemo/HistoricalTickResultsPanel.java
@@ -0,0 +1,94 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.HistoricalTick;
+import com.ib.client.HistoricalTickBidAsk;
+import com.ib.client.HistoricalTickLast;
+import com.ib.controller.ApiController.IHistoricalTickHandler;
+
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+class HistoricalTickResultsPanel extends NewTabPanel implements IHistoricalTickHandler {
+
+ HistoricalTickResultsPanel() {
+ m_table = new JTable(m_tickModel);
+ JScrollPane scroll = new JScrollPane(m_table) {
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+
+ d.width = 500;
+
+ return d;
+ }
+ };
+
+ setLayout(new GridLayout());
+ StackPanel hTicksPanel = new StackPanel();
+
+ hTicksPanel.add(new JLabel("Historical ticks:"));
+ hTicksPanel.add(scroll, BorderLayout.WEST);
+ add(hTicksPanel);
+ }
+
+ private final List<HistoricalTick> m_historicalTickRows = new ArrayList<>();
+ private final HistoricalTickModel m_tickModel = new HistoricalTickModel(m_historicalTickRows);
+
+ @Override
+ public void historicalTick(int reqId, List<HistoricalTick> ticks) {
+ for (HistoricalTick tick : ticks) {
+ m_historicalTickRows.add(tick);
+ }
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ private final List<HistoricalTickBidAsk> m_historicalTickBidAsk = new ArrayList<>();
+ private final HistoricalTickBidAskModel m_tickBidAskModel = new HistoricalTickBidAskModel(m_historicalTickBidAsk);
+
+ @Override
+ public void historicalTickBidAsk(int reqId, List<HistoricalTickBidAsk> ticks) {
+ for (HistoricalTickBidAsk tick : ticks) {
+ m_historicalTickBidAsk.add(tick);
+ }
+
+ m_table.setModel(m_tickBidAskModel);
+ m_tickBidAskModel.fireTableDataChanged();
+ }
+
+ private final List<HistoricalTickLast> m_historicalTickLast = new ArrayList<>();
+ private final HistoricalTickLastModel m_tickLastModel = new HistoricalTickLastModel(m_historicalTickLast);
+ private JTable m_table;
+
+ @Override
+ public void historicalTickLast(int reqId, List<HistoricalTickLast> ticks) {
+ for (HistoricalTickLast tick : ticks) {
+ m_historicalTickLast.add(tick);
+ }
+
+ m_table.setModel(m_tickLastModel);
+ m_tickLastModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void activated() { }
+
+ @Override
+ public void closed() { }
+
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/MarginConditionPanel.java b/demo/src/main/java/apidemo/MarginConditionPanel.java
new file mode 100755
index 0000000..8eaf5af
--- /dev/null
+++ b/demo/src/main/java/apidemo/MarginConditionPanel.java
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.MarginCondition;
+
+public class MarginConditionPanel extends OperatorConditionPanel<MarginCondition> {
+
+ MarginConditionPanel(MarginCondition condition) {
+ super(condition);
+
+ m_value.setText(condition().percent());
+
+ add("Operator", m_operator);
+ add("Cushion (%)", m_value);
+ }
+
+ public MarginCondition onOK() {
+ super.onOK();
+ condition().percent(m_value.getInt());
+
+ return condition();
+ }
+}
diff --git a/demo/src/main/java/apidemo/MarketDataPanel.java b/demo/src/main/java/apidemo/MarketDataPanel.java
new file mode 100755
index 0000000..202a51a
--- /dev/null
+++ b/demo/src/main/java/apidemo/MarketDataPanel.java
@@ -0,0 +1,1279 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.*;
+import com.ib.client.Types.BarSize;
+import com.ib.client.Types.DeepSide;
+import com.ib.client.Types.DeepType;
+import com.ib.client.Types.DurationUnit;
+import com.ib.client.Types.TickByTickType;
+import com.ib.client.Types.WhatToShow;
+import com.ib.controller.ApiController.IPnLHandler;
+import com.ib.controller.ApiController.IPnLSingleHandler;
+import com.ib.controller.ApiController.IDeepMktDataHandler;
+import com.ib.controller.ApiController.IHeadTimestampHandler;
+import com.ib.controller.ApiController.IHistogramDataHandler;
+import com.ib.controller.ApiController.IHistoricalDataHandler;
+import com.ib.controller.ApiController.IRealTimeBarHandler;
+import com.ib.controller.ApiController.IScannerHandler;
+import com.ib.controller.ApiController.ISecDefOptParamsReqHandler;
+import com.ib.controller.ApiController.ISmartComponentsHandler;
+import com.ib.controller.ApiController.ISymbolSamplesHandler;
+import com.ib.controller.Bar;
+import com.ib.controller.Formats;
+import com.ib.controller.Instrument;
+import com.ib.controller.ScanCode;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+class MarketDataPanel extends JPanel {
+ private final Contract m_contract = new Contract();
+ private final NewTabbedPanel m_resultsPanel = new NewTabbedPanel();
+ private TopResultsPanel m_topResultPanel;
+ private Set<String> m_bboExchanges = new HashSet<>();
+ private RequestSmartComponentsPanel m_smartComponentsPanel = new RequestSmartComponentsPanel();
+ private SmartComponentsResPanel m_smartComponentsResPanel = null;
+
+ MarketDataPanel() {
+ final NewTabbedPanel requestPanel = new NewTabbedPanel();
+
+ requestPanel.addTab("Top Market Data", new TopRequestPanel(this));
+ requestPanel.addTab("Deep Book", new DeepRequestPanel());
+ requestPanel.addTab("Historical Data", new HistRequestPanel());
+ requestPanel.addTab("Real-time Bars", new RealtimeRequestPanel());
+ requestPanel.addTab("Market Scanner", new ScannerRequestPanel(this));
+ requestPanel.addTab("Security definition optional parameters", new SecDefOptParamsPanel());
+ requestPanel.addTab("Matching Symbols", new RequestMatchingSymbolsPanel());
+ requestPanel.addTab("Market Depth Exchanges", new MktDepthExchangesPanel());
+ requestPanel.addTab("Smart Components", m_smartComponentsPanel);
+ requestPanel.addTab("PnL", new PnLPanel());
+ requestPanel.addTab("Tick-By-Tick", new TickByTickRequestPanel());
+
+ setLayout( new BorderLayout() );
+ add( requestPanel, BorderLayout.NORTH);
+ add( m_resultsPanel);
+ }
+
+ void addBboExch(String exch) {
+ m_bboExchanges.add(exch);
+ m_smartComponentsPanel.updateBboExchSet(m_bboExchanges);
+ }
+
+ private class RequestSmartComponentsPanel extends JPanel {
+ final TCombo<String> m_BBOExchList = new TCombo<>(m_bboExchanges.toArray(new String[0]));
+
+ RequestSmartComponentsPanel() {
+ HtmlButton requestSmartComponentsButton = new HtmlButton( "Request Smart Components") {
+ @Override protected void actionPerformed() {
+ onRequestSmartComponents();
+ }
+ };
+
+ VerticalPanel paramsPanel = new VerticalPanel();
+ paramsPanel.add( "BBO Exchange", m_BBOExchList, Box.createHorizontalStrut(10), requestSmartComponentsButton);
+ setLayout( new BorderLayout() );
+ add( paramsPanel, BorderLayout.NORTH);
+ }
+
+ void updateBboExchSet(Set<String> m_bboExchanges) {
+ m_BBOExchList.removeAllItems();
+
+ for (String item : m_bboExchanges) {
+ m_BBOExchList.addItem(item);
+ }
+ }
+
+ void onRequestSmartComponents() {
+ if (m_smartComponentsResPanel == null) {
+ m_smartComponentsResPanel = new SmartComponentsResPanel();
+
+ m_resultsPanel.addTab( "Smart Components ", m_smartComponentsResPanel, true, true);
+ }
+
+
+ ApiDemo.INSTANCE.controller().reqSmartComponents(m_BBOExchList.getSelectedItem(), m_smartComponentsResPanel);
+ }
+ }
+
+ private class SmartComponentsResPanel extends NewTabPanel implements ISmartComponentsHandler {
+
+ final SmartComponentsModel m_model = new SmartComponentsModel();
+ final List<SmartComponentsRow> m_rows = new ArrayList<>();
+ final JTable m_table = new JTable(m_model);
+
+ SmartComponentsResPanel() {
+ JScrollPane scroll = new JScrollPane( m_table);
+
+ setLayout(new BorderLayout());
+ add(scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() { m_smartComponentsResPanel = null; }
+
+ @Override
+ public void smartComponents(int reqId, Map<Integer, Entry<String, Character>> theMap) {
+ for (Map.Entry<Integer, Entry<String, Character>> entry : theMap.entrySet()) {
+ SmartComponentsRow symbolSamplesRow = new SmartComponentsRow(
+ reqId,
+ entry.getKey(),
+ entry.getValue().getKey(),
+ entry.getValue().getValue()
+ );
+
+ m_rows.add( symbolSamplesRow);
+ }
+
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ revalidate();
+ repaint();
+ });
+ }
+
+ class SmartComponentsModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 4;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Req Id";
+ case 1: return "Bit Number";
+ case 2: return "Exchange";
+ case 3: return "Exchange single-letter abbreviation";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ SmartComponentsRow symbolSamplesRow = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return symbolSamplesRow.m_reqId;
+ case 1: return symbolSamplesRow.m_bitNum;
+ case 2: return symbolSamplesRow.m_exch;
+ case 3: return symbolSamplesRow.m_exchLetter;
+ default: return null;
+ }
+ }
+ }
+
+ private class SmartComponentsRow {
+ int m_reqId;
+ int m_bitNum;
+ String m_exch;
+ char m_exchLetter;
+
+ SmartComponentsRow(int reqId, int bitNum, String exch, char exchLetter) {
+ m_reqId = reqId;
+ m_bitNum = bitNum;
+ m_exch = exch;
+ m_exchLetter = exchLetter;
+ }
+ }
+
+ }
+
+ private class RequestMatchingSymbolsPanel extends JPanel {
+ final UpperField m_pattern = new UpperField();
+
+ RequestMatchingSymbolsPanel() {
+ m_pattern.setText( "IBM");
+ HtmlButton requestMatchingSymbolsButton = new HtmlButton( "Request Matching Symbols") {
+ @Override protected void actionPerformed() {
+ onRequestMatchingSymbols();
+ }
+ };
+
+ VerticalPanel paramsPanel = new VerticalPanel();
+ paramsPanel.add( "Pattern", m_pattern, Box.createHorizontalStrut(10), requestMatchingSymbolsButton);
+ setLayout( new BorderLayout() );
+ add( paramsPanel, BorderLayout.NORTH);
+ }
+
+ void onRequestMatchingSymbols() {
+ SymbolSamplesPanel symbolSamplesPanel = new SymbolSamplesPanel();
+ m_resultsPanel.addTab( "Symbol Samples " + m_pattern.getText(), symbolSamplesPanel, true, true);
+ ApiDemo.INSTANCE.controller().reqMatchingSymbols(m_pattern.getText(), symbolSamplesPanel);
+ }
+ }
+
+ static class SymbolSamplesPanel extends NewTabPanel implements ISymbolSamplesHandler {
+ final SymbolSamplesModel m_model = new SymbolSamplesModel();
+ final List<SymbolSamplesRow> m_rows = new ArrayList<>();
+
+ SymbolSamplesPanel() {
+ JTable table = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( table);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() { /* noop */ }
+
+ @Override
+ public void symbolSamples(ContractDescription[] contractDescriptions) {
+ for (ContractDescription contractDescription : contractDescriptions) {
+ StringBuilder sb = new StringBuilder();
+ for (String string : contractDescription.derivativeSecTypes()) {
+ sb.append(string).append(' ');
+ }
+ final Contract contract = contractDescription.contract();
+ SymbolSamplesRow symbolSamplesRow = new SymbolSamplesRow(
+ contract.conid(),
+ contract.symbol(),
+ contract.secType().getApiString(),
+ contract.primaryExch(),
+ contract.currency(),
+ sb.toString()
+ );
+ m_rows.add(symbolSamplesRow);
+ }
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ revalidate();
+ repaint();
+ });
+ }
+
+ class SymbolSamplesModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 6;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "ConId";
+ case 1: return "Symbol";
+ case 2: return "SecType";
+ case 3: return "PrimaryExch";
+ case 4: return "Currency";
+ case 5: return "Derivative SecTypes";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ SymbolSamplesRow symbolSamplesRow = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return symbolSamplesRow.m_conId;
+ case 1: return symbolSamplesRow.m_symbol;
+ case 2: return symbolSamplesRow.m_secType;
+ case 3: return symbolSamplesRow.m_primaryExch;
+ case 4: return symbolSamplesRow.m_currency;
+ case 5: return symbolSamplesRow.m_derivativeSecTypes;
+ default: return null;
+ }
+ }
+ }
+
+ static class SymbolSamplesRow {
+ int m_conId;
+ String m_symbol;
+ String m_secType;
+ String m_primaryExch;
+ String m_currency;
+ String m_derivativeSecTypes;
+
+ SymbolSamplesRow(int conId, String symbol, String secType, String primaryExch, String currency, String derivativeSecTypes) {
+ update( conId, symbol, secType, primaryExch, currency, derivativeSecTypes);
+ }
+
+ void update( int conId, String symbol, String secType, String primaryExch, String currency, String derivativeSecTypes) {
+ m_conId = conId;
+ m_symbol = symbol;
+ m_secType = secType;
+ m_primaryExch = primaryExch;
+ m_currency = currency;
+ m_derivativeSecTypes = derivativeSecTypes;
+ }
+ }
+ }
+
+ private class TopRequestPanel extends JPanel {
+ final ContractPanel m_contractPanel = new ContractPanel(m_contract);
+ TCombo<String> m_marketDataType = new TCombo<>( MarketDataType.getFields() );
+ JTextField m_genericTicksTextField = new JTextField();
+ MarketDataPanel m_parentPanel;
+
+ TopRequestPanel(MarketDataPanel parentPanel) {
+ m_marketDataType.setSelectedItem( MarketDataType.REALTIME);
+ m_parentPanel = parentPanel;
+
+ HtmlButton reqTop = new HtmlButton( "Request Top Market Data") {
+ @Override protected void actionPerformed() {
+ onTop();
+ }
+ };
+
+ HtmlButton cancelTop = new HtmlButton( "Cancel Top Market Data") {
+ @Override protected void actionPerformed() {
+ onCancelTop();
+ }
+ };
+
+ m_marketDataType.addActionListener(event -> ApiDemo.INSTANCE.controller().reqMktDataType( MarketDataType.getField(m_marketDataType.getSelectedItem())));
+
+ VerticalPanel paramPanel = new VerticalPanel();
+ paramPanel.add( "Market data type", m_marketDataType);
+ paramPanel.add( "Generic ticks", m_genericTicksTextField);
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add( Box.createVerticalStrut( 40));
+ butPanel.add( reqTop);
+ butPanel.add( cancelTop);
+
+ JPanel rightPanel = new StackPanel();
+ rightPanel.add( paramPanel);
+ rightPanel.add( Box.createVerticalStrut( 20));
+ rightPanel.add( butPanel);
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( m_contractPanel);
+ add( Box.createHorizontalStrut(20));
+ add( rightPanel);
+ }
+
+ void onTop() {
+ m_contractPanel.onOK();
+ if (m_topResultPanel == null) {
+ m_topResultPanel = new TopResultsPanel(m_parentPanel);
+ m_resultsPanel.addTab( "Top Data", m_topResultPanel, true, true);
+ }
+ m_topResultPanel.m_model.setGenericTicks(m_genericTicksTextField.getText());
+
+ m_topResultPanel.m_model.addRow( m_contract);
+ }
+
+ void onCancelTop() {
+ m_topResultPanel.m_model.removeSelectedRows();
+ }
+ }
+
+ private class TopResultsPanel extends NewTabPanel {
+ final TopModel m_model;
+ final JTable m_tab;
+
+ TopResultsPanel(MarketDataPanel parentPanel) {
+ m_model = new TopModel(parentPanel);
+ m_tab = new TopTable( m_model);
+
+ JScrollPane scroll = new JScrollPane( m_tab);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ m_model.desubscribe();
+ m_topResultPanel = null;
+ }
+
+ class TopTable extends JTable {
+ TopTable(TopModel model) { super( model); }
+ }
+ }
+
+ private class DeepRequestPanel extends JPanel {
+ final ContractPanel m_contractPanel = new ContractPanel(m_contract);
+
+ DeepRequestPanel() {
+ HtmlButton reqDeep = new HtmlButton( "Request Deep Market Data") {
+ @Override protected void actionPerformed() {
+ onDeep();
+ }
+ };
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add( reqDeep);
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( m_contractPanel);
+ add( Box.createHorizontalStrut(20));
+ add( butPanel);
+ }
+
+ void onDeep() {
+ m_contractPanel.onOK();
+ DeepResultsPanel resultPanel = new DeepResultsPanel();
+ m_resultsPanel.addTab( "Deep " + m_contract.symbol(), resultPanel, true, true);
+ ApiDemo.INSTANCE.controller().reqDeepMktData(m_contract, 6, resultPanel);
+ }
+ }
+
+ private static class DeepResultsPanel extends NewTabPanel implements IDeepMktDataHandler {
+ final DeepModel m_buy = new DeepModel();
+ final DeepModel m_sell = new DeepModel();
+
+ DeepResultsPanel() {
+ HtmlButton desub = new HtmlButton( "Desubscribe") {
+ public void actionPerformed() {
+ onDesub();
+ }
+ };
+
+ JTable buyTab = new JTable( m_buy);
+ JTable sellTab = new JTable( m_sell);
+
+ JScrollPane buyScroll = new JScrollPane( buyTab);
+ JScrollPane sellScroll = new JScrollPane( sellTab);
+
+ JPanel mid = new JPanel( new GridLayout( 1, 2) );
+ mid.add( buyScroll);
+ mid.add( sellScroll);
+
+ setLayout( new BorderLayout() );
+ add( mid);
+ add( desub, BorderLayout.SOUTH);
+ }
+
+ void onDesub() {
+ ApiDemo.INSTANCE.controller().cancelDeepMktData( this);
+ }
+
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ ApiDemo.INSTANCE.controller().cancelDeepMktData( this);
+ }
+
+ @Override public void updateMktDepth(int pos, String mm, DeepType operation, DeepSide side, double price, int size) {
+ if (side == DeepSide.BUY) {
+ m_buy.updateMktDepth(pos, mm, operation, price, size);
+ }
+ else {
+ m_sell.updateMktDepth(pos, mm, operation, price, size);
+ }
+ }
+
+ static class DeepModel extends AbstractTableModel {
+ final List<DeepRow> m_rows = new ArrayList<>();
+
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ public void updateMktDepth(int pos, String mm, DeepType operation, double price, int size) {
+ switch( operation) {
+ case INSERT:
+ m_rows.add( pos, new DeepRow( mm, price, size) );
+ fireTableRowsInserted(pos, pos);
+ break;
+ case UPDATE:
+ m_rows.get( pos).update( mm, price, size);
+ fireTableRowsUpdated(pos, pos);
+ break;
+ case DELETE:
+ if (pos < m_rows.size() ) {
+ m_rows.remove( pos);
+ }
+ else {
+ // this happens but seems to be harmless
+ // System.out.println( "can't remove " + pos);
+ }
+ fireTableRowsDeleted(pos, pos);
+ break;
+ }
+ }
+
+ @Override public int getColumnCount() {
+ return 3;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Mkt Maker";
+ case 1: return "Price";
+ case 2: return "Size";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ DeepRow row = m_rows.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_mm;
+ case 1: return row.m_price;
+ case 2: return row.m_size;
+ default: return null;
+ }
+ }
+ }
+
+ static class DeepRow {
+ String m_mm;
+ double m_price;
+ int m_size;
+
+ DeepRow(String mm, double price, int size) {
+ update( mm, price, size);
+ }
+
+ void update( String mm, double price, int size) {
+ m_mm = mm;
+ m_price = price;
+ m_size = size;
+ }
+ }
+ }
+
+ private class HistRequestPanel extends JPanel {
+ final ContractPanel m_contractPanel = new ContractPanel(m_contract);
+ final UpperField m_begin = new UpperField();
+ final UpperField m_end = new UpperField();
+ final UpperField m_nTicks = new UpperField();
+ final UpperField m_duration = new UpperField();
+ final TCombo<DurationUnit> m_durationUnit = new TCombo<>( DurationUnit.values() );
+ final TCombo<BarSize> m_barSize = new TCombo<>( BarSize.values() );
+ final TCombo<WhatToShow> m_whatToShow = new TCombo<>( WhatToShow.values() );
+ final JCheckBox m_rthOnly = new JCheckBox();
+ final JCheckBox m_keepUpToDate = new JCheckBox();
+ final JCheckBox m_ignoreSize = new JCheckBox();
+
+ HistRequestPanel() {
+ m_end.setText("20120101 12:00:00");
+ m_duration.setText("1");
+ m_durationUnit.setSelectedItem(DurationUnit.WEEK);
+ m_barSize.setSelectedItem(BarSize._1_hour);
+
+ HtmlButton bReqHistoricalData = new HtmlButton("Request historical data") {
+ @Override protected void actionPerformed() {
+ onHistorical();
+ }
+ };
+
+ HtmlButton bReqHistogramData = new HtmlButton("Request histogram data") {
+ @Override protected void actionPerformed() {
+ onHistogram();
+ }
+ };
+
+ HtmlButton bReqHistoricalTick = new HtmlButton("Request historical tick") {
+ @Override protected void actionPerformed() {
+ onHistoricalTick();
+ }
+ };
+
+ VerticalPanel paramPanel = new VerticalPanel();
+ paramPanel.add("Begin", m_begin);
+ paramPanel.add("End", m_end);
+ paramPanel.add("Number of ticks", m_nTicks);
+ paramPanel.add("Duration", m_duration);
+ paramPanel.add("Duration unit", m_durationUnit);
+ paramPanel.add("Bar size", m_barSize);
+ paramPanel.add("What to show", m_whatToShow);
+ paramPanel.add("RTH only", m_rthOnly);
+ paramPanel.add("Keep up to date", m_keepUpToDate);
+ paramPanel.add("Ignore size", m_ignoreSize);
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add(bReqHistoricalData);
+ butPanel.add(bReqHistogramData);
+ butPanel.add(bReqHistoricalTick);
+
+ JPanel rightPanel = new StackPanel();
+ rightPanel.add(paramPanel);
+ rightPanel.add(Box.createVerticalStrut(20));
+ rightPanel.add(butPanel);
+
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS) );
+ add(m_contractPanel);
+ add(Box.createHorizontalStrut(20) );
+ add(rightPanel);
+ }
+
+ protected void onHistoricalTick() {
+ m_contractPanel.onOK();
+
+ HistoricalTickResultsPanel panel = new HistoricalTickResultsPanel();
+
+ ApiDemo.INSTANCE.controller().reqHistoricalTicks(m_contract, m_begin.getText(), m_end.getText(),
+ m_nTicks.getInt(), m_whatToShow.getSelectedItem().name(), m_rthOnly.isSelected() ? 1 : 0,
+ m_ignoreSize.isSelected(), panel);
+ m_resultsPanel.addTab("Historical tick " + m_contract.symbol(), panel, true, true);
+ }
+
+ void onHistogram() {
+ m_contractPanel.onOK();
+
+ HistogramResultsPanel panel = new HistogramResultsPanel();
+
+ ApiDemo.INSTANCE.controller().reqHistogramData(m_contract, m_duration.getInt(),
+ m_durationUnit.getSelectedItem(), m_rthOnly.isSelected(), panel);
+ m_resultsPanel.addTab("Histogram " + m_contract.symbol(), panel, true, true);
+ }
+
+ void onHistorical() {
+ m_contractPanel.onOK();
+
+ BarResultsPanel panel = new BarResultsPanel( true);
+
+ ApiDemo.INSTANCE.controller().reqHistoricalData(m_contract, m_end.getText(), m_duration.getInt(),
+ m_durationUnit.getSelectedItem(), m_barSize.getSelectedItem(), m_whatToShow.getSelectedItem(),
+ m_rthOnly.isSelected(), m_keepUpToDate.isSelected(), panel);
+ m_resultsPanel.addTab( "Historical " + m_contract.symbol(), panel, true, true);
+ }
+ }
+
+ private class RealtimeRequestPanel extends JPanel {
+ final ContractPanel m_contractPanel = new ContractPanel(m_contract);
+ final TCombo<WhatToShow> m_whatToShow = new TCombo<>( WhatToShow.values() );
+ final JCheckBox m_rthOnly = new JCheckBox();
+
+ RealtimeRequestPanel() {
+ HtmlButton button = new HtmlButton( "Request real-time bars") {
+ @Override protected void actionPerformed() {
+ onRealTime();
+ }
+ };
+
+ HtmlButton htsButton = new HtmlButton("Request head timestamp") {
+ @Override protected void actionPerformed() {
+ onHts();
+ }
+ };
+
+ VerticalPanel paramPanel = new VerticalPanel();
+ paramPanel.add( "What to show", m_whatToShow);
+ paramPanel.add( "RTH only", m_rthOnly);
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add( button);
+ butPanel.add(htsButton);
+
+ JPanel rightPanel = new StackPanel();
+ rightPanel.add( paramPanel);
+ rightPanel.add( Box.createVerticalStrut( 20));
+ rightPanel.add( butPanel);
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( m_contractPanel);
+ add( Box.createHorizontalStrut(20) );
+ add( rightPanel);
+ }
+
+ void onHts() {
+ m_contractPanel.onOK();
+
+ HtsResultsPanel panel = new HtsResultsPanel();
+
+ ApiDemo.INSTANCE.controller().reqHeadTimestamp(m_contract, m_whatToShow.getSelectedItem(), m_rthOnly.isSelected(), panel);
+ m_resultsPanel.addTab( "Head time stamp " + m_contract.symbol(), panel, true, true);
+ }
+
+ void onRealTime() {
+ m_contractPanel.onOK();
+ BarResultsPanel panel = new BarResultsPanel( false);
+ ApiDemo.INSTANCE.controller().reqRealTimeBars(m_contract, m_whatToShow.getSelectedItem(), m_rthOnly.isSelected(), panel);
+ m_resultsPanel.addTab( "Real-time " + m_contract.symbol(), panel, true, true);
+ }
+ }
+
+ static class HtsResultsPanel extends NewTabPanel implements IHeadTimestampHandler {
+ final BarModel m_model = new BarModel();
+ final List<Long> m_rows = new ArrayList<>();
+ //final Chart m_chart = new Chart( m_rows);
+
+ HtsResultsPanel() {
+
+ JTable tab = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( tab) {
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+ d.width = 500;
+ return d;
+ }
+ };
+
+ //JScrollPane chartScroll = new JScrollPane( m_chart);
+
+ setLayout( new BorderLayout() );
+ add( scroll, BorderLayout.WEST);
+ //add( chartScroll, BorderLayout.CENTER);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+
+ }
+
+ @Override public void headTimestamp(int reqId, long headTimestamp) {
+ m_rows.add(headTimestamp);
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ //m_chart.repaint();
+ });
+ }
+
+ class BarModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 7;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Date/time";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ long row = m_rows.get( rowIn);
+
+ switch( col) {
+ case 0: return Formats.fmtDateGmt(row * 1000);
+ default: return null;
+ }
+ }
+ }
+
+ }
+
+ static class HistogramResultsPanel extends NewTabPanel implements IHistogramDataHandler {
+ final HistogramModel m_model = new HistogramModel();
+ final List<HistogramEntry> m_rows = new ArrayList<>();
+ final Histogram m_hist = new Histogram(m_rows);
+
+ HistogramResultsPanel() {
+ JTable tab = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( tab) {
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+ d.width = 500;
+ return d;
+ }
+ };
+
+ JScrollPane chartScroll = new JScrollPane(m_hist);
+
+ setLayout(new BorderLayout());
+ add(scroll, BorderLayout.WEST);
+ add(chartScroll, BorderLayout.CENTER);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ ApiDemo.INSTANCE.controller().cancelHistogramData(this);
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ m_hist.repaint();
+ });
+ }
+
+ class HistogramModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 2;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch(col) {
+ case 0: return "Price";
+ case 1: return "Size";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ HistogramEntry row = m_rows.get(rowIn);
+
+ switch(col) {
+ case 0: return row.price;
+ case 1: return row.size;
+ default: return null;
+ }
+ }
+ }
+
+ @Override
+ public void histogramData(int reqId, List<HistogramEntry> items) {
+ m_rows.addAll(items);
+ fire();
+ }
+ }
+
+ static class BarResultsPanel extends NewTabPanel implements IHistoricalDataHandler, IRealTimeBarHandler {
+ final BarModel m_model = new BarModel();
+ final List<Bar> m_rows = new ArrayList<>();
+ final boolean m_historical;
+ final Chart m_chart = new Chart( m_rows);
+
+ BarResultsPanel( boolean historical) {
+ m_historical = historical;
+
+ JTable tab = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( tab) {
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+ d.width = 500;
+ return d;
+ }
+ };
+
+ JScrollPane chartScroll = new JScrollPane( m_chart);
+
+ setLayout( new BorderLayout() );
+ add( scroll, BorderLayout.WEST);
+ add( chartScroll, BorderLayout.CENTER);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ if (m_historical) {
+ ApiDemo.INSTANCE.controller().cancelHistoricalData( this);
+ }
+ else {
+ ApiDemo.INSTANCE.controller().cancelRealtimeBars( this);
+ }
+ }
+
+ @Override public void historicalData(Bar bar) {
+ m_rows.add( bar);
+ }
+
+ @Override public void historicalDataEnd() {
+ fire();
+ }
+
+ @Override public void realtimeBar(Bar bar) {
+ m_rows.add( bar);
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ m_chart.repaint();
+ });
+ }
+
+ class BarModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 7;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Date/time";
+ case 1: return "Open";
+ case 2: return "High";
+ case 3: return "Low";
+ case 4: return "Close";
+ case 5: return "Volume";
+ case 6: return "WAP";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ Bar row = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return row.formattedTime();
+ case 1: return row.open();
+ case 2: return row.high();
+ case 3: return row.low();
+ case 4: return row.close();
+ case 5: return row.volume();
+ case 6: return row.wap();
+ default: return null;
+ }
+ }
+ }
+ }
+
+ private class ScannerRequestPanel extends JPanel {
+ final UpperField m_numRows = new UpperField( "15");
+ final TCombo<ScanCode> m_scanCode = new TCombo<>( ScanCode.values() );
+ final TCombo<Instrument> m_instrument = new TCombo<>( Instrument.values() );
+ final UpperField m_location = new UpperField( "STK.US.MAJOR", 9);
+ final TCombo<String> m_stockType = new TCombo<>( "ALL", "STOCK", "ETF");
+ private MarketDataPanel m_parentPanel;
+
+ ScannerRequestPanel(MarketDataPanel parentPanel) {
+ m_parentPanel = parentPanel;
+
+ HtmlButton go = new HtmlButton( "Go") {
+ @Override protected void actionPerformed() {
+ onGo();
+ }
+ };
+
+ VerticalPanel paramsPanel = new VerticalPanel();
+ paramsPanel.add( "Scan code", m_scanCode);
+ paramsPanel.add( "Instrument", m_instrument);
+ paramsPanel.add( "Location", m_location, Box.createHorizontalStrut(10), go);
+ paramsPanel.add( "Stock type", m_stockType);
+ paramsPanel.add( "Num rows", m_numRows);
+
+ setLayout( new BorderLayout() );
+ add( paramsPanel, BorderLayout.NORTH);
+ }
+
+ void onGo() {
+ ScannerSubscription sub = new ScannerSubscription();
+ sub.numberOfRows( m_numRows.getInt() );
+ sub.scanCode( m_scanCode.getSelectedItem().toString() );
+ sub.instrument( m_instrument.getSelectedItem().toString() );
+ sub.locationCode( m_location.getText() );
+ sub.stockTypeFilter( m_stockType.getSelectedItem() );
+
+ ScannerResultsPanel resultsPanel = new ScannerResultsPanel(m_parentPanel);
+ m_resultsPanel.addTab( sub.scanCode(), resultsPanel, true, true);
+
+ ApiDemo.INSTANCE.controller().reqScannerSubscription( sub, resultsPanel);
+ }
+ }
+
+ static class ScannerResultsPanel extends NewTabPanel implements IScannerHandler {
+ final HashSet<Integer> m_conids = new HashSet<>();
+ final TopModel m_model;
+
+ ScannerResultsPanel(MarketDataPanel parentPanel) {
+ m_model = new TopModel(parentPanel);
+ JTable table = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( table);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ ApiDemo.INSTANCE.controller().cancelScannerSubscription( this);
+ m_model.desubscribe();
+ }
+
+ @Override public void scannerParameters(String xml) {
+ try {
+ File file = File.createTempFile( "pre", ".xml");
+ try (PrintStream ps = new PrintStream( file, "UTF-8")) {
+ ps.println(xml);
+ }
+ Desktop.getDesktop().open( file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override public void scannerData(int rank, final ContractDetails contractDetails, String legsStr) {
+ if (!m_conids.contains( contractDetails.conid() ) ) {
+ m_conids.add( contractDetails.conid() );
+ SwingUtilities.invokeLater(() -> m_model.addRow( contractDetails.contract() ));
+ }
+ }
+
+ @Override public void scannerDataEnd() {
+ // we could sort here
+ }
+ }
+
+ class PnLPanel extends JPanel {
+
+ final UpperField m_account = new UpperField();
+ final UpperField m_modelCode = new UpperField();
+ final UpperField m_conId = new UpperField();
+
+ PnLPanel() {
+ VerticalPanel paramsPanel = new VerticalPanel();
+ HtmlButton reqPnL =
+ new HtmlButton("Request PnL") { @Override protected void actionPerformed() { onReqPnL(); } };
+ HtmlButton reqPnLSingle =
+ new HtmlButton("Request PnL Single") { @Override protected void actionPerformed() { onReqPnLSingle(); } };
+
+ paramsPanel.add("Account", m_account);
+ paramsPanel.add("Model Code", m_modelCode);
+ paramsPanel.add("Con Id", m_conId);
+ paramsPanel.add(reqPnL);
+ paramsPanel.add(reqPnLSingle);
+ setLayout(new BorderLayout());
+ add(paramsPanel, BorderLayout.NORTH);
+ }
+
+ protected void onReqPnLSingle() {
+ final PnLSingleModel pnlSingleModel = new PnLSingleModel();
+ PnLResultsPanel resultsPanel = new PnLResultsPanel(pnlSingleModel);
+ String account = m_account.getText();
+ String modelCode = m_modelCode.getText();
+ int conId = m_conId.getInt();
+
+ m_resultsPanel.addTab(account + " " + modelCode + " " + conId, resultsPanel, true, true);
+
+ IPnLSingleHandler handler = (reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value) ->
+ SwingUtilities.invokeLater(() -> pnlSingleModel.addRow(pos, dailyPnL, unrealizedPnL, realizedPnL, value));
+
+ resultsPanel.handler(handler);
+ ApiDemo.INSTANCE.controller().reqPnLSingle(account, modelCode, conId, handler);
+
+ }
+
+ void onReqPnL() {
+ final PnLModel pnlModel = new PnLModel();
+ PnLResultsPanel resultsPanel = new PnLResultsPanel(pnlModel);
+ String account = m_account.getText();
+ String modelCode = m_modelCode.getText();
+
+ m_resultsPanel.addTab(account + " " + modelCode, resultsPanel, true, true);
+
+ IPnLHandler handler = (reqId, dailyPnL, unrealizedPnL, realizedPnL) ->
+ SwingUtilities.invokeLater(() -> pnlModel.addRow(dailyPnL, unrealizedPnL, realizedPnL));
+
+ resultsPanel.handler(handler);
+ ApiDemo.INSTANCE.controller().reqPnL(account, modelCode, handler);
+ }
+
+ }
+
+
+ static class PnLResultsPanel extends NewTabPanel {
+
+ public PnLResultsPanel(AbstractTableModel pnlModel) {
+ JTable table = new JTable(pnlModel);
+ JScrollPane scroll = new JScrollPane(table);
+
+ setLayout(new BorderLayout());
+ add(scroll);
+ }
+
+ private IPnLHandler m_handler;
+ private IPnLSingleHandler m_singleHandler;
+
+ public void handler(IPnLHandler v) {
+ m_handler = v;
+ }
+
+ public void handler(IPnLSingleHandler v) {
+ m_singleHandler = v;
+ }
+
+ @Override
+ public void activated() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void closed() {
+ if (m_handler != null) {
+ ApiDemo.INSTANCE.controller().cancelPnL(m_handler);
+ } else if (m_singleHandler != null) {
+ ApiDemo.INSTANCE.controller().cancelPnLSingle(m_singleHandler);
+ }
+ }
+
+ }
+
+ class SecDefOptParamsPanel extends JPanel {
+
+ final UpperField m_underlyingSymbol = new UpperField();
+ final UpperField m_futFopExchange = new UpperField();
+ final UpperField m_underlyingSecType = new UpperField();
+ final UpperField m_underlyingConId = new UpperField();
+
+ SecDefOptParamsPanel() {
+ VerticalPanel paramsPanel = new VerticalPanel();
+ HtmlButton go = new HtmlButton("Go") { @Override protected void actionPerformed() { onGo(); } };
+
+ m_underlyingConId.setText(Integer.MAX_VALUE);
+ paramsPanel.add("Underlying symbol", m_underlyingSymbol);
+ paramsPanel.add("FUT-FOP exchange", m_futFopExchange);
+ paramsPanel.add("Underlying security type", m_underlyingSecType);
+ paramsPanel.add("Underlying contract id", m_underlyingConId);
+ paramsPanel.add(go);
+ setLayout(new BorderLayout());
+ add(paramsPanel, BorderLayout.NORTH);
+ }
+
+ void onGo() {
+ String underlyingSymbol = m_underlyingSymbol.getText();
+ String futFopExchange = m_futFopExchange.getText();
+ String underlyingSecType = m_underlyingSecType.getText();
+ int underlyingConId = m_underlyingConId.getInt();
+
+ ApiDemo.INSTANCE.controller().reqSecDefOptParams(
+ underlyingSymbol,
+ futFopExchange,
+ underlyingSecType,
+ underlyingConId,
+ new ISecDefOptParamsReqHandler() {
+
+ @Override
+ public void securityDefinitionOptionalParameterEnd(int reqId) { }
+
+ @Override
+ public void securityDefinitionOptionalParameter(final String exchange, final int underlyingConId, final String tradingClass, final String multiplier,
+ final Set<String> expirations, final Set<Double> strikes) {
+ SwingUtilities.invokeLater(() -> {
+
+ SecDefOptParamsReqResultsPanel resultsPanel = new SecDefOptParamsReqResultsPanel(expirations, strikes);
+
+ m_resultsPanel.addTab(exchange + " " +
+ underlyingConId + " " +
+ tradingClass + " " +
+ multiplier,
+ resultsPanel, true, true);
+ });
+ }
+ });
+ }
+
+ }
+
+ static class SecDefOptParamsReqResultsPanel extends NewTabPanel {
+
+ final OptParamsModel m_model;
+
+ SecDefOptParamsReqResultsPanel(Set<String> expirations, Set<Double> strikes) {
+ JTable table = new JTable(m_model = new OptParamsModel(expirations, strikes));
+ JScrollPane scroll = new JScrollPane(table);
+
+ setLayout(new BorderLayout());
+ add(scroll);
+ }
+
+ @Override
+ public void activated() {
+ }
+
+ @Override
+ public void closed() {
+ }
+
+ }
+
+ private class TickByTickRequestPanel extends JPanel {
+ final ContractPanel m_contractPanel = new ContractPanel(m_contract);
+ final TCombo<TickByTickType> m_tickType = new TCombo<>( TickByTickType.values() );
+ final UpperField m_numberOfTicks = new UpperField();
+ final JCheckBox m_ignoreSize = new JCheckBox();
+
+ TickByTickRequestPanel() {
+ m_tickType.setSelectedItem(TickByTickType.Last);
+
+ HtmlButton bReqTickByTickData = new HtmlButton("Request Tick-By-Tick Data") {
+ @Override protected void actionPerformed() {
+ onReqTickByTickData();
+ }
+ };
+
+ VerticalPanel paramPanel = new VerticalPanel();
+ paramPanel.add("Tick-By-Tick Type", m_tickType);
+ paramPanel.add("Number Of Ticks", m_numberOfTicks);
+ paramPanel.add("Ignore Size", m_ignoreSize);
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add(bReqTickByTickData);
+
+ JPanel rightPanel = new StackPanel();
+ rightPanel.add(paramPanel);
+ rightPanel.add(Box.createVerticalStrut(20));
+ rightPanel.add(butPanel);
+
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS) );
+ add(m_contractPanel);
+ add(Box.createHorizontalStrut(20) );
+ add(rightPanel);
+ }
+
+ protected void onReqTickByTickData() {
+ m_contractPanel.onOK();
+
+ TickByTickResultsPanel panel = new TickByTickResultsPanel(TickByTickType.valueOf(m_tickType.getSelectedItem().name()));
+
+ ApiDemo.INSTANCE.controller().reqTickByTickData(m_contract, m_tickType.getSelectedItem().name(),
+ m_numberOfTicks.getInt(), m_ignoreSize.isSelected(), panel);
+ m_resultsPanel.addTab("Tick-By-Tick " + (m_numberOfTicks.getInt() > 0 ? "Hist + " : "") + m_tickType.getSelectedItem().name() + " " + m_contract.symbol(), panel, true, true);
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/MarketValueSummaryPanel.java b/demo/src/main/java/apidemo/MarketValueSummaryPanel.java
new file mode 100755
index 0000000..cf68b68
--- /dev/null
+++ b/demo/src/main/java/apidemo/MarketValueSummaryPanel.java
@@ -0,0 +1,74 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.ib.controller.ApiController.IMarketValueSummaryHandler;
+import com.ib.controller.MarketValueTag;
+
+import apidemo.AccountInfoPanel.MktValModel;
+import apidemo.AccountInfoPanel.Table;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+public class MarketValueSummaryPanel extends NewTabPanel implements IMarketValueSummaryHandler {
+ private MktValModel m_model = new MktValModel();
+
+ MarketValueSummaryPanel() {
+ HtmlButton sub = new HtmlButton( "Subscribe") {
+ protected void actionPerformed() {
+ subscribe();
+ }
+ };
+
+ HtmlButton desub = new HtmlButton( "Desubscribe") {
+ protected void actionPerformed() {
+ desubscribe();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( sub);
+ buts.add( desub);
+
+ JTable table = new Table( m_model, 2);
+ JScrollPane scroll = new JScrollPane( table);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ subscribe();
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ desubscribe();
+ }
+
+ private void subscribe() {
+ ApiDemo.INSTANCE.controller().reqMarketValueSummary( "All", this);
+ }
+
+ private void desubscribe() {
+ ApiDemo.INSTANCE.controller().cancelMarketValueSummary( this);
+ m_model.clear();
+ }
+
+ @Override public void marketValueSummary(String account, MarketValueTag tag, String value, String currency) {
+ m_model.handle( account, currency, tag, value);
+ }
+
+ @Override public void marketValueSummaryEnd() {
+ }
+}
diff --git a/demo/src/main/java/apidemo/MktDepthExchangesPanel.java b/demo/src/main/java/apidemo/MktDepthExchangesPanel.java
new file mode 100755
index 0000000..6a7284b
--- /dev/null
+++ b/demo/src/main/java/apidemo/MktDepthExchangesPanel.java
@@ -0,0 +1,136 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.DepthMktDataDescription;
+import com.ib.controller.ApiController.IMktDepthExchangesHandler;
+
+import apidemo.AccountInfoPanel.Table;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+
+public class MktDepthExchangesPanel extends NewTabPanel {
+ private MktDepthExchangesModel m_model = new MktDepthExchangesModel();
+
+ MktDepthExchangesPanel() {
+ HtmlButton reqMktDepthExchangesButton = new HtmlButton( "Request Market Depth Exchanges") {
+ protected void actionPerformed() {
+ reqMktDepthExchanges();
+ }
+ };
+
+ HtmlButton clearMktDepthExchangesButton = new HtmlButton( "Clear MarketDepth Exchanges") {
+ protected void actionPerformed() {
+ clearMktDepthExchanges();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( reqMktDepthExchangesButton);
+ buts.add( clearMktDepthExchangesButton);
+
+ JTable table = new Table( m_model, 2);
+ JScrollPane scroll = new JScrollPane( table);
+ scroll.setPreferredSize(new Dimension(100, 100));
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ clearMktDepthExchanges();
+ }
+
+ private void reqMktDepthExchanges() {
+ ApiDemo.INSTANCE.controller().reqMktDepthExchanges( m_model);
+ }
+
+ private void clearMktDepthExchanges() {
+ m_model.clear();
+ }
+
+ private class MktDepthExchangesModel extends AbstractTableModel implements IMktDepthExchangesHandler {
+ List<DepthMktDataDescriptionRow> m_list = new ArrayList<>();
+
+ @Override public void mktDepthExchanges(DepthMktDataDescription[] depthMktDataDescriptions) {
+ for (DepthMktDataDescription depthMktDataDescription : depthMktDataDescriptions){
+ DepthMktDataDescriptionRow row = new DepthMktDataDescriptionRow();
+ m_list.add( row);
+ row.update(depthMktDataDescription.exchange(), depthMktDataDescription.secType(),
+ depthMktDataDescription.listingExch(), depthMktDataDescription.serviceDataType(),
+ depthMktDataDescription.aggGroup());
+ }
+ m_model.fireTableDataChanged();
+ }
+
+ public void clear() {
+ m_list.clear();
+ fireTableDataChanged();
+ }
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 5;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Exchange";
+ case 1: return "Security Type";
+ case 2: return "Listing Exch";
+ case 3: return "Service Data Type";
+ case 4: return "Agg Group";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ DepthMktDataDescriptionRow row = m_list.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_exchange;
+ case 1: return row.m_secType;
+ case 2: return row.m_listingExch;
+ case 3: return row.m_serviceDataType;
+ case 4: return row.m_aggGroup;
+ default: return null;
+ }
+ }
+ }
+
+ private static class DepthMktDataDescriptionRow {
+ String m_exchange;
+ String m_secType;
+ String m_listingExch;
+ String m_serviceDataType;
+ String m_aggGroup;
+
+ void update(String exchange, String secType, String listingExch, String serviceDataType, int aggGroup) {
+ m_exchange = exchange;
+ m_secType = secType;
+ m_listingExch = listingExch;
+ m_serviceDataType = serviceDataType;
+ m_aggGroup = (aggGroup != Integer.MAX_VALUE ? String.valueOf(aggGroup) : "");
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/NewsPanel.java b/demo/src/main/java/apidemo/NewsPanel.java
new file mode 100755
index 0000000..9533055
--- /dev/null
+++ b/demo/src/main/java/apidemo/NewsPanel.java
@@ -0,0 +1,557 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.client.NewsProvider;
+import com.ib.client.Util;
+import com.ib.client.Types.SecType;
+import com.ib.controller.ApiController.INewsArticleHandler;
+import com.ib.controller.ApiController.INewsProvidersHandler;
+import com.ib.controller.ApiController.ITickNewsHandler;
+import com.ib.controller.ApiController.IHistoricalNewsHandler;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel;
+import apidemo.util.VerticalPanel.HorzPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+class NewsPanel extends JPanel {
+ private final NewTabbedPanel m_requestPanels = new NewTabbedPanel();
+ private final NewTabbedPanel m_resultsPanels = new NewTabbedPanel();
+
+ private NewsArticleRequestPanel m_newsArticleRequestPanel = new NewsArticleRequestPanel();
+
+ NewsPanel() {
+ m_requestPanels.addTab( "News Ticks", new NewsTicksRequestPanel() );
+ m_requestPanels.addTab( "News Providers", new RequestNewsProvidersPanel() );
+ m_requestPanels.addTab( "News Article", m_newsArticleRequestPanel );
+ m_requestPanels.addTab( "Historical News", new HistoricalNewsRequestPanel() );
+
+ setLayout( new BorderLayout() );
+ add( m_requestPanels, BorderLayout.NORTH);
+ add( m_resultsPanels);
+ }
+
+ private static class RequestPanel extends JPanel {
+ JTextField m_providerCode = new JTextField();
+ JTextField m_articleId = new JTextField();
+ JTextField m_path = new JTextField(System.getProperty("user.dir"));
+
+ RequestPanel() {
+ VerticalPanel p = new VerticalPanel();
+ p.add( "Provider Code", m_providerCode);
+ m_providerCode.setColumns(20);
+ p.add( "Article Id", m_articleId);
+ m_articleId.setColumns(20);
+
+ JButton choosePathDialogButton = new JButton("...");
+ JFileChooser chooser = new JFileChooser(m_path.getText());
+
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+
+ choosePathDialogButton.addActionListener(e -> m_path.setText(chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile().getPath() : m_path.getText()));
+
+ HorzPanel pathPanel = new HorzPanel();
+
+ pathPanel.add(m_path);
+ pathPanel.add(choosePathDialogButton);
+
+ p.add( "Path to save binary/pdf", pathPanel);
+ m_path.setColumns(20);
+
+ setLayout( new BorderLayout() );
+ add( p);
+ }
+
+ public void setProviderCode(String v){
+ m_providerCode.setText(v);
+ }
+
+ public void setArticleId(String v){
+ m_articleId.setText(v);
+ }
+
+ @Override public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+ }
+
+ class NewsArticleRequestPanel extends JPanel {
+ final RequestPanel m_requestPanel = new RequestPanel();
+
+ NewsArticleRequestPanel() {
+ HtmlButton butReqNewsArticle = new HtmlButton( "Request News Article") {
+ @Override protected void actionPerformed() {
+ onReqNewsArticle();
+ }
+ };
+
+ VerticalPanel butPanel = new VerticalPanel();
+ butPanel.add( butReqNewsArticle);
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( m_requestPanel);
+ add( Box.createHorizontalStrut(20));
+ add( butPanel);
+ }
+
+ void onReqNewsArticle() {
+ NewsArticleResultsPanel panel = new NewsArticleResultsPanel();
+ String providerCode = m_requestPanel.m_providerCode.getText().trim();
+ String articleId = m_requestPanel.m_articleId.getText().trim();
+ String path = m_requestPanel.m_path.getText().trim() + "\\" + articleId + ".pdf";
+ panel.setPath(path);
+ ApiDemo.INSTANCE.controller().reqNewsArticle(providerCode, articleId, panel);
+ m_resultsPanels.addTab( "News Article: " + providerCode + " " + articleId, panel, true, true);
+ }
+ }
+
+ static class NewsArticleResultsPanel extends JPanel implements INewsArticleHandler {
+ JLabel m_label = new JLabel();
+ JTextArea m_text = new JTextArea();
+ String m_path;
+
+ NewsArticleResultsPanel() {
+ JScrollPane scroll = new JScrollPane( m_text);
+
+ setLayout( new BorderLayout() );
+ add( m_label, BorderLayout.NORTH);
+ add( scroll);
+ }
+
+ void setPath(String path) {
+ m_path = path;
+ }
+
+ @Override
+ public void newsArticle(int articleType, String articleText) {
+ if (articleType == 0) {
+ m_label.setText( "Article type is text or html");
+ m_text.setText( articleText);
+ } else if (articleType == 1){
+ m_label.setText( "Article type is binary/pdf");
+ if (articleType == 1) {
+ try {
+ byte[] bytes = Base64.getDecoder().decode(articleText);
+ FileOutputStream fos = new FileOutputStream(m_path);
+ fos.write(bytes);
+ fos.close();
+ m_text.setText( "Binary/pdf article was saved to " + m_path);
+ } catch (IOException ex) {
+ m_text.setText( "Binary/pdf article was not saved to " + m_path + " due to error: " + ex.getMessage());
+ }
+ }
+
+ }
+ }
+ }
+
+ private class RequestNewsProvidersPanel extends JPanel {
+ RequestNewsProvidersPanel() {
+ HtmlButton requestNewsProvidersButton = new HtmlButton( "Request NewsProviders") {
+ @Override protected void actionPerformed() {
+ onRequestNewsProviders();
+ }
+ };
+
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS) );
+ add( requestNewsProvidersButton);
+ }
+
+ void onRequestNewsProviders() {
+ NewsProvidersPanel newsProvidersPanel = new NewsProvidersPanel();
+ m_resultsPanels.addTab("News Providers", newsProvidersPanel, true, true);
+ ApiDemo.INSTANCE.controller().reqNewsProviders(newsProvidersPanel);
+ }
+ }
+
+ static class NewsProvidersPanel extends NewTabPanel implements INewsProvidersHandler {
+ final NewsProvidersModel m_model = new NewsProvidersModel();
+ final List<NewsProvidersRow> m_rows = new ArrayList<>();
+
+ NewsProvidersPanel() {
+ JTable table = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( table);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() { /* noop */ }
+
+ @Override
+ public void newsProviders(NewsProvider[] newsProviders) {
+ for (NewsProvider newsProvider : newsProviders) {
+ NewsProvidersRow newsProvidersRow = new NewsProvidersRow(
+ newsProvider.providerCode(),
+ newsProvider.providerName()
+ );
+ m_rows.add(newsProvidersRow);
+ }
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ revalidate();
+ });
+ }
+
+ class NewsProvidersModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 2;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Provider Code";
+ case 1: return "Provider Name";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ NewsProvidersRow newsProvidersRow = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return newsProvidersRow.m_providerCode;
+ case 1: return newsProvidersRow.m_providerName;
+ default: return null;
+ }
+ }
+ }
+
+ static class NewsProvidersRow {
+ String m_providerCode;
+ String m_providerName;
+
+ NewsProvidersRow(String providerCode, String providerName) {
+ update( providerCode, providerName);
+ }
+
+ void update( String providerCode, String providerName) {
+ m_providerCode = providerCode;
+ m_providerName = providerName;
+ }
+ }
+ }
+
+ class NewsTicksRequestPanel extends JPanel {
+ private UpperField m_symbol = new UpperField();
+ private TCombo<SecType> m_secType = new TCombo<>( SecType.values() );
+ private UpperField m_exchange = new UpperField();
+ private UpperField m_primExchange = new UpperField();
+ private UpperField m_currency = new UpperField();
+
+ NewsTicksRequestPanel() {
+ m_symbol.setText( "IBKR");
+ m_secType.setSelectedItem( SecType.STK);
+ m_exchange.setText( "SMART");
+ m_primExchange.setText( "NYSE");
+ m_currency.setText( "USD");
+
+ HtmlButton but = new HtmlButton( "Request News Ticks") {
+ @Override protected void actionPerformed() {
+ onRequestNewsTicks();
+ }
+ };
+
+ VerticalPanel topPanel = new VerticalPanel();
+ topPanel.add( "Symbol", m_symbol);
+ topPanel.add( "Sec Type", m_secType);
+ topPanel.add( "Exchange", m_exchange, Box.createHorizontalStrut(30), but);
+ topPanel.add( "Prim Exch", m_primExchange);
+ topPanel.add( "Currency", m_currency);
+ setLayout( new BorderLayout() );
+ add( topPanel, BorderLayout.NORTH);
+ }
+
+ void onRequestNewsTicks() {
+ Contract contract = new Contract();
+ contract.symbol( m_symbol.getText().toUpperCase() );
+ contract.secType( m_secType.getSelectedItem() );
+ contract.exchange( m_exchange.getText().toUpperCase() );
+ contract.primaryExch( m_primExchange.getText().toUpperCase() );
+ contract.currency( m_currency.getText().toUpperCase() );
+
+ NewsTicksResultsPanel panel = new NewsTicksResultsPanel();
+ m_resultsPanels.addTab( "News Ticks: " + contract.symbol(), panel, true, true);
+ ApiDemo.INSTANCE.controller().reqNewsTicks(contract, panel);
+ }
+ }
+
+ class NewsTicksResultsPanel extends NewTabPanel implements ITickNewsHandler {
+ final NewsTicksModel m_model = new NewsTicksModel();
+ final List<NewsTickRow> m_rows = new ArrayList<>();
+
+ NewsTicksResultsPanel() {
+ JTable table = new JTable( m_model);
+ table.getSelectionModel().addListSelectionListener(event -> {
+ if (!event.getValueIsAdjusting() && table.getSelectedRow() != -1) {
+ NewsTickRow newsTickRow = m_rows.get( table.getSelectedRow());
+ if (newsTickRow.m_providerCode.length() > 0 && newsTickRow.m_articleId.length() > 0) {
+ m_requestPanels.select( "News Article");
+ m_newsArticleRequestPanel.m_requestPanel.setProviderCode(newsTickRow.m_providerCode);
+ m_newsArticleRequestPanel.m_requestPanel.setArticleId(newsTickRow.m_articleId);
+ }
+ }
+ });
+ table.getColumnModel().getColumn(3).setMinWidth(550);
+ JScrollPane scroll = new JScrollPane( table);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() { /* noop */ }
+
+ @Override
+ public void tickNews(long timeStamp, String providerCode, String articleId, String headline, String extraData) {
+ NewsTickRow newsTickRow = new NewsTickRow(timeStamp, providerCode, articleId, headline, extraData);
+ m_rows.add( newsTickRow);
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ revalidate();
+ repaint();
+ });
+ }
+
+ class NewsTicksModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 5;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Time Stamp";
+ case 1: return "Provider Code";
+ case 2: return "Article Id";
+ case 3: return "Headline";
+ case 4: return "Extra Data";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ NewsTickRow newsTickRow = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return newsTickRow.m_timeStamp;
+ case 1: return newsTickRow.m_providerCode;
+ case 2: return newsTickRow.m_articleId;
+ case 3: return newsTickRow.m_headline;
+ case 4: return newsTickRow.m_extraData;
+ default: return null;
+ }
+ }
+ }
+
+ class NewsTickRow {
+ String m_timeStamp;
+ String m_providerCode;
+ String m_articleId;
+ String m_headline;
+ String m_extraData;
+
+ NewsTickRow(long timeStamp, String providerCode, String articleId, String headline, String extraData) {
+ update( timeStamp, providerCode, articleId, headline, extraData);
+ }
+
+ void update( long timeStamp, String providerCode, String articleId, String headline, String extraData) {
+ m_timeStamp = Util.UnixMillisecondsToString(timeStamp, "yyyy-MM-dd HH:mm:ss zzz");
+ m_providerCode = providerCode;
+ m_articleId = articleId;
+ m_headline = headline;
+ m_extraData = extraData;
+ }
+ }
+ }
+
+ class HistoricalNewsRequestPanel extends JPanel {
+ protected UpperField m_conId = new UpperField("8314");
+ protected JTextField m_providerCodes = new JTextField("BZ+FLY");
+ protected JTextField m_startDateTime = new JTextField();
+ protected JTextField m_endDateTime = new JTextField();
+ protected UpperField m_totalResults = new UpperField("10");
+
+ HistoricalNewsRequestPanel() {
+
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0");
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -3);
+ m_endDateTime.setText(df.format(cal.getTime()));
+ cal.add(Calendar.DATE, -1);
+ m_startDateTime.setText(df.format(cal.getTime()));
+
+ HtmlButton but = new HtmlButton( "Request Historical News") {
+ @Override protected void actionPerformed() {
+ onRequestHistoricalNews();
+ }
+ };
+
+ VerticalPanel topPanel = new VerticalPanel();
+ topPanel.add( "Contract Id", m_conId);
+ topPanel.add( "Provider Codes", m_providerCodes);
+ topPanel.add( "Start Date/Time", m_startDateTime, Box.createHorizontalStrut(30), but);
+ topPanel.add( "End Date/Time", m_endDateTime);
+ topPanel.add( "Total Results", m_totalResults);
+
+ setLayout( new BorderLayout() );
+ add( topPanel, BorderLayout.NORTH);
+ }
+
+ void onRequestHistoricalNews() {
+ HistoricalNewsResultsPanel panel = new HistoricalNewsResultsPanel();
+ m_resultsPanels.addTab( "Hist News: " + m_conId.getText(), panel, true, true);
+ ApiDemo.INSTANCE.controller().reqHistoricalNews(m_conId.getInt(), m_providerCodes.getText(),
+ m_startDateTime.getText(), m_endDateTime.getText(), m_totalResults.getInt(), panel);
+ }
+ }
+
+ class HistoricalNewsResultsPanel extends NewTabPanel implements IHistoricalNewsHandler {
+ final HistoricalNewsModel m_model = new HistoricalNewsModel();
+ final List<HistoricalNewsRow> m_rows = new ArrayList<>();
+
+ HistoricalNewsResultsPanel() {
+ JTable table = new JTable( m_model);
+ table.getSelectionModel().addListSelectionListener(event -> {
+ if (!event.getValueIsAdjusting() && table.getSelectedRow() != -1) {
+ HistoricalNewsRow historicalNewsRow = m_rows.get( table.getSelectedRow());
+ if (historicalNewsRow.m_providerCode.length() > 0 && historicalNewsRow.m_articleId.length() > 0) {
+ m_requestPanels.select( "News Article");
+ m_newsArticleRequestPanel.m_requestPanel.setProviderCode(historicalNewsRow.m_providerCode);
+ m_newsArticleRequestPanel.m_requestPanel.setArticleId(historicalNewsRow.m_articleId);
+ }
+ }
+ });
+ table.getColumnModel().getColumn(3).setMinWidth(650);
+ JScrollPane scroll = new JScrollPane( table);
+ setLayout( new BorderLayout() );
+ add( scroll);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() { /* noop */ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() { /* noop */ }
+
+ @Override
+ public void historicalNews(String time, String providerCode, String articleId, String headline) {
+ HistoricalNewsRow historicalNewsRow = new HistoricalNewsRow(time, providerCode, articleId, headline);
+ m_rows.add( historicalNewsRow);
+ }
+
+ @Override
+ public void historicalNewsEnd(boolean hasMore) {
+ if (hasMore){
+ HistoricalNewsRow historicalNewsRow = new HistoricalNewsRow("", "", "", "has more ...");
+ m_rows.add( historicalNewsRow);
+ }
+ fire();
+ }
+
+ private void fire() {
+ SwingUtilities.invokeLater(() -> {
+ m_model.fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ revalidate();
+ repaint();
+ });
+ }
+
+ class HistoricalNewsModel extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 4;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Time";
+ case 1: return "Provider Code";
+ case 2: return "Article Id";
+ case 3: return "Headline";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ HistoricalNewsRow historicalNewsRow = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return historicalNewsRow.m_time;
+ case 1: return historicalNewsRow.m_providerCode;
+ case 2: return historicalNewsRow.m_articleId;
+ case 3: return historicalNewsRow.m_headline;
+ default: return null;
+ }
+ }
+ }
+
+ class HistoricalNewsRow {
+ String m_time;
+ String m_providerCode;
+ String m_articleId;
+ String m_headline;
+
+ HistoricalNewsRow(String time, String providerCode, String articleId, String headline) {
+ update( time, providerCode, articleId, headline);
+ }
+
+ void update( String time, String providerCode, String articleId, String headline) {
+ m_time = time;
+ m_providerCode = providerCode;
+ m_articleId = articleId;
+ m_headline = headline;
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/OnOKPanel.java b/demo/src/main/java/apidemo/OnOKPanel.java
new file mode 100755
index 0000000..14ba741
--- /dev/null
+++ b/demo/src/main/java/apidemo/OnOKPanel.java
@@ -0,0 +1,12 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.OrderCondition;
+
+import apidemo.util.VerticalPanel;
+
+public abstract class OnOKPanel extends VerticalPanel {
+ public abstract OrderCondition onOK();
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/OperatorConditionPanel.java b/demo/src/main/java/apidemo/OperatorConditionPanel.java
new file mode 100755
index 0000000..99c1c87
--- /dev/null
+++ b/demo/src/main/java/apidemo/OperatorConditionPanel.java
@@ -0,0 +1,31 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.OperatorCondition;
+
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+
+public class OperatorConditionPanel<T extends OperatorCondition> extends OnOKPanel {
+ final T m_condition;
+ final TCombo<String> m_operator = new TCombo<>("<=", ">=");
+ final UpperField m_value = new UpperField();
+
+ OperatorConditionPanel(T condition) {
+ m_condition = condition;
+
+ m_operator.setSelectedIndex(m_condition.isMore() ? 1 : 0);
+ }
+
+ public T onOK() {
+ m_condition.isMore(m_operator.getSelectedIndex() == 1);
+
+ return m_condition;
+ }
+
+ protected T condition() {
+ return m_condition;
+ }
+}
diff --git a/demo/src/main/java/apidemo/OptParamsModel.java b/demo/src/main/java/apidemo/OptParamsModel.java
new file mode 100755
index 0000000..b73802f
--- /dev/null
+++ b/demo/src/main/java/apidemo/OptParamsModel.java
@@ -0,0 +1,57 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.Set;
+
+import javax.swing.table.AbstractTableModel;
+
+public class OptParamsModel extends AbstractTableModel {
+
+ String[] m_expirations;
+ Double[] m_strikes;
+
+ public OptParamsModel(Set<String> expirations, Set<Double> strikes) {
+ expirations.toArray(m_expirations = new String[expirations.size()]);
+ strikes.toArray(m_strikes = new Double[strikes.size()]);
+ }
+
+ @Override
+ public int getRowCount() {
+ return Math.max(m_expirations.length, m_strikes.length);
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return "Expirations";
+
+ case 1:
+ return "Strikes";
+ }
+
+ return super.getColumnName(column);
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return rowIndex < m_expirations.length ? m_expirations[rowIndex] : null;
+
+ case 1:
+ return rowIndex < m_strikes.length ? m_strikes[rowIndex] : null;
+
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/OptionChainsPanel.java b/demo/src/main/java/apidemo/OptionChainsPanel.java
new file mode 100755
index 0000000..e49a5f0
--- /dev/null
+++ b/demo/src/main/java/apidemo/OptionChainsPanel.java
@@ -0,0 +1,310 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import static com.ib.controller.Formats.fmtNz;
+import static com.ib.controller.Formats.fmtPct;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.Timer;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.client.ContractDetails;
+import com.ib.client.MarketDataType;
+import com.ib.client.TickAttr;
+import com.ib.client.TickType;
+import com.ib.client.Types.Right;
+import com.ib.client.Types.SecType;
+import com.ib.controller.ApiController.IContractDetailsHandler;
+import com.ib.controller.ApiController.IOptHandler;
+import com.ib.controller.ApiController.TopMktDataAdapter;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.Util;
+import apidemo.util.VerticalPanel;
+
+class OptionChainsPanel extends JPanel {
+ private Contract m_underContract = new Contract();
+ private NewTabbedPanel m_tabbedPanel = new NewTabbedPanel();
+ private JTextField m_optExch = new UpperField();
+ private UpperField m_symbol = new UpperField();
+ private TCombo<SecType> m_secType = new TCombo<>( SecType.values() );
+ private UpperField m_lastTradeDateOrContractMonth = new UpperField();
+ private UpperField m_exchange = new UpperField();
+ private UpperField m_currency = new UpperField();
+ private JCheckBox m_snapshot = new JCheckBox();
+ private TCombo<String> m_marketDataType = new TCombo<>( MarketDataType.getFields() );
+
+ OptionChainsPanel() {
+ m_symbol.setText( "IBKR");
+ m_secType.setSelectedItem( SecType.STK);
+ m_exchange.setText( "SMART");
+ m_lastTradeDateOrContractMonth.setText("20170616");
+ m_currency.setText( "USD");
+ m_optExch.setText( "SMART");
+ m_marketDataType.setSelectedItem( MarketDataType.REALTIME);
+
+ HtmlButton button = new HtmlButton( "Go") {
+ @Override protected void actionPerformed() {
+ onAdd();
+ }
+ };
+
+ m_marketDataType.addActionListener(event ->
+ ApiDemo.INSTANCE.controller().reqMktDataType( MarketDataType.getField(m_marketDataType.getSelectedItem())));
+
+ VerticalPanel topPanel = new VerticalPanel();
+ topPanel.add( "Symbol", m_symbol);
+ topPanel.add( "Currency", m_currency);
+ topPanel.add( "Underlying sec type", m_secType);
+ topPanel.add( "Underlying exchange", m_exchange, Box.createHorizontalStrut(30), button);
+ topPanel.add( "Last trade date or contract month", m_lastTradeDateOrContractMonth);
+ topPanel.add( "Option exchange", m_optExch);
+ topPanel.add( "Use snapshot data", m_snapshot);
+ topPanel.add( "Market data type", m_marketDataType);
+
+ setLayout( new BorderLayout() );
+ add( topPanel, BorderLayout.NORTH);
+ add( m_tabbedPanel);
+ }
+
+ void onAdd() {
+ m_underContract.symbol( m_symbol.getText().toUpperCase() );
+ m_underContract.secType( m_secType.getSelectedItem() );
+ m_underContract.exchange( m_exchange.getText().toUpperCase() );
+ m_underContract.currency( m_currency.getText().toUpperCase() );
+
+ ApiDemo.INSTANCE.controller().reqContractDetails( m_underContract, this::onRecUnderDetails);
+ }
+
+ void onRecUnderDetails(List<ContractDetails> list) {
+ if (list.size() != 1) {
+ ApiDemo.INSTANCE.show( "Error: " + list.size() + " underlying contracts returned");
+ return;
+ }
+
+ // request option chains
+ Contract optContract = new Contract();
+ optContract.symbol( m_underContract.symbol() );
+ optContract.lastTradeDateOrContractMonth( m_lastTradeDateOrContractMonth.getText() );
+ optContract.currency( m_underContract.currency() );
+ optContract.exchange( m_optExch.getText() );
+ optContract.secType( SecType.OPT);
+
+ final ChainPanel symbolPanel = new ChainPanel();
+ m_tabbedPanel.addTab( optContract.symbol(), symbolPanel, true, true);
+
+ ApiDemo.INSTANCE.controller().reqContractDetails( optContract, symbolPanel);
+ }
+
+ private class ChainPanel extends NewTabPanel implements IContractDetailsHandler, ActionListener {
+ ChainModel m_putsModel = new ChainModel();
+ JTable m_putsTable = new JTable( m_putsModel);
+ ChainModel m_callsModel = new ChainModel();
+ JTable m_callsTable = new JTable( m_callsModel);
+ Timer m_timer = new Timer( 800, this);
+ JLabel m_labUnderPrice = new JLabel();
+ transient TopMktDataAdapter m_stockListener = new TopMktDataAdapter() {
+ @Override public void tickPrice(TickType tickType, double price, TickAttr attribs) {
+ if (tickType == TickType.LAST || tickType == TickType.DELAYED_LAST) {
+ m_labUnderPrice.setText( "" + price);
+ }
+ }
+ };
+
+ ChainPanel() {
+ JScrollPane scrollPuts = new JScrollPane( m_putsTable);
+ scrollPuts.setBorder( new TitledBorder( "Puts"));
+
+ JScrollPane scrollCalls = new JScrollPane( m_callsTable);
+ scrollCalls.setBorder( new TitledBorder( "Calls"));
+
+ VerticalPanel underPanel = new VerticalPanel();
+ underPanel.add( "Underlying price", m_labUnderPrice);
+
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout( new BoxLayout( mainPanel, BoxLayout.X_AXIS));
+ mainPanel.add( scrollCalls);
+ mainPanel.add( scrollPuts);
+
+ setLayout( new BorderLayout() );
+ add( underPanel, BorderLayout.NORTH);
+ add( mainPanel);
+
+ m_timer.start();
+
+ ApiDemo.INSTANCE.controller().reqTopMktData( m_underContract, "", false, false, m_stockListener);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ ApiDemo.INSTANCE.controller().cancelTopMktData( m_stockListener);
+ m_putsModel.desubscribe();
+ m_callsModel.desubscribe();
+ m_timer.stop();
+ }
+
+ @Override public void actionPerformed(ActionEvent e) {
+ m_putsModel.fireTableDataChanged();
+ m_callsModel.fireTableDataChanged();
+ }
+
+ @Override public void contractDetails(List<ContractDetails> list) {
+ for (ContractDetails data : list) {
+ Contract contract = data.contract();
+
+ if (contract.right() == Right.Put) {
+ m_putsModel.addRow( contract, m_snapshot.isSelected() );
+ }
+ else {
+ m_callsModel.addRow( contract, m_snapshot.isSelected() );
+ }
+ }
+ m_putsModel.sort();
+ m_callsModel.sort();
+ }
+
+ private class ChainModel extends AbstractTableModel {
+ Comparator<ChainRow> c = (o1, o2) -> {
+ int rc = o1.m_c.lastTradeDateOrContractMonth().compareTo( o2.m_c.lastTradeDateOrContractMonth());
+ if (rc == 0) {
+ rc = Double.compare(o1.m_c.strike(), o2.m_c.strike());
+ }
+ return rc;
+ };
+
+ List<ChainRow> m_list = new ArrayList<>();
+
+ void desubscribe() {
+ for (ChainRow row : m_list) {
+ ApiDemo.INSTANCE.controller().cancelOptionMktData( row);
+ }
+ }
+
+ @Override public int getRowCount() {
+ return m_list.size();
+ }
+
+ void sort() {
+ m_list.sort(c);
+ fireTableDataChanged();
+ }
+
+ void addRow(Contract contract, boolean snapshot) {
+ ChainRow row = new ChainRow( contract);
+ m_list.add( row);
+
+ ApiDemo.INSTANCE.controller().reqOptionMktData(contract, "", snapshot, false, row);
+
+ if (snapshot) {
+ Util.sleep( 11); // try to avoid pacing violation at TWS
+ }
+ }
+
+ @Override public int getColumnCount() {
+ return m_snapshot.isSelected() ? 10 : 9;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Last trade date";
+ case 1: return "Strike";
+ case 2: return "Bid";
+ case 3: return "Ask";
+ case 4: return "Imp Vol";
+ case 5: return "Delta";
+ case 6: return "Gamma";
+ case 7: return "Vega";
+ case 8: return "Theta";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ ChainRow row = m_list.get( rowIn);
+ switch( col) {
+ case 0: return row.m_c.lastTradeDateOrContractMonth();
+ case 1: return row.m_c.strike();
+ case 2: return fmtNz( row.m_bid);
+ case 3: return fmtNz( row.m_ask);
+ case 4: return fmtPct( row.m_impVol);
+ case 5: return fmtNz( row.m_delta);
+ case 6: return fmtNz( row.m_gamma);
+ case 7: return fmtNz( row.m_vega);
+ case 8: return fmtNz( row.m_theta);
+ case 9: return row.m_done ? "*" : null;
+ default: return null;
+ }
+ }
+
+ private class ChainRow extends TopMktDataAdapter implements IOptHandler {
+ Contract m_c;
+ double m_bid;
+ double m_ask;
+ double m_impVol;
+ double m_delta;
+ double m_gamma;
+ double m_vega;
+ double m_theta;
+ boolean m_done;
+
+ ChainRow(Contract contract) {
+ m_c = contract;
+ }
+
+ @Override public void tickPrice(TickType tickType, double price, TickAttr attribs) {
+ switch( tickType) {
+ case BID:
+ case DELAYED_BID:
+ m_bid = price;
+ break;
+ case ASK:
+ case DELAYED_ASK:
+ m_ask = price;
+ break;
+ default: break;
+ }
+ }
+
+ @Override public void tickOptionComputation( TickType tickType, double impVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) {
+ if (tickType == TickType.MODEL_OPTION || tickType == TickType.DELAYED_MODEL_OPTION) {
+ m_impVol = impVol;
+ m_delta = delta;
+ m_gamma = gamma;
+ m_vega = vega;
+ m_theta = theta;
+ }
+ }
+
+ @Override public void tickSnapshotEnd() {
+ m_done = true;
+ }
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/OptionsPanel.java b/demo/src/main/java/apidemo/OptionsPanel.java
new file mode 100755
index 0000000..5a53080
--- /dev/null
+++ b/demo/src/main/java/apidemo/OptionsPanel.java
@@ -0,0 +1,17 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import apidemo.util.NewTabbedPanel;
+
+public class OptionsPanel extends NewTabbedPanel {
+ private final OptionChainsPanel m_optionChains = new OptionChainsPanel();
+ private final ExercisePanel m_exercisePanel = new ExercisePanel();
+
+ OptionsPanel() {
+ NewTabbedPanel tabs = this;
+ tabs.addTab( "Option Chains", m_optionChains);
+ tabs.addTab( "Option Exercise", m_exercisePanel);
+ }
+}
diff --git a/demo/src/main/java/apidemo/OrdersPanel.java b/demo/src/main/java/apidemo/OrdersPanel.java
new file mode 100755
index 0000000..0b439f9
--- /dev/null
+++ b/demo/src/main/java/apidemo/OrdersPanel.java
@@ -0,0 +1,288 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.client.Order;
+import com.ib.client.OrderState;
+import com.ib.client.OrderStatus;
+import com.ib.client.OrderType;
+import com.ib.controller.ApiController.ILiveOrderHandler;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.VerticalPanel;
+
+public class OrdersPanel extends JPanel {
+ private OrdersModel m_model = new OrdersModel();
+ private JTable m_table = new JTable( m_model);
+
+ OrdersPanel() {
+ JScrollPane scroll = new JScrollPane( m_table);
+ scroll.setBorder( new TitledBorder( "Live Orders"));
+
+ m_table.addMouseListener( new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ onDoubleClick();
+ }
+ }
+ });
+
+ HtmlButton ticket = new HtmlButton( "Place New Order") {
+ @Override public void actionPerformed() {
+ onPlaceOrder();
+ }
+ };
+
+ HtmlButton modify = new HtmlButton( "Modify Selected Order") {
+ @Override public void actionPerformed() {
+ onDoubleClick();
+ }
+ };
+
+ HtmlButton attach = new HtmlButton( "Attach New Order to Selected Order") {
+ @Override public void actionPerformed() {
+ onAttachOrder();
+ }
+ };
+
+ HtmlButton reqExisting = new HtmlButton( "Take Over Existing TWS Orders") {
+ @Override public void actionPerformed() {
+ onTakeOverExisting();
+ }
+ };
+
+ HtmlButton reqFuture = new HtmlButton( "Take Over Future TWS Orders") {
+ @Override public void actionPerformed() {
+ onTakeOverFuture();
+ }
+ };
+
+ HtmlButton cancel = new HtmlButton( "Cancel Selected Order") {
+ @Override public void actionPerformed() {
+ onCancel();
+ }
+ };
+
+ HtmlButton cancelAll = new HtmlButton( "Cancel All Orders") {
+ @Override public void actionPerformed() {
+ onCancelAll();
+ }
+ };
+
+ HtmlButton refresh = new HtmlButton( "Refresh") {
+ @Override public void actionPerformed() {
+ onRefresh();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( ticket);
+ buts.add( modify);
+ buts.add( attach);
+ buts.add( cancel);
+ buts.add( cancelAll);
+ buts.add( reqExisting);
+ buts.add( reqFuture);
+ buts.add( refresh);
+
+ setLayout( new BorderLayout() );
+ add( buts, BorderLayout.EAST);
+ add( scroll);
+ }
+
+ protected void onDoubleClick() {
+ OrderRow order = getSelectedOrder();
+ if (order != null) {
+ TicketDlg dlg = new TicketDlg( order.m_contract, order.m_order);
+ dlg.setVisible( true);
+ }
+ }
+
+ protected void onTakeOverExisting() {
+ ApiDemo.INSTANCE.controller().takeTwsOrders( m_model);
+ }
+
+ protected void onTakeOverFuture() {
+ ApiDemo.INSTANCE.controller().takeFutureTwsOrders( m_model);
+ }
+
+ protected void onCancel() {
+ OrderRow order = getSelectedOrder();
+ if (order != null) {
+ ApiDemo.INSTANCE.controller().cancelOrder( order.m_order.orderId() );
+ }
+ }
+
+ protected void onCancelAll() {
+ ApiDemo.INSTANCE.controller().cancelAllOrders();
+ }
+
+ private OrderRow getSelectedOrder() {
+ int i = m_table.getSelectedRow();
+ return i != -1 ? m_model.get( i) : null;
+ }
+
+ public void activated() {
+ onRefresh();
+ }
+
+ private static void onPlaceOrder() {
+ TicketDlg dlg = new TicketDlg( null, null);
+ dlg.setVisible( true);
+ }
+
+ protected void onAttachOrder() {
+ OrderRow row = getSelectedOrder();
+ if (row != null) {
+ Order parent = row.m_order;
+
+ Order child = new Order();
+ child.parentId( parent.orderId() );
+ child.action( parent.action() );
+ child.totalQuantity( parent.totalQuantity() );
+ child.orderType( OrderType.TRAIL);
+ child.auxPrice( 1);
+
+ TicketDlg dlg = new TicketDlg( row.m_contract.clone(), child);
+ dlg.setVisible( true);
+ }
+ }
+
+ protected void onRefresh() {
+ m_model.clear();
+ m_model.fireTableDataChanged();
+ ApiDemo.INSTANCE.controller().reqLiveOrders( m_model);
+ }
+
+ static class OrdersModel extends AbstractTableModel implements ILiveOrderHandler {
+ private Map<Integer,OrderRow> m_map = new HashMap<>();
+ private List<OrderRow> m_orders = new ArrayList<>();
+
+ @Override public int getRowCount() {
+ return m_orders.size();
+ }
+
+ public void clear() {
+ m_orders.clear();
+ m_map.clear();
+ }
+
+ public OrderRow get(int i) {
+ return m_orders.get( i);
+ }
+
+ @Override public void openOrder(Contract contract, Order order, OrderState orderState) {
+ OrderRow full = m_map.get( order.permId() );
+
+ if (full != null) {
+ full.m_order = order;
+ full.m_state = orderState;
+ fireTableDataChanged();
+ }
+ else if (shouldAdd(contract, order, orderState) ) {
+ full = new OrderRow( contract, order, orderState);
+ add( full);
+ m_map.put( order.permId(), full);
+ fireTableDataChanged();
+ }
+ }
+
+ protected boolean shouldAdd(Contract contract, Order order, OrderState orderState) {
+ return true;
+ }
+
+ protected void add(OrderRow full) {
+ m_orders.add( full);
+ }
+
+ @Override public void openOrderEnd() {
+ }
+
+ @Override public void orderStatus(int orderId, OrderStatus status, double filled, double remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
+ OrderRow full = m_map.get( permId);
+ if (full != null) {
+ full.m_state.status( status);
+ }
+ fireTableDataChanged();
+ }
+
+ @Override public int getColumnCount() {
+ return 10;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Perm ID";
+ case 1: return "Client ID";
+ case 2: return "Order ID";
+ case 3: return "Account";
+ case 4: return "ModelCode";
+ case 5: return "Action";
+ case 6: return "Quantity";
+ case 7: return "Cash Qty";
+ case 8: return "Contract";
+ case 9: return "Status";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int row, int col) {
+ OrderRow fullOrder = m_orders.get( row);
+ Order order = fullOrder.m_order;
+ switch( col) {
+ case 0: return order.permId();
+ case 1: return order.clientId();
+ case 2: return order.orderId();
+ case 3: return order.account();
+ case 4: return order.modelCode();
+ case 5: return order.action();
+ case 6: return order.totalQuantity();
+ case 7: return (order.cashQty() == Double.MAX_VALUE) ? "" : String.valueOf(order.cashQty());
+ case 8: return fullOrder.m_contract.description();
+ case 9: return fullOrder.m_state.status();
+ default: return null;
+ }
+ }
+
+ @Override public void handle(int orderId, int errorCode, String errorMsg) {
+ }
+ }
+
+ static class OrderRow {
+ Contract m_contract;
+ Order m_order;
+ OrderState m_state;
+
+ OrderRow( Contract contract, Order order, OrderState state) {
+ m_contract = contract;
+ m_order = order;
+ m_state = state;
+ }
+ }
+
+ static class Key {
+ int m_clientId;
+ int m_orderId;
+
+ Key( int clientId, int orderId) {
+ m_clientId = clientId;
+ m_orderId = orderId;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/PegBenchPanel.java b/demo/src/main/java/apidemo/PegBenchPanel.java
new file mode 100755
index 0000000..10a741d
--- /dev/null
+++ b/demo/src/main/java/apidemo/PegBenchPanel.java
@@ -0,0 +1,62 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import javax.swing.JDialog;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.Order;
+import com.ib.client.OrderCondition;
+
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+
+public class PegBenchPanel extends OnOKPanel {
+ /**
+ *
+ */
+ private final JDialog m_parentDlg;
+ private final Order m_order;
+ final UpperField m_startingPrice = new UpperField();
+ final UpperField m_startingRefPrice = new UpperField();
+ final ContractLookupButton m_refCon;
+ final UpperField m_pegChangeAmount = new UpperField();
+ final UpperField m_refChangeAmount = new UpperField();
+ final TCombo<String> m_pegChangeType = new TCombo<>("increase", "decrease");
+
+ public PegBenchPanel(JDialog parentDlg, Order order, ContractLookuper lookuper) {
+ m_parentDlg = parentDlg;
+ m_order = order;
+ m_startingPrice.setText(m_order.startingPrice());
+ m_startingRefPrice.setText(m_order.stockRefPrice());
+ m_pegChangeAmount.setText(m_order.peggedChangeAmount());
+ m_refChangeAmount.setText(m_order.referenceChangeAmount());
+ m_pegChangeType.setSelectedIndex(m_order.isPeggedChangeAmountDecrease() ? 1 : 0);
+
+ m_refCon = new ContractLookupButton(m_order.referenceContractId(), m_order.referenceExchangeId(), lookuper) {
+ @Override
+ protected void actionPerformed(int refConId, String refExchId) {
+ PegBenchPanel.this.m_order.referenceContractId(refConId);
+ PegBenchPanel.this.m_order.referenceExchangeId(refExchId);
+ }
+ };
+
+ add("Starting price", m_startingPrice);
+ add("Reference contract", m_refCon);
+ add("Starting reference price", m_startingRefPrice);
+ add("Pegged change amount", m_pegChangeAmount);
+ add("Pegged change type", m_pegChangeType);
+ add("Reference change amount", m_refChangeAmount);
+ }
+
+ public OrderCondition onOK() {
+ m_order.startingPrice(m_startingPrice.getDouble());
+ m_order.stockRefPrice(m_startingRefPrice.getDouble());
+ m_order.peggedChangeAmount(m_pegChangeAmount.getDouble());
+ m_order.referenceChangeAmount(m_refChangeAmount.getDouble());
+ m_order.isPeggedChangeAmountDecrease(m_pegChangeType.getSelectedIndex() == 1);
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/PercentConditionPanel.java b/demo/src/main/java/apidemo/PercentConditionPanel.java
new file mode 100755
index 0000000..710cd3f
--- /dev/null
+++ b/demo/src/main/java/apidemo/PercentConditionPanel.java
@@ -0,0 +1,27 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.PercentChangeCondition;
+
+public class PercentConditionPanel extends ContractConditionPanel<PercentChangeCondition> {
+
+ PercentConditionPanel(PercentChangeCondition condition, ContractLookuper lookuper) {
+ super(condition, lookuper);
+
+ m_value.setText(condition().changePercent());
+
+ add("Operator", m_operator);
+ add("Percentage Change", m_value);
+ }
+
+
+ public PercentChangeCondition onOK() {
+ super.onOK();
+ condition().changePercent(m_value.getDouble());
+
+ return condition();
+ }
+}
diff --git a/demo/src/main/java/apidemo/PnLModel.java b/demo/src/main/java/apidemo/PnLModel.java
new file mode 100755
index 0000000..97e95da
--- /dev/null
+++ b/demo/src/main/java/apidemo/PnLModel.java
@@ -0,0 +1,89 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+public class PnLModel extends AbstractTableModel {
+
+ private class PnLItem {
+
+ public PnLItem(double dailyPnL, double unrealizedPnL, double realizedPnL) {
+ m_dailyPnL = dailyPnL;
+ m_unrealizedPnL = unrealizedPnL;
+ m_realizedPnL = realizedPnL;
+ }
+
+ public double dailyPnL() {
+ return m_dailyPnL;
+ }
+
+ public double unrealizedPnL() {
+ return m_unrealizedPnL;
+ }
+
+ public double realizedPnL() {
+ return m_realizedPnL;
+ }
+
+ private double m_dailyPnL;
+ private double m_unrealizedPnL;
+ private double m_realizedPnL;
+
+ }
+
+ private List<PnLItem> m_rows = new ArrayList<>();
+ private static Dictionary<Integer, String> columnNames = new Hashtable<>();
+
+ static {
+ columnNames.put(0, "Daily Pnl");
+ columnNames.put(1, "Unrealized PnL");
+ columnNames.put(2, "Realized PnL");
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ return columnNames.get(column);
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 3;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ PnLItem item = m_rows.get(rowIndex);
+
+ switch (columnIndex) {
+ case 0:
+ return item.dailyPnL();
+
+ case 1:
+ return item.unrealizedPnL();
+
+ case 2:
+ return item.realizedPnL();
+ }
+
+ return null;
+ }
+
+ public void addRow(double dailyPnL, double unrealizedPnL, double realizedPnL) {
+ m_rows.add(new PnLItem(dailyPnL, unrealizedPnL, realizedPnL));
+
+ fireTableDataChanged();
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/PnLSingleModel.java b/demo/src/main/java/apidemo/PnLSingleModel.java
new file mode 100755
index 0000000..9b95984
--- /dev/null
+++ b/demo/src/main/java/apidemo/PnLSingleModel.java
@@ -0,0 +1,85 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+public class PnLSingleModel extends AbstractTableModel {
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return "Pos";
+ case 1:
+ return "Daily PnL";
+ case 2:
+ return "Unrealized PnL";
+ case 3:
+ return "Realized PnL";
+ case 4:
+ return "Value";
+ default:
+ return super.getColumnName(column);
+ }
+ }
+
+ static class Row {
+ int m_pos;
+ double m_dailyPnL;
+ double m_unrealizedPnL;
+ double m_realizedPnL;
+ double m_value;
+
+ public Row(int pos, double dailyPnL, double unrealizedPnL, double realizedPnL, double value) {
+ m_pos = pos;
+ m_dailyPnL = dailyPnL;
+ m_unrealizedPnL = unrealizedPnL;
+ m_realizedPnL = realizedPnL;
+ m_value = value;
+ }
+ }
+
+ List<Row> m_rows = new ArrayList<>();
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 5;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ Row r = m_rows.get(rowIndex);
+
+ switch (columnIndex) {
+ case 0:
+ return r.m_pos;
+ case 1:
+ return r.m_dailyPnL;
+ case 2:
+ return r.m_unrealizedPnL;
+ case 3:
+ return r.m_realizedPnL;
+ case 4:
+ return r.m_value;
+ default:
+ return null;
+ }
+ }
+
+ public void addRow(int pos, double dailyPnL, double unrealizedPnL, double realizedPnL, double value) {
+ m_rows.add(new Row(pos, dailyPnL, unrealizedPnL, realizedPnL, value));
+
+ fireTableDataChanged();
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/PositionsPanel.java b/demo/src/main/java/apidemo/PositionsPanel.java
new file mode 100755
index 0000000..f7bd268
--- /dev/null
+++ b/demo/src/main/java/apidemo/PositionsPanel.java
@@ -0,0 +1,174 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.controller.ApiController.IPositionHandler;
+import com.ib.controller.Formats;
+
+import apidemo.AccountInfoPanel.Table;
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel;
+
+
+public class PositionsPanel extends NewTabPanel {
+ private PositionModel m_model = new PositionModel();
+ private boolean m_complete;
+
+ PositionsPanel() {
+ HtmlButton sub = new HtmlButton( "Subscribe") {
+ protected void actionPerformed() {
+ subscribe();
+ }
+ };
+
+ HtmlButton desub = new HtmlButton( "Desubscribe") {
+ protected void actionPerformed() {
+ desubscribe();
+ }
+ };
+
+ JPanel buts = new VerticalPanel();
+ buts.add( sub);
+ buts.add( desub);
+
+ JTable table = new Table( m_model, 2);
+ JScrollPane scroll = new JScrollPane( table);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( buts, BorderLayout.EAST);
+ }
+
+ /** Called when the tab is first visited. Sends request for all positions. */
+ @Override public void activated() {
+ subscribe();
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ desubscribe();
+ }
+
+ private void subscribe() {
+ ApiDemo.INSTANCE.controller().reqPositions( m_model);
+ }
+
+ private void desubscribe() {
+ ApiDemo.INSTANCE.controller().cancelPositions( m_model);
+ m_model.clear();
+ }
+
+ private class PositionModel extends AbstractTableModel implements IPositionHandler {
+ Map<PositionKey,PositionRow> m_map = new HashMap<>();
+ List<PositionRow> m_list = new ArrayList<>();
+
+ @Override public void position(String account, Contract contract, double position, double avgCost) {
+ PositionKey key = new PositionKey( account, contract.conid() );
+ PositionRow row = m_map.get( key);
+ if (row == null) {
+ row = new PositionRow();
+ m_map.put( key, row);
+ m_list.add( row);
+ }
+ row.update( account, contract, position, avgCost);
+
+ if (m_complete) {
+ m_model.fireTableDataChanged();
+ }
+ }
+
+ @Override public void positionEnd() {
+ m_model.fireTableDataChanged();
+ m_complete = true;
+ }
+
+ public void clear() {
+ m_map.clear();
+ m_list.clear();
+ fireTableDataChanged();
+ }
+
+ @Override public int getRowCount() {
+ return m_map.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 4;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Account";
+ case 1: return "Contract";
+ case 2: return "Position";
+ case 3: return "Avg Cost";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ PositionRow row = m_list.get( rowIn);
+
+ switch( col) {
+ case 0: return row.m_account;
+ case 1: return row.m_contract.description();
+ case 2: return row.m_position;
+ case 3: return Formats.fmt( row.m_avgCost);
+ default: return null;
+ }
+ }
+ }
+
+ private static class PositionKey {
+ String m_account;
+ int m_conid;
+
+ PositionKey( String account, int conid) {
+ m_account = account;
+ m_conid = conid;
+ }
+
+ @Override public int hashCode() {
+ return m_account.hashCode() + m_conid;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PositionKey)) {
+ return false;
+ }
+ PositionKey other = (PositionKey)obj;
+ return m_account.equals( other.m_account) && m_conid == other.m_conid;
+ }
+ }
+
+ private static class PositionRow {
+ String m_account;
+ Contract m_contract;
+ double m_position;
+ double m_avgCost;
+
+ void update(String account, Contract contract, double position, double avgCost) {
+ m_account = account;
+ m_contract = contract;
+ m_position = position;
+ m_avgCost = avgCost;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/PriceConditionPanel.java b/demo/src/main/java/apidemo/PriceConditionPanel.java
new file mode 100755
index 0000000..5ef3c88
--- /dev/null
+++ b/demo/src/main/java/apidemo/PriceConditionPanel.java
@@ -0,0 +1,73 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.PriceCondition;
+
+import apidemo.util.TCombo;
+
+public class PriceConditionPanel extends ContractConditionPanel<PriceCondition> {
+
+ enum Method {
+ Default(0),
+ DoubleBidAsk(1),
+ Last(2),
+ DoubleLast(3),
+ BidAsk(4),
+ LastBidAsk(7),
+ MidPoint(8);
+
+ private static String[] names = new String[] { "default", "double bid/ask", "last", "double last", "bid/ask", "", "", "last of bid/ask", "mid-point" };
+ private int m_value;
+
+ Method(int v) {
+ m_value = v;
+ }
+
+ public int value() {
+ return m_value;
+ }
+
+ @Override
+ public String toString() {
+ if (m_value < 0 || m_value >= names.length)
+ return super.toString();
+
+ return names[m_value];
+ }
+
+ static Method fromInt(int i) {
+ for (Method m : Method.values()) {
+ if (m.value() == i)
+ return m;
+ }
+
+ return null;
+ }
+ }
+
+ final private TCombo<Method> m_method = new TCombo<>(Method.values());
+
+ PriceConditionPanel(PriceCondition condition, ContractLookuper lookuper) {
+ super(condition, lookuper);
+
+ m_method.setSelectedItem(Method.fromInt(condition().triggerMethod()));
+ m_value.setText(condition().price());
+
+ add("Method", m_method);
+ add("Operator", m_operator);
+ add("Price", m_value);
+ }
+
+ @Override
+ public PriceCondition onOK() {
+ super.onOK();
+ condition().price(m_value.getDouble());
+ condition().triggerMethod(m_method.getSelectedItem().value());
+
+ return condition();
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/StratPanel.java b/demo/src/main/java/apidemo/StratPanel.java
new file mode 100755
index 0000000..c8e9247
--- /dev/null
+++ b/demo/src/main/java/apidemo/StratPanel.java
@@ -0,0 +1,205 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.border.TitledBorder;
+
+import com.ib.client.Contract;
+import com.ib.client.Types.BarSize;
+import com.ib.client.Types.DurationUnit;
+import com.ib.client.Types.WhatToShow;
+import com.ib.controller.ApiController.IHistoricalDataHandler;
+import com.ib.controller.ApiController.IRealTimeBarHandler;
+import com.ib.controller.Bar;
+
+import apidemo.OrdersPanel.OrdersModel;
+import apidemo.util.HtmlButton;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.VerticalPanel.BorderPanel;
+import apidemo.util.VerticalPanel.HorzPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+
+public class StratPanel extends StackPanel implements IHistoricalDataHandler, IRealTimeBarHandler {
+ final private Contract m_contract = new Contract();
+ final private ContractPanel m_contractPanel = new ContractPanel( m_contract);
+ final private UpperField m_shares = new UpperField();
+ final private UpperField m_pct1 = new UpperField();
+ final private UpperField m_pct2 = new UpperField();
+ final private OrdersModel m_ordersModel = new OrdersModel();
+ final private TCombo<BarSize> m_barSize = new TCombo<>( BarSize.values() );
+ final private UpperField m_bars = new UpperField();
+ final private List<Bar> m_rows = new ArrayList<>();
+ final private Chart m_chart = new Chart( m_rows);
+ private boolean m_req;
+
+ private static Component sp(int n) { return Box.createHorizontalStrut(n); }
+
+ StratPanel() {
+ m_contractPanel.setBorder( new TitledBorder( "Define Contract"));
+
+ JPanel p1 = new HPanel();
+ add( p1, "Go long", sp(5), m_shares, sp(5), "shares when ask goes above SMA by", sp(5), m_pct1, "%");
+
+ JPanel p2 = new HPanel();
+ add( p2, "Go flat when bid goes below SMA by", sp(5), m_pct2, "%");
+
+ JPanel p3 = new HPanel();
+ add( p3, "SMA bar size:", sp(5), m_barSize, sp(20), "SMA number of bars", sp(5), m_bars);
+
+ HtmlButton start = new HtmlButton( "Start") {
+ @Override protected void actionPerformed() {
+ onStart();
+ }
+ };
+
+ HtmlButton stop = new HtmlButton( "Stop") {
+ @Override protected void actionPerformed() {
+ onStop();
+ }
+ };
+
+ JPanel buts = new JPanel();
+ buts.add( start);
+ buts.add( Box.createHorizontalStrut(30));
+ buts.add( stop);
+
+ StackPanel rightPanel = new StackPanel();
+ rightPanel.setBorder( new TitledBorder( "Define Strategy"));
+ rightPanel.add( p1);
+ rightPanel.add( Box.createVerticalStrut(10));
+ rightPanel.add( p2);
+ rightPanel.add( Box.createVerticalStrut(10));
+ rightPanel.add( p3);
+ rightPanel.add( Box.createVerticalStrut(10));
+ rightPanel.add( buts);
+
+ JScrollPane chartScroll = new JScrollPane( m_chart);
+ m_chart.setBorder( new TitledBorder( "chart"));
+ chartScroll.setBorder( new TitledBorder( "chart scroll"));
+
+ HorzPanel horzPanel = new HorzPanel();
+ horzPanel.add( m_contractPanel);
+ horzPanel.add( rightPanel);
+
+ BorderPanel topPanel = new BorderPanel();
+ topPanel.add( horzPanel, BorderLayout.WEST);
+ topPanel.add( chartScroll);
+
+ JTable ordersTable = new JTable( m_ordersModel);
+ JScrollPane ordersScroll = new JScrollPane( ordersTable);
+ ordersScroll.setBorder( new TitledBorder( "Orders"));
+
+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS));
+ add( topPanel);
+ add( ordersScroll);
+ add( new TradesPanel() );
+ }
+
+ protected void onStart() {
+ m_contractPanel.onOK();
+ ApiDemo.INSTANCE.controller().reqRealTimeBars(m_contract, WhatToShow.TRADES, false, this);
+ }
+
+ @Override public void realtimeBar(Bar bar) {
+ if (!m_req) {
+ BarSize barSize = m_barSize.getSelectedItem();
+ QueryLength queryLength = getQueryLength( barSize);
+ if (queryLength == null) return;
+ String date = Bar.format( bar.time() * 1000);
+ int duration = m_bars.getInt() * queryLength.m_units;
+ ApiDemo.INSTANCE.controller().reqHistoricalData(m_contract, date, duration, queryLength.m_unit, barSize, WhatToShow.TRADES, false, false, this);
+ m_req = true;
+ }
+ addBar( bar);
+ m_chart.repaint();
+ }
+
+ private Map<Long, Bar> m_map = new TreeMap<>();
+
+ @Override public void historicalData(Bar bar) {
+ System.out.println( bar);
+ addBar( bar);
+ }
+
+ private void addBar( Bar bar) {
+ m_map.put( bar.time(), bar);
+ m_rows.clear();
+ m_rows.addAll( m_map.values() );
+ }
+
+ @Override public void historicalDataEnd() {
+ m_chart.repaint();
+ }
+
+ static class QueryLength {
+ int m_units;
+ DurationUnit m_unit;
+
+ QueryLength( int units, DurationUnit unit) {
+ m_units = units;
+ m_unit = unit;
+ }
+ }
+
+ protected void onStop() {
+ ApiDemo.INSTANCE.controller().cancelRealtimeBars(this);
+ ApiDemo.INSTANCE.controller().cancelHistoricalData(this);
+ }
+
+ void add( JPanel p, Object...objs) {
+ for (Object obj : objs) {
+ if (obj instanceof String) {
+ p.add( new JLabel( (String)obj) );
+ }
+ else {
+ p.add( (Component)obj);
+ }
+ }
+ }
+
+ class HPanel extends HorzPanel {
+ @Override public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+ }
+
+ private static QueryLength getQueryLength( BarSize barSize) {
+ switch( barSize) {
+ case _1_secs: return new QueryLength( 1, DurationUnit.SECOND);
+ case _5_secs: return new QueryLength( 5, DurationUnit.SECOND);
+ case _10_secs: return new QueryLength( 10, DurationUnit.SECOND);
+ case _15_secs: return new QueryLength( 15, DurationUnit.SECOND);
+ case _30_secs: return new QueryLength( 30, DurationUnit.SECOND);
+ case _1_min: return new QueryLength( 60, DurationUnit.SECOND);
+ case _2_mins: return new QueryLength( 120, DurationUnit.SECOND);
+ case _3_mins: return new QueryLength( 180, DurationUnit.SECOND);
+ case _5_mins: return new QueryLength( 300, DurationUnit.SECOND);
+ case _10_mins: return new QueryLength( 600, DurationUnit.SECOND);
+ case _15_mins: return new QueryLength( 900, DurationUnit.SECOND);
+ case _20_mins: return new QueryLength( 1200, DurationUnit.SECOND);
+ case _30_mins: return new QueryLength( 1800, DurationUnit.SECOND);
+ case _1_hour: return new QueryLength( 3600, DurationUnit.SECOND);
+ case _4_hours: return new QueryLength( 14400, DurationUnit.SECOND);
+ case _1_day: return new QueryLength( 1, DurationUnit.DAY);
+ case _1_week: return new QueryLength( 1, DurationUnit.WEEK);
+ default: return null;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/Test.java b/demo/src/main/java/apidemo/Test.java
new file mode 100755
index 0000000..1be146e
--- /dev/null
+++ b/demo/src/main/java/apidemo/Test.java
@@ -0,0 +1,431 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import static apidemo.util.Util.sleep;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.ib.client.*;
+import com.ib.client.HistoricalTick;
+import com.ib.client.HistoricalTickBidAsk;
+import com.ib.client.HistoricalTickLast;
+
+import javax.swing.*;
+
+public class Test implements EWrapper {
+ private EJavaSignal m_signal = new EJavaSignal();
+ private EClientSocket m_s = new EClientSocket(this, m_signal);
+ private int NextOrderId = -1;
+
+ public static void main(String[] args) {
+ new Test().run();
+ }
+
+ private void run() {
+ m_s.eConnect("localhost", 7497, 0);
+
+ final EReader reader = new EReader(m_s, m_signal);
+
+ reader.start();
+
+ new Thread(() -> {
+ while (m_s.isConnected()) {
+ m_signal.waitForSignal();
+ try {
+ SwingUtilities.invokeAndWait(() -> {
+ try {
+ reader.processMsgs();
+ } catch (IOException e) {
+ error(e);
+ }
+ });
+ } catch (Exception e) {
+ error(e);
+ }
+ }
+ }).start();
+
+ if (NextOrderId < 0) {
+ sleep(1000);
+ }
+
+ m_s.reqSecDefOptParams(0, "IBM", "",/* "",*/ "STK", 8314);
+
+ try {
+ System.in.read();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ m_s.eDisconnect();
+ }
+
+ @Override public void nextValidId(int orderId) {
+ NextOrderId = orderId;
+ System.out.println(EWrapperMsgGenerator.nextValidId(orderId));
+ }
+
+ @Override public void error(Exception e) {
+ System.out.println(EWrapperMsgGenerator.error(e));
+ }
+
+ @Override public void error(int id, int errorCode, String errorMsg) {
+ System.out.println(EWrapperMsgGenerator.error(id, errorCode, errorMsg));
+ }
+
+ @Override public void connectionClosed() {
+ System.out.println(EWrapperMsgGenerator.connectionClosed());
+ }
+
+ @Override public void error(String str) {
+ System.out.println(EWrapperMsgGenerator.error(str));
+ }
+
+ @Override public void tickPrice(int tickerId, int field, double price, TickAttr attribs) {
+ System.out.println(EWrapperMsgGenerator.tickPrice(tickerId, field, price, attribs));
+ }
+
+ @Override public void tickSize(int tickerId, int field, int size) {
+ System.out.println(EWrapperMsgGenerator.tickSize(tickerId, field, size));
+ }
+
+ @Override public void tickOptionComputation(int tickerId, int field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) {
+ System.out.println(EWrapperMsgGenerator.tickOptionComputation(tickerId, field, impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice));
+ }
+
+ @Override public void tickGeneric(int tickerId, int tickType, double value) {
+ System.out.println(EWrapperMsgGenerator.tickGeneric(tickerId, tickType, value));
+ }
+
+ @Override public void tickString(int tickerId, int tickType, String value) {
+ System.out.println(EWrapperMsgGenerator.tickString(tickerId, tickType, value));
+ }
+
+ @Override public void tickEFP(int tickerId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureLastTradeDate, double dividendImpact,
+ double dividendsToLastTradeDate) {
+ System.out.println(EWrapperMsgGenerator.tickEFP( tickerId, tickType, basisPoints, formattedBasisPoints, impliedFuture, holdDays, futureLastTradeDate, dividendImpact, dividendsToLastTradeDate));
+ }
+
+ @Override public void orderStatus(int orderId, String status, double filled, double remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
+ System.out.println(EWrapperMsgGenerator.orderStatus( orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice));
+ }
+
+ @Override public void openOrder(int orderId, Contract contract, Order order, OrderState orderState) {
+ System.out.println(EWrapperMsgGenerator.openOrder( orderId, contract, order, orderState));
+ }
+
+ @Override public void openOrderEnd() {
+ System.out.println(EWrapperMsgGenerator.openOrderEnd());
+ }
+
+ @Override public void updateAccountValue(String key, String value, String currency, String accountName) {
+ System.out.println(EWrapperMsgGenerator.updateAccountValue( key, value, currency, accountName));
+ }
+
+ @Override public void updatePortfolio(Contract contract, double position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName) {
+ System.out.println(EWrapperMsgGenerator.updatePortfolio( contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName));
+ }
+
+ @Override public void updateAccountTime(String timeStamp) {
+ System.out.println(EWrapperMsgGenerator.updateAccountTime( timeStamp));
+ }
+
+ @Override public void accountDownloadEnd(String accountName) {
+ System.out.println(EWrapperMsgGenerator.accountDownloadEnd(accountName));
+ }
+
+ @Override public void contractDetails(int reqId, ContractDetails contractDetails) {
+ System.out.println(EWrapperMsgGenerator.contractDetails( reqId, contractDetails));
+ }
+
+ @Override public void bondContractDetails(int reqId, ContractDetails contractDetails) {
+ System.out.println(EWrapperMsgGenerator.bondContractDetails( reqId, contractDetails));
+ }
+
+ @Override public void contractDetailsEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.contractDetailsEnd(reqId));
+ }
+
+ @Override public void execDetails(int reqId, Contract contract, Execution execution) {
+ System.out.println(EWrapperMsgGenerator.execDetails( reqId, contract, execution));
+ }
+
+ @Override public void execDetailsEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.execDetailsEnd( reqId));
+ }
+
+ @Override public void updateMktDepth(int tickerId, int position, int operation, int side, double price, int size) {
+ System.out.println(EWrapperMsgGenerator.updateMktDepth(tickerId, position, operation, side, price, size));
+ }
+
+ @Override public void updateMktDepthL2(int tickerId, int position, String marketMaker, int operation, int side, double price, int size) {
+ System.out.println(EWrapperMsgGenerator.updateMktDepthL2( tickerId, position, marketMaker, operation, side, price, size));
+ }
+
+ @Override public void updateNewsBulletin(int msgId, int msgType, String message, String origExchange) {
+ System.out.println(EWrapperMsgGenerator.updateNewsBulletin( msgId, msgType, message, origExchange));
+ }
+
+ @Override public void managedAccounts(String accountsList) {
+ System.out.println(EWrapperMsgGenerator.managedAccounts( accountsList));
+ }
+
+ @Override public void receiveFA(int faDataType, String xml) {
+ System.out.println(EWrapperMsgGenerator.receiveFA( faDataType, xml));
+ }
+
+ @Override public void historicalData(int reqId, Bar bar) {
+ System.out.println(EWrapperMsgGenerator.historicalData( reqId, bar.time(), bar.open(), bar.high(), bar.low(), bar.close(), bar.volume(), bar.count(), bar.wap()));
+ }
+
+ @Override public void scannerParameters(String xml) {
+ System.out.println(EWrapperMsgGenerator.scannerParameters(xml));
+ }
+
+ @Override public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr) {
+ System.out.println(EWrapperMsgGenerator.scannerData( reqId, rank, contractDetails, distance, benchmark, projection, legsStr));
+ }
+
+ @Override public void scannerDataEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.scannerDataEnd(reqId));
+ }
+
+ @Override public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) {
+ System.out.println(EWrapperMsgGenerator.realtimeBar( reqId, time, open, high, low, close, volume, wap, count));
+ }
+
+ @Override public void currentTime(long time) {
+ System.out.println(EWrapperMsgGenerator.currentTime( time));
+ }
+
+ @Override public void fundamentalData(int reqId, String data) {
+ System.out.println(EWrapperMsgGenerator.fundamentalData( reqId, data));
+ }
+
+ @Override public void deltaNeutralValidation(int reqId, DeltaNeutralContract deltaNeutralContract) {
+ System.out.println(EWrapperMsgGenerator.deltaNeutralValidation( reqId, deltaNeutralContract));
+ }
+
+ @Override public void tickSnapshotEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.tickSnapshotEnd( reqId));
+ }
+
+ @Override public void marketDataType(int reqId, int marketDataType) {
+ System.out.println(EWrapperMsgGenerator.marketDataType( reqId, marketDataType));
+ }
+
+ @Override public void commissionReport(CommissionReport commissionReport) {
+ System.out.println(EWrapperMsgGenerator.commissionReport( commissionReport));
+ }
+
+ @Override public void position(String account, Contract contract, double pos, double avgCost) {
+ System.out.println(EWrapperMsgGenerator.position( account, contract, pos, avgCost));
+ }
+
+ @Override public void positionEnd() {
+ System.out.println(EWrapperMsgGenerator.positionEnd());
+ }
+
+ @Override public void accountSummary(int reqId, String account, String tag, String value, String currency) {
+ System.out.println(EWrapperMsgGenerator.accountSummary( reqId, account, tag, value, currency));
+ }
+
+ @Override public void accountSummaryEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.accountSummaryEnd( reqId));
+ }
+
+ @Override public void verifyMessageAPI( String apiData) {
+ }
+
+ @Override public void verifyCompleted( boolean isSuccessful, String errorText){
+ }
+
+ @Override public void verifyAndAuthMessageAPI( String apiData, String xyzChallenge) {
+ }
+
+ @Override public void verifyAndAuthCompleted( boolean isSuccessful, String errorText){
+ }
+
+ @Override public void displayGroupList( int reqId, String groups){
+ }
+
+ @Override public void displayGroupUpdated( int reqId, String contractInfo){
+ }
+
+ @Override public void positionMulti( int reqId, String account, String modelCode, Contract contract, double pos, double avgCost) {
+ System.out.println(EWrapperMsgGenerator.positionMulti( reqId, account, modelCode, contract, pos, avgCost));
+ }
+
+ @Override public void positionMultiEnd( int reqId) {
+ System.out.println(EWrapperMsgGenerator.positionMultiEnd( reqId));
+ }
+
+ @Override public void accountUpdateMulti( int reqId, String account, String modelCode, String key, String value, String currency) {
+ System.out.println(EWrapperMsgGenerator.accountUpdateMulti( reqId, account, modelCode, key, value, currency));
+ }
+
+ @Override public void accountUpdateMultiEnd( int reqId) {
+ System.out.println(EWrapperMsgGenerator.accountUpdateMultiEnd( reqId));
+ }
+
+ public void connectAck() {
+ }
+
+ @Override
+ public void securityDefinitionOptionalParameter(int reqId, String exchange, int underlyingConId, String tradingClass,
+ String multiplier, Set<String> expirations, Set<Double> strikes) {
+ System.out.println(EWrapperMsgGenerator.securityDefinitionOptionalParameter( reqId, exchange, underlyingConId, tradingClass, multiplier, expirations, strikes));
+ }
+
+ @Override
+ public void securityDefinitionOptionalParameterEnd(int reqId) {
+ System.out.println(EWrapperMsgGenerator.securityDefinitionOptionalParameterEnd( reqId));
+ }
+
+ @Override
+ public void softDollarTiers(int reqId, SoftDollarTier[] tiers) {
+ System.out.println(EWrapperMsgGenerator.softDollarTiers( reqId,tiers));
+ }
+
+ @Override
+ public void familyCodes(FamilyCode[] familyCodes) {
+ System.out.println(EWrapperMsgGenerator.familyCodes(familyCodes));
+ }
+
+ @Override
+ public void symbolSamples(int reqId, ContractDescription[] contractDescriptions) {
+ System.out.println(EWrapperMsgGenerator.symbolSamples( reqId, contractDescriptions));
+ }
+
+ @Override
+ public void historicalDataEnd(int reqId, String startDateStr, String endDateStr) {
+ System.out.println(EWrapperMsgGenerator.historicalDataEnd( reqId, startDateStr, endDateStr));
+ }
+
+ @Override
+ public void mktDepthExchanges(DepthMktDataDescription[] depthMktDataDescriptions) {
+ System.out.println(EWrapperMsgGenerator.mktDepthExchanges(depthMktDataDescriptions));
+ }
+
+ @Override
+ public void tickNews(int tickerId, long timeStamp, String providerCode, String articleId, String headline,
+ String extraData) {
+ System.out.println(EWrapperMsgGenerator.tickNews(tickerId, timeStamp, providerCode, articleId, headline, extraData));
+ }
+
+ @Override
+ public void smartComponents(int reqId, Map<Integer, Entry<String, Character>> theMap) {
+ System.out.println(EWrapperMsgGenerator.smartComponents(reqId, theMap));
+ }
+
+ @Override
+ public void tickReqParams(int tickerId, double minTick, String bboExchange, int snapshotPermissions) {
+ System.out.println(EWrapperMsgGenerator.tickReqParams(tickerId, minTick, bboExchange, snapshotPermissions));
+ }
+
+ @Override
+ public void newsProviders(NewsProvider[] newsProviders) {
+ System.out.println(EWrapperMsgGenerator.newsProviders(newsProviders));
+ }
+
+ @Override
+ public void newsArticle(int requestId, int articleType, String articleText) {
+ System.out.println(EWrapperMsgGenerator.newsArticle(requestId, articleType, articleText));
+ }
+
+ @Override
+ public void historicalNews(int requestId, String time, String providerCode, String articleId, String headline) {
+ System.out.println(EWrapperMsgGenerator.historicalNews(requestId, time, providerCode, articleId, headline));
+ }
+
+ @Override
+ public void historicalNewsEnd(int requestId, boolean hasMore) {
+ System.out.println(EWrapperMsgGenerator.historicalNewsEnd(requestId, hasMore));
+ }
+
+ @Override
+ public void headTimestamp(int reqId, String headTimestamp) {
+ System.out.println(EWrapperMsgGenerator.headTimestamp(reqId, headTimestamp));
+ }
+
+ @Override
+ public void histogramData(int reqId, List<HistogramEntry> items) {
+ System.out.println(EWrapperMsgGenerator.histogramData(reqId, items));
+ }
+
+ @Override
+ public void historicalDataUpdate(int reqId, Bar bar) {
+ historicalData(reqId, bar);
+ }
+
+ @Override
+ public void rerouteMktDataReq(int reqId, int conId, String exchange) {
+ System.out.println(EWrapperMsgGenerator.rerouteMktDataReq(reqId, conId, exchange));
+ }
+
+ @Override
+ public void rerouteMktDepthReq(int reqId, int conId, String exchange) {
+ System.out.println(EWrapperMsgGenerator.rerouteMktDepthReq(reqId, conId, exchange));
+ }
+
+ @Override
+ public void marketRule(int marketRuleId, PriceIncrement[] priceIncrements) {
+ System.out.println(EWrapperMsgGenerator.marketRule(marketRuleId, priceIncrements));
+ }
+
+ @Override
+ public void pnl(int reqId, double dailyPnL, double unrealizedPnL, double realizedPnL) {
+ System.out.println(EWrapperMsgGenerator.pnl(reqId, dailyPnL, unrealizedPnL, realizedPnL));
+ }
+
+ @Override
+ public void pnlSingle(int reqId, int pos, double dailyPnL, double unrealizedPnL, double realizedPnL, double value) {
+ System.out.println(EWrapperMsgGenerator.pnlSingle(reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value));
+ }
+
+ @Override
+ public void historicalTicks(int reqId, List<HistoricalTick> ticks, boolean done) {
+ for (HistoricalTick tick : ticks) {
+ System.out.println(EWrapperMsgGenerator.historicalTick(reqId, tick.time(), tick.price(), tick.size()));
+ }
+ }
+
+ @Override
+ public void historicalTicksBidAsk(int reqId, List<HistoricalTickBidAsk> ticks, boolean done) {
+ for (HistoricalTickBidAsk tick : ticks) {
+ System.out.println(EWrapperMsgGenerator.historicalTickBidAsk(reqId, tick.time(), tick.mask(), tick.priceBid(), tick.priceAsk(), tick.sizeBid(),
+ tick.sizeAsk()));
+ }
+ }
+
+ @Override
+ public void historicalTicksLast(int reqId, List<HistoricalTickLast> ticks, boolean done) {
+ for (HistoricalTickLast tick : ticks) {
+ System.out.println(EWrapperMsgGenerator.historicalTickLast(reqId, tick.time(), tick.mask(), tick.price(), tick.size(), tick.exchange(),
+ tick.specialConditions()));
+ }
+ }
+
+ @Override
+ public void tickByTickAllLast(int reqId, int tickType, long time, double price, int size, TickAttr attribs,
+ String exchange, String specialConditions) {
+ System.out.println(EWrapperMsgGenerator.tickByTickAllLast(reqId, tickType, time, price, size, attribs, exchange, specialConditions));
+ }
+
+ @Override
+ public void tickByTickBidAsk(int reqId, long time, double bidPrice, double askPrice, int bidSize, int askSize,
+ TickAttr attribs) {
+ System.out.println(EWrapperMsgGenerator.tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize, askSize, attribs));
+ }
+
+ @Override
+ public void tickByTickMidPoint(int reqId, long time, double midPoint) {
+ System.out.println(EWrapperMsgGenerator.tickByTickMidPoint(reqId, time, midPoint));
+ }
+}
diff --git a/demo/src/main/java/apidemo/TickByTickModel.java b/demo/src/main/java/apidemo/TickByTickModel.java
new file mode 100755
index 0000000..b9be4bc
--- /dev/null
+++ b/demo/src/main/java/apidemo/TickByTickModel.java
@@ -0,0 +1,125 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.TickByTick;
+import com.ib.client.Util;
+import com.ib.client.Types.TickByTickType;
+
+class TickByTickModel extends AbstractTableModel {
+
+ private List<TickByTick> m_rows;
+ private TickByTickType m_tickType;
+
+ public TickByTickModel(List<TickByTick> rows, TickByTickType tickType) {
+ m_rows = rows;
+ m_tickType = tickType;
+ }
+
+ @Override
+ public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ int columnCount = 0;
+ switch (m_tickType) {
+ case None:
+ break;
+ case Last:
+ case AllLast:
+ columnCount = 6;
+ break;
+ case BidAsk:
+ columnCount = 6;
+ break;
+ case MidPoint:
+ columnCount = 2;
+ break;
+ }
+ return columnCount;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ TickByTick row = m_rows.get(rowIndex);
+
+ switch (m_tickType) {
+ case None:
+ break;
+ case Last:
+ case AllLast:
+ switch (columnIndex) {
+ case 0: return Util.UnixSecondsToString(row.time(), "yyyyMMdd-HH:mm:ss zzz");
+ case 1: return row.price();
+ case 2: return row.size();
+ case 3: return row.attribsStr();
+ case 4: return row.exchange();
+ case 5: return row.specialConditions();
+ }
+ break;
+ case BidAsk:
+ switch (columnIndex) {
+ case 0: return Util.UnixSecondsToString(row.time(), "yyyyMMdd-HH:mm:ss zzz");
+ case 1: return row.bidPrice();
+ case 2: return row.bidSize();
+ case 3: return row.askPrice();
+ case 4: return row.askSize();
+ case 5: return row.attribsStr();
+ }
+ break;
+ case MidPoint:
+ switch (columnIndex) {
+ case 0: return Util.UnixSecondsToString(row.time(), "yyyyMMdd-HH:mm:ss zzz");
+ case 1: return row.midPoint();
+ }
+ break;
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (m_tickType) {
+ case None:
+ break;
+ case Last:
+ case AllLast:
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Price";
+ case 2: return "Size";
+ case 3: return "Attribs";
+ case 4: return "Exchange";
+ case 5: return "Spec Cond";
+ }
+ break;
+ case BidAsk:
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Bid Price";
+ case 2: return "Bid Size";
+ case 3: return "Ask Price";
+ case 4: return "Ask Size";
+ case 5: return "Attribs";
+ }
+ break;
+ case MidPoint:
+ switch (column) {
+ case 0: return "Time";
+ case 1: return "Mid Point";
+ }
+ break;
+ }
+
+ return super.getColumnName(column);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/TickByTickResultsPanel.java b/demo/src/main/java/apidemo/TickByTickResultsPanel.java
new file mode 100755
index 0000000..c72b896
--- /dev/null
+++ b/demo/src/main/java/apidemo/TickByTickResultsPanel.java
@@ -0,0 +1,134 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.ib.client.BitMask;
+import com.ib.client.HistoricalTick;
+import com.ib.client.HistoricalTickBidAsk;
+import com.ib.client.HistoricalTickLast;
+import com.ib.client.TickAttr;
+import com.ib.client.TickByTick;
+import com.ib.client.Types.TickByTickType;
+import com.ib.controller.ApiController.ITickByTickDataHandler;
+
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+class TickByTickResultsPanel extends NewTabPanel implements ITickByTickDataHandler {
+
+ private JTable m_table;
+ private final List<TickByTick> m_tickByTickRows = new ArrayList<>();
+ private final TickByTickModel m_tickModel;
+
+ TickByTickResultsPanel(TickByTickType tickType) {
+ m_tickModel = new TickByTickModel(m_tickByTickRows, tickType);
+ m_table = new JTable(m_tickModel);
+ JScrollPane scroll = new JScrollPane(m_table) {
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+
+ d.width = 500;
+
+ return d;
+ }
+ };
+
+ setLayout(new GridLayout());
+ StackPanel hTicksPanel = new StackPanel();
+
+ hTicksPanel.add(new JLabel("Tick-By-Tick: " + tickType.toString()));
+ hTicksPanel.add(scroll, BorderLayout.WEST);
+ add(hTicksPanel);
+ }
+
+ @Override
+ public void tickByTickAllLast(int reqId, int tickType, long time, double price, int size, TickAttr attribs,
+ String exchange, String specialConditions) {
+ TickByTick tick = new TickByTick(tickType, time, price, size, attribs, exchange, specialConditions);
+ m_tickByTickRows.add(tick);
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void tickByTickBidAsk(int reqId, long time, double bidPrice, double askPrice, int bidSize, int askSize,
+ TickAttr attribs) {
+ TickByTick tick = new TickByTick(time, bidPrice, bidSize, askPrice, askSize, attribs);
+ m_tickByTickRows.add(tick);
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void tickByTickMidPoint(int reqId, long time, double midPoint) {
+ TickByTick tick = new TickByTick(time, midPoint);
+ m_tickByTickRows.add(tick);
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void activated() { }
+
+ @Override
+ public void closed() {
+ ApiDemo.INSTANCE.controller().cancelTickByTickData( this);
+ }
+
+ @Override
+ public void tickByTickHistoricalTickAllLast(int reqId, List<HistoricalTickLast> ticks) {
+ for (HistoricalTickLast tick : ticks) {
+ BitMask bitMask = new BitMask(tick.mask());
+ TickAttr attribs = new TickAttr();
+ attribs.pastLimit(bitMask.get(0));
+ attribs.unreported(bitMask.get(1));
+
+ TickByTick tickByTick = new TickByTick(2, tick.time(), tick.price(), tick.size(), attribs, tick.exchange(), tick.specialConditions());
+ m_tickByTickRows.add(tickByTick);
+ }
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void tickByTickHistoricalTickBidAsk(int reqId, List<HistoricalTickBidAsk> ticks) {
+ for (HistoricalTickBidAsk tick : ticks) {
+ BitMask bitMask = new BitMask(tick.mask());
+ TickAttr attribs = new TickAttr();
+ attribs.bidPastLow(bitMask.get(0));
+ attribs.askPastHigh(bitMask.get(1));
+
+ TickByTick tickByTick = new TickByTick(tick.time(), tick.priceBid(), tick.sizeBid(), tick.priceAsk(), tick.sizeAsk(), attribs);
+ m_tickByTickRows.add(tickByTick);
+ }
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+
+ @Override
+ public void tickByTickHistoricalTick(int reqId, List<HistoricalTick> ticks) {
+ for (HistoricalTick tick : ticks) {
+ TickByTick tickByTick = new TickByTick(tick.time(), tick.price());
+ m_tickByTickRows.add(tickByTick);
+ }
+
+ m_table.setModel(m_tickModel);
+ m_tickModel.fireTableDataChanged();
+ }
+} \ No newline at end of file
diff --git a/demo/src/main/java/apidemo/TicketDlg.java b/demo/src/main/java/apidemo/TicketDlg.java
new file mode 100755
index 0000000..3a38fcb
--- /dev/null
+++ b/demo/src/main/java/apidemo/TicketDlg.java
@@ -0,0 +1,730 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import static com.ib.client.Util.lookupContract;
+import static com.ib.controller.Formats.fmt;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+
+import javax.swing.Box;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+
+import com.ib.client.Contract;
+import com.ib.client.Order;
+import com.ib.client.OrderState;
+import com.ib.client.OrderStatus;
+import com.ib.client.OrderType;
+import com.ib.client.SoftDollarTier;
+import com.ib.client.TagValue;
+import com.ib.client.Types.Action;
+import com.ib.client.Types.AlgoParam;
+import com.ib.client.Types.AlgoStrategy;
+import com.ib.client.Types.ComboParam;
+import com.ib.client.Types.HedgeType;
+import com.ib.client.Types.Method;
+import com.ib.client.Types.OcaType;
+import com.ib.client.Types.ReferencePriceType;
+import com.ib.client.Types.Rule80A;
+import com.ib.client.Types.TimeInForce;
+import com.ib.client.Types.TriggerMethod;
+import com.ib.client.Types.VolatilityType;
+import com.ib.controller.ApiController.IOrderHandler;
+
+import apidemo.util.HtmlButton;
+import apidemo.util.NewTabbedPanel;
+import apidemo.util.TCombo;
+import apidemo.util.UpperField;
+import apidemo.util.Util;
+import apidemo.util.VerticalPanel;
+import apidemo.util.VerticalPanel.HorzPanel;
+import apidemo.util.VerticalPanel.StackPanel;
+
+class TicketDlg extends JDialog {
+ private boolean m_editContract;
+ private final Contract m_contract;
+ private final Order m_order;
+ private final ContractPanel m_contractPanel;
+ private final OrderPanel m_orderPanel;
+ private final AdvisorTicketPanel m_advisorPanel;
+ private final MiscTicketPanel m_attribTicketPanel;
+ private final VolatilityTicketPanel m_volPanel;
+ private final ComboTicketPanel m_comboPanel;
+ private final AlgoPanel m_algoPanel;
+ private final ScalePanel m_scalePanel;
+ private final PegBenchPanel m_pegBenchPanel;
+ private final AdjustedPanel m_adjustedPanel;
+ private final ConditionsPanel m_conditionPanel;
+
+ TicketDlg(Contract contract, Order order) {
+ super( ApiDemo.INSTANCE.frame());
+
+ if (contract == null) {
+ contract = new Contract();
+ m_editContract = true;
+ }
+
+ if (order == null) {
+ order = new Order();
+ order.totalQuantity( 100);
+ order.lmtPrice( 1);
+ }
+
+ m_contract = contract;
+ m_order = order;
+
+ m_contractPanel = new ContractPanel( m_contract);
+ m_pegBenchPanel = new PegBenchPanel(this, m_order,
+ c -> lookupContract(ApiDemo.INSTANCE.controller(), c));
+ m_advisorPanel = new AdvisorTicketPanel();
+ m_attribTicketPanel = new MiscTicketPanel();
+ m_volPanel = new VolatilityTicketPanel();
+ m_comboPanel = new ComboTicketPanel();
+ m_algoPanel = new AlgoPanel();
+ m_scalePanel = new ScalePanel();
+ m_orderPanel = new OrderPanel();
+ m_adjustedPanel = new AdjustedPanel(this, m_order);
+ m_conditionPanel = new ConditionsPanel(this, m_order,
+ c -> lookupContract(ApiDemo.INSTANCE.controller(), c));
+
+ HtmlButton transmitOrder = new HtmlButton( "Transmit Order") {
+ @Override public void actionPerformed() {
+ onTransmitOrder();
+ }
+ };
+
+ HtmlButton checkMargin = new HtmlButton( "Check Margin") {
+ @Override public void actionPerformed() {
+ onCheckMargin();
+ }
+ };
+
+ HtmlButton close = new HtmlButton( "Close") {
+ @Override public void actionPerformed() {
+ dispose();
+ }
+ };
+
+ NewTabbedPanel tabbedPanel = new NewTabbedPanel(true);
+ if (m_editContract) {
+ tabbedPanel.addTab( "Contract", m_contractPanel);
+ }
+ tabbedPanel.addTab( "Order", m_orderPanel);
+ tabbedPanel.addTab("Pegged to benchmark", m_pegBenchPanel);
+ tabbedPanel.addTab("Adjustable stops", m_adjustedPanel);
+ tabbedPanel.addTab( "Misc", m_attribTicketPanel);
+ tabbedPanel.addTab( "Advisor", m_advisorPanel);
+ tabbedPanel.addTab( "Volatility", m_volPanel);
+ if (m_contract.isCombo() ) {
+ tabbedPanel.addTab( "Combo", m_comboPanel);
+ }
+ tabbedPanel.addTab( "Scale", m_scalePanel);
+ tabbedPanel.addTab( "IB Algo", m_algoPanel);
+ tabbedPanel.addTab("Conditions", m_conditionPanel);
+
+ JPanel buts = new JPanel( new FlowLayout( FlowLayout.CENTER, 15, 5));
+ buts.add( transmitOrder);
+ buts.add( checkMargin);
+ buts.add( close);
+
+ // check-margin is for new orders only
+ if (m_order.orderId() != 0) {
+ checkMargin.setVisible( false);
+ }
+
+ add( tabbedPanel);
+ add( buts, BorderLayout.SOUTH);
+
+ setLocation(200, 200);
+ pack();
+ Util.closeOnEsc( this);
+ }
+
+ private void onTransmitOrder() {
+ scrape();
+
+ // close window right away for mods
+ if (m_order.orderId() != 0) {
+ dispose();
+ }
+
+ ApiDemo.INSTANCE.controller().placeOrModifyOrder( m_contract, m_order, new IOrderHandler() {
+ @Override public void orderState(OrderState orderState) {
+ ApiDemo.INSTANCE.controller().removeOrderHandler( this);
+ SwingUtilities.invokeLater(() -> dispose());
+ }
+ @Override public void orderStatus(OrderStatus status, double filled, double remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
+ }
+ @Override public void handle(int errorCode, final String errorMsg) {
+ m_order.orderId( 0);
+ SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog( TicketDlg.this, errorMsg));
+ }
+ });
+ }
+
+ private void onCheckMargin() {
+ scrape();
+
+ m_order.whatIf( true);
+ ApiDemo.INSTANCE.controller().placeOrModifyOrder( m_contract, m_order, new IOrderHandler() {
+ @Override public void orderState(final OrderState orderState) {
+ SwingUtilities.invokeLater(() -> displayMargin( orderState));
+ }
+ @Override public void handle(int errorCode, final String errorMsg) {
+ SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog( TicketDlg.this, errorMsg));
+ }
+ @Override public void orderStatus(OrderStatus status, double filled, double remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
+ }
+ });
+
+ m_order.whatIf( false);
+ m_order.orderId( 0);
+ }
+
+ private void displayMargin(OrderState orderState) {
+ String str = String.format( "Current:%nEquity with loan: %s%nInitial margin: %s%nMaintenance margin: %s%n%n"
+ + "Change:%nEquity with loan: %s%nInitial margin: %s%nMaintenance margin: %s%n%n"
+ + "Post-Trade:%nEquity with loan: %s%nInitial margin: %s%nMaintenance margin: %s%n%n",
+ orderState.equityWithLoanBefore() != null ? fmt(Double.parseDouble(orderState.equityWithLoanBefore())) : "",
+ orderState.initMarginBefore() != null ? fmt(Double.parseDouble(orderState.initMarginBefore())) : "",
+ orderState.maintMarginBefore() != null ? fmt(Double.parseDouble(orderState.maintMarginBefore())) : "",
+ orderState.equityWithLoanChange() != null ? fmt(Double.parseDouble(orderState.equityWithLoanChange())) : "",
+ orderState.initMarginChange() != null ? fmt(Double.parseDouble(orderState.initMarginChange())) : "",
+ orderState.maintMarginChange() != null ? fmt(Double.parseDouble(orderState.maintMarginChange())) : "",
+ fmt( Double.parseDouble(orderState.equityWithLoanAfter() ) ),
+ fmt( Double.parseDouble( orderState.initMarginAfter() ) ),
+ fmt( Double.parseDouble(orderState.maintMarginAfter() ) )
+ );
+
+ JOptionPane.showMessageDialog( this, str, "Margin Requirements", JOptionPane.INFORMATION_MESSAGE);
+ }
+
+ private void scrape() {
+ m_order.smartComboRoutingParams().clear();
+
+ if (m_editContract) {
+ m_contractPanel.onOK();
+ }
+ m_orderPanel.onOK();
+ m_advisorPanel.onOK();
+ m_attribTicketPanel.onOK();
+ m_volPanel.onOK();
+ m_algoPanel.onOK();
+ m_scalePanel.onOK();
+ if (m_contract.isCombo() ) {
+ m_comboPanel.onOK();
+ }
+ m_pegBenchPanel.onOK();
+ m_adjustedPanel.onOK();
+ m_conditionPanel.onOK();
+ }
+
+ enum AmntUnit {
+ Amnt("amnt", 0),
+ Percent("%", 100);
+
+ String m_text;
+ int m_val;
+
+ AmntUnit(String txt, int v) {
+ m_text = txt;
+ m_val = v;
+ }
+
+ public static AmntUnit fromInt(int i) {
+ for (AmntUnit v : AmntUnit.values())
+ if (v.m_val == i)
+ return v;
+
+ return Amnt;
+ }
+
+ @Override
+ public String toString() {
+ return m_text;
+ }
+ }
+
+ class OrderPanel extends VerticalPanel {
+ final TCombo<String> m_account = new TCombo<>( ApiDemo.INSTANCE.accountList().toArray(new String[0]) );
+ final TCombo<Action> m_action = new TCombo<>( Action.values() );
+ final JTextField m_modelCode = new JTextField();
+ final UpperField m_quantity = new UpperField( "100");
+ final UpperField m_cashQty = new UpperField();
+ final UpperField m_displaySize = new UpperField();
+ final TCombo<OrderType> m_orderType = new TCombo<>( OrderType.values() );
+ final UpperField m_lmtPrice = new UpperField( "200");
+ final UpperField m_auxPrice = new UpperField();
+ final TCombo<TimeInForce> m_tif = new TCombo<>( TimeInForce.values() );
+ final JCheckBox m_nonGuaranteed = new JCheckBox();
+ final UpperField m_lmtPriceOffset = new UpperField();
+ final UpperField m_triggerPrice = new UpperField();
+ final UpperField m_mifid2DecisionMaker = new UpperField();
+ final UpperField m_mifid2DecisionAlgo = new UpperField();
+ final UpperField m_mifid2ExecutionTrader = new UpperField();
+ final UpperField m_mifid2ExecutionAlgo = new UpperField();
+
+ OrderPanel() {
+ m_orderType.removeItemAt( 0); // remove None
+
+ m_account.setSelectedItem( m_order.account() != null ? m_order.account() : ApiDemo.INSTANCE.accountList().get( 0) );
+ m_modelCode.setText( m_order.modelCode() );
+ m_action.setSelectedItem( m_order.action() );
+ m_quantity.setText( m_order.totalQuantity());
+ m_cashQty.setText( m_order.cashQty());
+ m_displaySize.setText( m_order.displaySize());
+ m_orderType.setSelectedItem( m_order.orderType() );
+ m_lmtPrice.setText( m_order.lmtPrice());
+ m_auxPrice.setText( m_order.auxPrice());
+ m_tif.setSelectedItem( m_order.tif());
+ m_nonGuaranteed.setSelected( getVal( ComboParam.NonGuaranteed).equals( "1") );
+ m_lmtPriceOffset.setText(m_order.lmtPriceOffset());
+ m_triggerPrice.setText(m_order.triggerPrice());
+ m_mifid2DecisionMaker.setText(m_order.mifid2DecisionMaker());
+ m_mifid2DecisionAlgo.setText(m_order.mifid2DecisionAlgo());
+ m_mifid2ExecutionTrader.setText(m_order.mifid2ExecutionTrader());
+ m_mifid2ExecutionAlgo.setText(m_order.mifid2ExecutionAlgo());
+
+ add("Account", m_account);
+
+ m_modelCode.setColumns(7);
+
+ add("Model code", m_modelCode);
+ add("Action", m_action);
+ add("Quantity", m_quantity);
+ add("Cash Qty", m_cashQty);
+ add("Display size", m_displaySize);
+ add("Order type", m_orderType);
+ add("Limit price", m_lmtPrice);
+ add("Limit price offset", m_lmtPriceOffset);
+ add("Trigger price", m_triggerPrice);
+ add("Aux price", m_auxPrice);
+ add("Time-in-force", m_tif);
+ add("MiFID II Decision Maker", m_mifid2DecisionMaker);
+ add("MiFID II Decision Algo", m_mifid2DecisionAlgo);
+ add("MiFID II Execution Trader", m_mifid2ExecutionTrader);
+ add("MiFID II Execution Algo", m_mifid2ExecutionAlgo);
+
+ if (m_contract.isCombo() ) {
+ add( "Non-guaranteed", m_nonGuaranteed);
+ }
+ }
+
+ private void onOK() {
+ m_order.account( m_account.getText().toUpperCase() );
+ m_order.modelCode( m_modelCode.getText().trim() );
+ m_order.action( m_action.getSelectedItem() );
+ m_order.totalQuantity( m_quantity.getDouble() );
+ m_order.cashQty( m_cashQty.getDouble() );
+ m_order.displaySize( m_displaySize.getInt() );
+ m_order.orderType( m_orderType.getSelectedItem() );
+ m_order.lmtPrice( m_lmtPrice.getDouble() );
+ m_order.auxPrice( m_auxPrice.getDouble() );
+ m_order.tif( m_tif.getSelectedItem() );
+ m_order.lmtPriceOffset(m_lmtPriceOffset.getDouble());
+ m_order.triggerPrice(m_triggerPrice.getDouble());
+ m_order.mifid2DecisionMaker(m_mifid2DecisionMaker.getText());
+ m_order.mifid2DecisionAlgo(m_mifid2DecisionAlgo.getText());
+ m_order.mifid2ExecutionTrader(m_mifid2ExecutionTrader.getText());
+ m_order.mifid2ExecutionAlgo(m_mifid2ExecutionAlgo.getText());
+
+ if (m_contract.isCombo() ) {
+ TagValue tv = new TagValue( ComboParam.NonGuaranteed.toString(), m_nonGuaranteed.isSelected() ? "1" : "0");
+ m_order.smartComboRoutingParams().add( tv);
+ }
+ }
+ }
+
+ class AdvisorTicketPanel extends VerticalPanel {
+ final UpperField m_faGroup = new UpperField();
+ final TCombo<Method> m_faMethod = new TCombo<>( Method.values() );
+ final UpperField m_faPercentage = new UpperField();
+ final UpperField m_faProfile = new UpperField();
+
+ AdvisorTicketPanel() {
+ m_faGroup.setText( m_order.faGroup() );
+ m_faMethod.setSelectedItem( m_order.faMethod() );
+ m_faPercentage.setText( m_order.faPercentage() );
+ m_faProfile.setText( m_order.faProfile() );
+
+ add( "Group", m_faGroup);
+ add( "Method", m_faMethod);
+ add( "Percentage", m_faPercentage);
+ add( Box.createVerticalStrut(10));
+ add( "--or--");
+ add( Box.createVerticalStrut(10));
+ add( "Profile", m_faProfile);
+ }
+
+ void onOK() {
+ m_order.faGroup( m_faGroup.getText() );
+ m_order.faMethod( m_faMethod.getSelectedItem() );
+ m_order.faPercentage( m_faPercentage.getText() );
+ m_order.faProfile( m_faProfile.getText() );
+ }
+ }
+
+ class MiscTicketPanel extends StackPanel {
+ final UpperField m_goodAfter = new UpperField();
+ final UpperField m_goodTil = new UpperField();
+ final JTextField m_orderRef = new JTextField(7);
+ final JTextField m_ocaGroup = new JTextField(7);
+ final UpperField m_minQty = new UpperField();
+ final UpperField m_percentOffset = new UpperField();
+ final UpperField m_trailingStopPrice = new UpperField();
+ final UpperField m_trailingPercent = new UpperField();
+ final UpperField m_discretionaryAmt = new UpperField();
+ final UpperField m_nbboPriceCap = new UpperField();
+ final UpperField m_algoId = new UpperField();
+ final UpperField m_extOperator = new UpperField();
+ final TCombo<SoftDollarTier> m_softDollarTiers = new TCombo<>();
+
+ final TCombo<OcaType> m_ocaType = new TCombo<>( OcaType.values() );
+ final TCombo<Rule80A> m_rule80A = new TCombo<>( Rule80A.values() );
+ final TCombo<TriggerMethod> m_trigger = new TCombo<>( TriggerMethod.values() );
+
+ final TCombo<HedgeType> m_hedgeType = new TCombo<>( HedgeType.values() );
+ final UpperField m_hedgeParam = new UpperField();
+
+ final JCheckBox m_blockOrder = new JCheckBox();
+ final JCheckBox m_sweepToFill = new JCheckBox();
+ final JCheckBox m_hidden = new JCheckBox();
+ final JCheckBox m_outsideRth = new JCheckBox();
+ final JCheckBox m_allOrNone = new JCheckBox();
+ final JCheckBox m_overrideConstraints = new JCheckBox();
+ final JCheckBox m_notHeld = new JCheckBox();
+ final JCheckBox m_transmit = new JCheckBox();
+ final JCheckBox m_eTradeOnly = new JCheckBox();
+ final JCheckBox m_firmQuoteOnly = new JCheckBox();
+ final JCheckBox m_optOutSmartRouting = new JCheckBox();
+ final JCheckBox m_dontUseAutoPriceForHedge = new JCheckBox();
+
+
+
+ MiscTicketPanel() {
+ VerticalPanel top = new VerticalPanel();
+ top.add( "Order ref", m_orderRef);
+ top.add( "Min Qty", m_minQty);
+ top.add( "Good after", m_goodAfter);
+ top.add( "Good until", m_goodTil);
+ top.add( "Rule 80A", m_rule80A);
+ top.add( "Trigger method", m_trigger);
+ top.add( "Percent Offset", m_percentOffset);
+ top.add( "Trail order stop price", m_trailingStopPrice);
+ top.add( "Trailing percent", m_trailingPercent);
+ top.add("Discretionary amount", m_discretionaryAmt);
+ top.add("NBBO price cap", m_nbboPriceCap);
+ top.add( "Algo Id", m_algoId);
+ top.add( "OCA group and type", m_ocaGroup, m_ocaType);
+ top.add( "Hedge type and param" , m_hedgeType, m_hedgeParam);
+ top.add("Ext operator", m_extOperator);
+ top.add("Soft dollar tier", m_softDollarTiers);
+
+ VerticalPanel left = new VerticalPanel();
+ left.add( "Not held", m_notHeld);
+ left.add( "Block order", m_blockOrder);
+ left.add( "Sweep-to-fill", m_sweepToFill);
+ left.add( "Hidden", m_hidden);
+ left.add( "Fill outside RTH", m_outsideRth);
+ left.add( "All-or-none", m_allOrNone);
+
+ VerticalPanel right = new VerticalPanel();
+ right.add( "Override constraints", m_overrideConstraints);
+ right.add( "E-trade only", m_eTradeOnly);
+ right.add( "Firm quote only", m_firmQuoteOnly);
+ right.add( "Opt out SMART routing", m_optOutSmartRouting);
+ right.add( "Don't use auto price for hedge", m_dontUseAutoPriceForHedge);
+ right.add( "Transmit", m_transmit);
+
+ HorzPanel checks = new HorzPanel();
+ checks.add( left);
+ checks.add( Box.createHorizontalGlue());
+ checks.add( right);
+ checks.add( Box.createHorizontalGlue());
+
+ add( top);
+ add( Box.createVerticalStrut(20));
+ add( checks);
+
+ m_minQty.setText( m_order.minQty() );
+ m_goodAfter.setText( m_order.goodAfterTime() );
+ m_goodTil.setText( m_order.goodTillDate() );
+ m_orderRef.setText( m_order.orderRef() );
+ m_ocaGroup.setText( m_order.ocaGroup() );
+ m_ocaType.setSelectedItem( m_order.ocaType() );
+ m_rule80A.setSelectedItem( m_order.rule80A() );
+ m_trigger.setSelectedItem( m_order.triggerMethod() );
+ m_blockOrder.setSelected( m_order.blockOrder() );
+ m_sweepToFill.setSelected( m_order.sweepToFill() );
+ m_hidden.setSelected( m_order.hidden() );
+ m_outsideRth.setSelected( m_order.outsideRth() );
+ m_allOrNone.setSelected( m_order.allOrNone() );
+ m_overrideConstraints.setSelected( m_order.overridePercentageConstraints() );
+ m_hedgeType.setSelectedItem( m_order.hedgeType() );
+ m_hedgeParam.setText( m_order.hedgeParam() );
+ m_notHeld.setSelected( m_order.notHeld() );
+ m_percentOffset.setText( m_order.percentOffset() );
+ m_trailingStopPrice.setText( m_order.trailStopPrice() );
+ m_trailingPercent.setText( m_order.trailingPercent() );
+ m_discretionaryAmt.setText( m_order.discretionaryAmt() );
+ m_eTradeOnly.setSelected( m_order.eTradeOnly() );
+ m_firmQuoteOnly.setSelected( m_order.firmQuoteOnly() );
+ m_nbboPriceCap.setText( m_order.nbboPriceCap() );
+ m_optOutSmartRouting.setSelected( m_order.optOutSmartRouting() );
+ m_algoId.setText( m_order.algoId() );
+ m_transmit.setSelected( true);
+ m_extOperator.setText(m_order.extOperator());
+ m_softDollarTiers.removeAllItems();
+ m_dontUseAutoPriceForHedge.setSelected( m_order.dontUseAutoPriceForHedge());
+
+ ApiDemo.INSTANCE.controller().reqSoftDollarTiers(tiers -> {
+ m_softDollarTiers.invalidate();
+ m_softDollarTiers.removeAllItems();
+ m_softDollarTiers.addItem(new SoftDollarTier("", "", ""));
+
+ for (SoftDollarTier tier : tiers) {
+ m_softDollarTiers.addItem(tier);
+ }
+ });
+
+ }
+
+ void onOK() {
+ m_order.minQty( m_minQty.getInt() );
+ m_order.goodAfterTime( m_goodAfter.getText() );
+ m_order.goodTillDate( m_goodTil.getText() );
+ m_order.orderRef( m_orderRef.getText() );
+ m_order.ocaGroup( m_ocaGroup.getText() );
+ m_order.ocaType( m_ocaType.getSelectedItem() );
+ m_order.rule80A( m_rule80A.getSelectedItem() );
+ m_order.triggerMethod( m_trigger.getSelectedItem() );
+ m_order.sweepToFill( m_sweepToFill.isSelected() );
+ m_order.hidden( m_hidden.isSelected() );
+ m_order.outsideRth( m_outsideRth.isSelected() );
+ m_order.allOrNone( m_allOrNone.isSelected() );
+ m_order.overridePercentageConstraints( m_overrideConstraints.isSelected() );
+ m_order.hedgeType( m_hedgeType.getSelectedItem() );
+ m_order.hedgeParam( m_hedgeParam.getText() );
+ m_order.notHeld( m_notHeld.isSelected() );
+ m_order.percentOffset( m_percentOffset.getDouble() );
+ m_order.trailStopPrice( m_trailingStopPrice.getDouble() );
+ m_order.trailingPercent( m_trailingPercent.getDouble() );
+ m_order.discretionaryAmt( m_discretionaryAmt.getDouble() );
+ m_order.eTradeOnly( m_eTradeOnly.isSelected() );
+ m_order.firmQuoteOnly( m_firmQuoteOnly.isSelected() );
+ m_order.nbboPriceCap( m_nbboPriceCap.getDouble() );
+ m_order.optOutSmartRouting( m_optOutSmartRouting.isSelected() );
+ m_order.algoId( m_algoId.getText() );
+ m_order.transmit( m_transmit.isSelected() );
+ m_order.extOperator(m_extOperator .getText());
+ m_order.softDollarTier(m_softDollarTiers.getSelectedItem());
+ m_order.dontUseAutoPriceForHedge( m_dontUseAutoPriceForHedge.isSelected() );
+ }
+ }
+
+ class VolatilityTicketPanel extends VerticalPanel {
+ final UpperField m_volatility = new UpperField();
+ final TCombo<VolatilityType> m_volatilityType = new TCombo<>( VolatilityType.values() );
+ final JCheckBox m_continuousUpdate = new JCheckBox();
+ final TCombo<ReferencePriceType> m_referencePriceType = new TCombo<>(ReferencePriceType.values());
+ final TCombo<OrderType> m_deltaNeutralOrderType = new TCombo<>( OrderType.values() );
+ final UpperField m_deltaNeutralAuxPrice = new UpperField();
+ final UpperField m_deltaNeutralConId = new UpperField();
+ final UpperField m_upper = new UpperField();
+ final UpperField m_lower = new UpperField();
+
+ VolatilityTicketPanel() {
+ add( "Volatility", m_volatility, m_volatilityType);
+ add( "Continuously update price", m_continuousUpdate);
+ add( "Option reference price", m_referencePriceType);
+ add( "Hedge order type", m_deltaNeutralOrderType);
+ add( "Hedge order aux price", m_deltaNeutralAuxPrice);
+ add( "Hedge contract conid", m_deltaNeutralConId);
+ add( "Stock range - upper", m_upper);
+ add( "Stock range - lower", m_lower);
+
+ m_volatility.setText( m_order.volatility() );
+ m_volatilityType.setSelectedItem( m_order.volatilityType() );
+ m_continuousUpdate.setSelected( m_order.continuousUpdate() != 0 );
+ m_referencePriceType.setSelectedItem( m_order.referencePriceType() );
+ m_deltaNeutralOrderType.setSelectedItem( m_order.deltaNeutralOrderType() );
+ m_deltaNeutralAuxPrice.setText( m_order.deltaNeutralAuxPrice() );
+ m_deltaNeutralConId.setText( m_order.deltaNeutralConId() );
+ m_upper.setText( m_order.stockRangeUpper() );
+ m_lower.setText( m_order.stockRangeLower() );
+ }
+
+ void onOK() {
+ m_order.volatility( m_volatility.getDouble() );
+ m_order.volatilityType( m_volatilityType.getSelectedItem() );
+ m_order.continuousUpdate( m_continuousUpdate.isSelected() ? 1 : 0 );
+ m_order.referencePriceType( m_referencePriceType.getSelectedItem() );
+ m_order.deltaNeutralOrderType( m_deltaNeutralOrderType.getSelectedItem() );
+ m_order.deltaNeutralAuxPrice( m_deltaNeutralAuxPrice.getDouble() );
+ m_order.deltaNeutralConId( m_deltaNeutralConId.getInt() );
+ m_order.stockRangeUpper( m_upper.getDouble() );
+ m_order.stockRangeLower( m_lower.getDouble() );
+ }
+ }
+
+ /** This panel edits all ComboParam values except for Non-Guaranteed.
+ * That one goes on main panel because it applies to all combo orders. */
+ class ComboTicketPanel extends VerticalPanel {
+ final UpperField[] m_fields = new UpperField[ComboParam.values().length];
+
+ ComboTicketPanel() {
+ for (ComboParam param : ComboParam.values() ) {
+ if (param == ComboParam.NonGuaranteed) {
+ continue;
+ }
+ UpperField field = new UpperField();
+ m_fields[param.ordinal()] = field;
+ add( param.toString(), field);
+ field.setText( getVal( param) );
+ }
+ }
+
+ void onOK() {
+ for (ComboParam param : ComboParam.values() ) {
+ if (param == ComboParam.NonGuaranteed) {
+ continue;
+ }
+ UpperField field = m_fields[param.ordinal()];
+ String val = field.getText();
+ if (val != null && val.length() > 0) {
+ TagValue tv = new TagValue( param.toString(), val);
+ m_order.smartComboRoutingParams().add( tv);
+ }
+ }
+ }
+ }
+
+ class AlgoPanel extends VerticalPanel {
+ final TCombo<AlgoStrategy> m_strategy = new TCombo<>( AlgoStrategy.values() );
+ final UpperField[] m_params = new UpperField[AlgoParam.values().length];
+
+ AlgoPanel() {
+ add( "Algo strategy", m_strategy);
+
+ for (AlgoParam param : AlgoParam.values() ) {
+ int i = param.ordinal();
+ m_params[i] = new UpperField(11);
+ add( param.toString(), m_params[param.ordinal()]);
+ }
+
+ m_strategy.setSelectedItem( m_order.algoStrategy() );
+
+ if (m_order.algoParams() != null) {
+ for (TagValue tagVal : m_order.algoParams() ) {
+ AlgoParam param = AlgoParam.valueOf( tagVal.m_tag);
+ m_params[param.ordinal()].setText( tagVal.m_value);
+ }
+ }
+
+ m_strategy.addActionListener(e -> onSelChanged());
+
+ onSelChanged();
+ }
+
+ void onSelChanged() {
+ for (UpperField m_param : m_params) {
+ m_param.setEnabled(false);
+ }
+
+ AlgoStrategy strategy = m_strategy.getSelectedItem();
+ if (strategy != null) {
+ for (AlgoParam param : strategy.params() ) {
+ m_params[param.ordinal()].setEnabled( true);
+ }
+ }
+ }
+
+ void onOK() {
+ m_order.algoStrategy( m_strategy.getSelectedItem() );
+
+ m_order.algoParams().clear();
+ for (AlgoParam param : AlgoParam.values() ) {
+ String val = m_params[param.ordinal()].getText();
+ if (val != null && val.length() > 0) {
+ m_order.algoParams().add( new TagValue( param.toString(), val) );
+ }
+ }
+ }
+ }
+
+ class ScalePanel extends VerticalPanel {
+ UpperField m_initLevelSize = new UpperField();
+ UpperField m_subsLevelSize = new UpperField();
+ UpperField m_priceIncrement = new UpperField();
+ UpperField m_priceAdjustValue = new UpperField();
+ UpperField m_priceAdjustInterval = new UpperField();
+ UpperField m_profitOffset = new UpperField();
+ JCheckBox m_autoReset = new JCheckBox();
+ UpperField m_initPosition = new UpperField();
+ UpperField m_initFillQty = new UpperField();
+ JCheckBox m_randomPercent = new JCheckBox();
+ UpperField m_table = new UpperField();
+
+ ScalePanel() {
+ m_initLevelSize.setText( m_order.scaleInitLevelSize() );
+ m_subsLevelSize.setText( m_order.scaleSubsLevelSize() );
+ m_priceIncrement.setText( m_order.scalePriceIncrement() );
+ m_priceAdjustValue.setText( m_order.scalePriceAdjustValue() );
+ m_priceAdjustInterval.setText( m_order.scalePriceAdjustInterval() );
+ m_profitOffset.setText( m_order.scaleProfitOffset() );
+ m_autoReset.setSelected( m_order.scaleAutoReset() );
+ m_initPosition.setText( m_order.scaleInitPosition() );
+ m_initFillQty.setText( m_order.scaleInitFillQty() );
+ m_randomPercent.setSelected( m_order.scaleRandomPercent() );
+ m_table.setText( m_order.scaleTable() );
+
+ add( "Initial comp size", m_initLevelSize);
+ add( "Subsequent comp size", m_subsLevelSize);
+ add( "Randomize size", m_randomPercent);
+ add( Box.createVerticalStrut( 10) );
+ add( "Price increment", m_priceIncrement);
+ add( "Profit offset", m_profitOffset);
+ add( "Auto-reset", m_autoReset);
+ add( Box.createVerticalStrut( 10) );
+ add( "Initial position", m_initPosition);
+ add( "Filled init comp size", m_initFillQty);
+ add( Box.createVerticalStrut( 10) );
+ add( "Increase price by", m_priceAdjustValue, new JLabel( "every"), m_priceAdjustInterval, new JLabel( "seconds") );
+ add( Box.createVerticalStrut( 10) );
+ add( "Manual table", m_table);
+ }
+
+ void onOK() {
+ m_order.scaleInitLevelSize( m_initLevelSize.getInt());
+ m_order.scaleSubsLevelSize( m_subsLevelSize.getInt());
+ m_order.scalePriceIncrement( m_priceIncrement.getDouble());
+ m_order.scalePriceAdjustValue( m_priceAdjustValue.getDouble());
+ m_order.scalePriceAdjustInterval( m_priceAdjustInterval.getInt());
+ m_order.scaleProfitOffset( m_profitOffset.getDouble());
+ m_order.scaleAutoReset( m_autoReset.isSelected());
+ m_order.scaleInitPosition( m_initPosition.getInt());
+ m_order.scaleInitFillQty( m_initFillQty.getInt());
+ m_order.scaleRandomPercent( m_randomPercent.isSelected());
+ m_order.scaleTable( m_table.getText() );
+ }
+ }
+
+ private String getVal(ComboParam param) {
+ if (m_order.smartComboRoutingParams() != null) {
+ for (TagValue tv : m_order.smartComboRoutingParams() ) {
+ if (tv.m_tag.equals( param.toString() ) ) {
+ return tv.m_value;
+ }
+ }
+ }
+ return "";
+ }
+}
diff --git a/demo/src/main/java/apidemo/TimeConditionPanel.java b/demo/src/main/java/apidemo/TimeConditionPanel.java
new file mode 100755
index 0000000..95632eb
--- /dev/null
+++ b/demo/src/main/java/apidemo/TimeConditionPanel.java
@@ -0,0 +1,26 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.TimeCondition;
+
+public class TimeConditionPanel extends OperatorConditionPanel<TimeCondition> {
+
+ TimeConditionPanel(TimeCondition condition) {
+ super(condition);
+
+ m_value.setText(condition().time());
+
+ add("Operator", m_operator);
+ add("Time", m_value);
+ }
+
+ @Override
+ public TimeCondition onOK() {
+ super.onOK();
+ condition().time(m_value.getText());
+
+ return condition();
+ }
+}
diff --git a/demo/src/main/java/apidemo/TopModel.java b/demo/src/main/java/apidemo/TopModel.java
new file mode 100755
index 0000000..ebd9e6c
--- /dev/null
+++ b/demo/src/main/java/apidemo/TopModel.java
@@ -0,0 +1,300 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import static com.ib.controller.Formats.fmt;
+import static com.ib.controller.Formats.fmtPct;
+import static com.ib.controller.Formats.fmtTime;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.Contract;
+import com.ib.client.MarketDataType;
+import com.ib.client.TickAttr;
+import com.ib.client.TickType;
+import com.ib.controller.ApiController.TopMktDataAdapter;
+import com.ib.controller.Formats;
+
+class TopModel extends AbstractTableModel {
+ private List<TopRow> m_rows = new ArrayList<>();
+ private MarketDataPanel m_parentPanel;
+ private static final int CANCEL_CHBX_COL_INDEX = 25;
+ String m_genericTicks = "";
+
+ TopModel(MarketDataPanel parentPanel) {
+ m_parentPanel = parentPanel;
+ }
+
+ void setGenericTicks(String genericTicks) {
+ m_genericTicks = genericTicks;
+ }
+
+ void addRow( Contract contract) {
+ TopRow row = new TopRow( this, contract.description(), m_parentPanel );
+ m_rows.add( row);
+ ApiDemo.INSTANCE.controller().reqTopMktData(contract, m_genericTicks, false, false, row);
+ fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ }
+
+ void addRow( TopRow row) {
+ m_rows.add( row);
+ fireTableRowsInserted( m_rows.size() - 1, m_rows.size() - 1);
+ }
+
+ void removeSelectedRows() {
+ for(int rowIndex = m_rows.size() - 1; rowIndex >= 0; rowIndex--) {
+ if(m_rows.get(rowIndex).m_cancel) {
+ ApiDemo.INSTANCE.controller().cancelTopMktData( m_rows.get(rowIndex));
+ m_rows.remove(rowIndex);
+ }
+ }
+ fireTableDataChanged();
+ }
+
+ void desubscribe() {
+ for (TopRow row : m_rows) {
+ ApiDemo.INSTANCE.controller().cancelTopMktData( row);
+ }
+ }
+
+ @Override public Class<?> getColumnClass(int columnIndex) {
+ switch (columnIndex) {
+ case CANCEL_CHBX_COL_INDEX:
+ return Boolean.class;
+ default:
+ return String.class;
+ }
+ }
+
+ @Override public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex == CANCEL_CHBX_COL_INDEX;
+ }
+
+ @Override public int getRowCount() {
+ return m_rows.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 26;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Description";
+ case 1: return "Bid Size";
+ case 2: return "Bid";
+ case 3: return "Bid Mask";
+ case 4: return "Bid Can Auto Execute";
+ case 5: return "Bid Past Limit";
+ case 6: return "Pre Open Bid";
+ case 7: return "Ask";
+ case 8: return "Ask Size";
+ case 9: return "Ask Mask";
+ case 10: return "Ask Can Auto Execute";
+ case 11: return "Ask Past Limit";
+ case 12: return "Pre Open Ask";
+ case 13: return "Last";
+ case 14: return "Time";
+ case 15: return "Change";
+ case 16: return "Volume";
+ case 17: return "Min Tick";
+ case 18: return "BBO Exchange";
+ case 19: return "Snapshot Permissions";
+ case 20: return "Close";
+ case 21: return "Open";
+ case 22: return "Market Data Type";
+ case 23: return "Futures Open Interest";
+ case 24: return "Avg Opt Volume";
+ case CANCEL_CHBX_COL_INDEX: return "Cancel";
+
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int rowIn, int col) {
+ TopRow row = m_rows.get( rowIn);
+ switch( col) {
+ case 0: return row.m_description;
+ case 1: return row.m_bidSize;
+ case 2: return fmt( row.m_bid);
+ case 3: return row.m_bidMask;
+ case 4: return row.m_bidCanAutoExecute;
+ case 5: return row.m_bidPastLimit;
+ case 6: return row.m_preOpenBid;
+ case 7: return fmt( row.m_ask);
+ case 8: return row.m_askSize;
+ case 9: return row.m_askMask;
+ case 10: return row.m_askCanAutoExecute;
+ case 11: return row.m_askPastLimit;
+ case 12: return row.m_preOpenAsk;
+ case 13: return fmt( row.m_last);
+ case 14: return fmtTime( row.m_lastTime);
+ case 15: return row.change();
+ case 16: return Formats.fmt0( row.m_volume);
+ case 17: return row.m_minTick;
+ case 18: return row.m_bboExch;
+ case 19: return row.m_snapshotPermissions;
+ case 20: return fmt( row.m_close);
+ case 21: return fmt( row.m_open);
+ case 22: return row.m_marketDataType;
+ case 23: return row.m_futuresOpenInterest;
+ case 24: return row.m_avgOptVolume;
+ case CANCEL_CHBX_COL_INDEX: return row.m_cancel;
+ default: return null;
+ }
+ }
+
+ @Override public void setValueAt(Object aValue, int rowIn, int col) {
+ TopRow row = m_rows.get( rowIn);
+ switch (col) {
+ case CANCEL_CHBX_COL_INDEX:
+ row.m_cancel = (Boolean) aValue;
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void cancel(int i) {
+ ApiDemo.INSTANCE.controller().cancelTopMktData( m_rows.get( i) );
+ }
+
+ static class TopRow extends TopMktDataAdapter {
+ AbstractTableModel m_model;
+ MarketDataPanel m_parentPanel;
+ String m_description;
+ double m_bid;
+ double m_ask;
+ double m_last;
+ long m_lastTime;
+ int m_bidSize;
+ int m_askSize;
+ double m_close;
+ int m_volume;
+ double m_open;
+ boolean m_cancel;
+ String m_marketDataType = MarketDataType.getField(MarketDataType.REALTIME);
+ boolean m_frozen;
+ boolean m_bidCanAutoExecute, m_askCanAutoExecute;
+ boolean m_bidPastLimit, m_askPastLimit;
+ boolean m_preOpenBid, m_preOpenAsk;
+ double m_minTick;
+ String m_bboExch;
+ int m_snapshotPermissions;
+ int m_bidMask, m_askMask;
+ int m_futuresOpenInterest;
+ int m_avgOptVolume;
+
+ TopRow( AbstractTableModel model, String description, MarketDataPanel parentPanel) {
+ m_model = model;
+ m_description = description;
+ m_parentPanel = parentPanel;
+ }
+
+ public String change() {
+ return m_close == 0 ? null : fmtPct( (m_last - m_close) / m_close);
+ }
+
+ @Override public void tickPrice( TickType tickType, double price, TickAttr attribs) {
+ if ( m_marketDataType.equalsIgnoreCase(MarketDataType.getField(MarketDataType.REALTIME)) &&
+ (tickType == TickType.DELAYED_BID ||
+ tickType == TickType.DELAYED_ASK ||
+ tickType == TickType.DELAYED_LAST ||
+ tickType == TickType.DELAYED_CLOSE ||
+ tickType == TickType.DELAYED_OPEN )) {
+ m_marketDataType = MarketDataType.getField(MarketDataType.DELAYED);
+ }
+
+ switch( tickType) {
+ case BID:
+ case DELAYED_BID:
+ m_bid = price;
+ m_bidCanAutoExecute = attribs.canAutoExecute();
+ m_bidPastLimit = attribs.pastLimit();
+ m_preOpenBid = attribs.preOpen();
+ break;
+ case ASK:
+ case DELAYED_ASK:
+ m_ask = price;
+ m_askCanAutoExecute = attribs.canAutoExecute();
+ m_askPastLimit = attribs.pastLimit();
+ m_preOpenAsk = attribs.preOpen();
+ break;
+ case LAST:
+ case DELAYED_LAST:
+ m_last = price;
+ break;
+ case CLOSE:
+ case DELAYED_CLOSE:
+ m_close = price;
+ break;
+ case OPEN:
+ case DELAYED_OPEN:
+ m_open = price;
+ break;
+ default: break;
+ }
+ m_model.fireTableDataChanged(); // should use a timer to be more efficient
+ }
+
+ @Override public void tickSize( TickType tickType, int size) {
+ if ( m_marketDataType.equalsIgnoreCase(MarketDataType.getField(MarketDataType.REALTIME)) &&
+ (tickType == TickType.DELAYED_BID_SIZE ||
+ tickType == TickType.DELAYED_ASK_SIZE ||
+ tickType == TickType.DELAYED_VOLUME)) {
+ m_marketDataType = MarketDataType.getField(MarketDataType.DELAYED);
+ }
+
+ switch( tickType) {
+ case BID_SIZE:
+ case DELAYED_BID_SIZE:
+ m_bidSize = size;
+ break;
+ case ASK_SIZE:
+ case DELAYED_ASK_SIZE:
+ m_askSize = size;
+ break;
+ case VOLUME:
+ case DELAYED_VOLUME:
+ m_volume = size;
+ break;
+ case FUTURES_OPEN_INTEREST:
+ m_futuresOpenInterest = size;
+ break;
+ case AVG_OPT_VOLUME:
+ m_avgOptVolume = size;
+ break;
+ default: break;
+ }
+ m_model.fireTableDataChanged();
+ }
+
+ @Override public void tickString(TickType tickType, String value) {
+ switch( tickType) {
+ case LAST_TIMESTAMP:
+ case DELAYED_LAST_TIMESTAMP:
+ m_lastTime = Long.parseLong( value) * 1000;
+ break;
+ default: break;
+ }
+ }
+
+ @Override public void marketDataType(int marketDataType) {
+ m_marketDataType = MarketDataType.getField(marketDataType);
+ m_model.fireTableDataChanged();
+ }
+
+ @Override public void tickReqParams(int tickerId, double minTick, String bboExchange, int snapshotPermissions) {
+ m_minTick = minTick;
+ m_bboExch = bboExchange;
+ m_snapshotPermissions = snapshotPermissions;
+
+ m_parentPanel.addBboExch(bboExchange);
+ m_model.fireTableDataChanged();
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/TradeConditionPanel.java b/demo/src/main/java/apidemo/TradeConditionPanel.java
new file mode 100755
index 0000000..d6c213f
--- /dev/null
+++ b/demo/src/main/java/apidemo/TradeConditionPanel.java
@@ -0,0 +1,36 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.ExecutionCondition;
+import com.ib.client.OrderCondition;
+
+import apidemo.util.UpperField;
+
+public class TradeConditionPanel extends OnOKPanel {
+ private ExecutionCondition m_condition;
+ private final UpperField m_secType = new UpperField();
+ private final UpperField m_exchange = new UpperField();
+ private final UpperField m_symbol = new UpperField();
+
+ public TradeConditionPanel(ExecutionCondition condition) {
+ m_condition = condition;
+
+ m_secType.setText(m_condition.secType());
+ m_exchange.setText(m_condition.exchange());
+ m_symbol.setText(m_condition.symbol());
+
+ add("Underlying", m_symbol);
+ add("Exchange", m_exchange);
+ add("Type", m_secType);
+ }
+
+ public OrderCondition onOK() {
+ m_condition.symbol(m_symbol.getText());
+ m_condition.exchange(m_exchange.getText());
+ m_condition.secType(m_secType.getText());
+
+ return m_condition;
+ }
+}
diff --git a/demo/src/main/java/apidemo/TradesPanel.java b/demo/src/main/java/apidemo/TradesPanel.java
new file mode 100755
index 0000000..9c15792
--- /dev/null
+++ b/demo/src/main/java/apidemo/TradesPanel.java
@@ -0,0 +1,136 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.AbstractTableModel;
+
+import com.ib.client.CommissionReport;
+import com.ib.client.Contract;
+import com.ib.client.Execution;
+import com.ib.client.ExecutionFilter;
+import com.ib.controller.ApiController.ITradeReportHandler;
+
+import apidemo.util.HtmlButton;
+
+public class TradesPanel extends JPanel implements ITradeReportHandler {
+ private List<FullExec> m_trades = new ArrayList<>();
+ private Map<String,FullExec> m_map = new HashMap<>();
+ private Model m_model = new Model();
+
+ TradesPanel() {
+ JTable table = new JTable( m_model);
+ JScrollPane scroll = new JScrollPane( table);
+ scroll.setBorder( new TitledBorder( "Trade Log"));
+
+ HtmlButton but = new HtmlButton( "Refresh") {
+ @Override public void actionPerformed() {
+ onRefresh();
+ }
+ };
+
+ JPanel p = new JPanel( new FlowLayout( FlowLayout.RIGHT));
+ p.add( but);
+
+ setLayout( new BorderLayout() );
+ add( scroll);
+ add( p, BorderLayout.SOUTH);
+ }
+
+ public void activated() {
+ onRefresh();
+ }
+
+ private void onRefresh() {
+ ApiDemo.INSTANCE.controller().reqExecutions( new ExecutionFilter(), this);
+ }
+
+ @Override public void tradeReport(String tradeKey, Contract contract, Execution trade) {
+ FullExec full = m_map.get( tradeKey);
+
+ if (full != null) {
+ full.m_trade = trade;
+ }
+ else {
+ full = new FullExec( contract, trade);
+ m_trades.add( full);
+ m_map.put( tradeKey, full);
+ }
+
+ m_model.fireTableDataChanged();
+ }
+
+ @Override public void tradeReportEnd() {
+ }
+
+ @Override public void commissionReport(String tradeKey, CommissionReport commissionReport) {
+ FullExec full = m_map.get( tradeKey);
+ if (full != null) {
+ full.m_commissionReport = commissionReport;
+ }
+ }
+
+ private class Model extends AbstractTableModel {
+ @Override public int getRowCount() {
+ return m_trades.size();
+ }
+
+ @Override public int getColumnCount() {
+ return 9;
+ }
+
+ @Override public String getColumnName(int col) {
+ switch( col) {
+ case 0: return "Date/Time";
+ case 1: return "Account";
+ case 2: return "Model Code";
+ case 3: return "Action";
+ case 4: return "Quantity";
+ case 5: return "Description";
+ case 6: return "Price";
+ case 7: return "Commission";
+ case 8: return "Last liquidity";
+ default: return null;
+ }
+ }
+
+ @Override public Object getValueAt(int row, int col) {
+ FullExec full = m_trades.get( row);
+
+ switch( col) {
+ case 0: return full.m_trade.time();
+ case 1: return full.m_trade.acctNumber();
+ case 2: return full.m_trade.modelCode();
+ case 3: return full.m_trade.side();
+ case 4: return full.m_trade.shares();
+ case 5: return full.m_contract.description();
+ case 6: return full.m_trade.price();
+ case 7: return full.m_commissionReport != null ? full.m_commissionReport.m_commission : null;
+ case 8: return full.m_trade.lastLiquidity();
+ default: return null;
+ }
+ }
+ }
+
+ static class FullExec {
+ Contract m_contract;
+ Execution m_trade;
+ CommissionReport m_commissionReport;
+
+ FullExec( Contract contract, Execution trade) {
+ m_contract = contract;
+ m_trade = trade;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/TradingPanel.java b/demo/src/main/java/apidemo/TradingPanel.java
new file mode 100755
index 0000000..b588f91
--- /dev/null
+++ b/demo/src/main/java/apidemo/TradingPanel.java
@@ -0,0 +1,35 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import java.awt.Dimension;
+
+import javax.swing.BoxLayout;
+
+import apidemo.util.NewTabbedPanel.NewTabPanel;
+
+
+public class TradingPanel extends NewTabPanel {
+ private final OrdersPanel m_ordersPanel = new OrdersPanel();
+ private final TradesPanel m_tradesPanel = new TradesPanel();
+
+ TradingPanel() {
+ m_ordersPanel.setPreferredSize( new Dimension( 1, 10000));
+ m_tradesPanel.setPreferredSize( new Dimension( 1, 10000));
+
+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS));
+ add( m_ordersPanel);
+ add( m_tradesPanel);
+ }
+
+ /** Called when the tab is first visited. */
+ @Override public void activated() {
+ m_ordersPanel.activated();
+ m_tradesPanel.activated();
+ }
+
+ /** Called when the tab is closed by clicking the X. */
+ @Override public void closed() {
+ }
+}
diff --git a/demo/src/main/java/apidemo/VolumeConditionPanel.java b/demo/src/main/java/apidemo/VolumeConditionPanel.java
new file mode 100755
index 0000000..ae1e35f
--- /dev/null
+++ b/demo/src/main/java/apidemo/VolumeConditionPanel.java
@@ -0,0 +1,28 @@
+/* Copyright (C) 2018 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 apidemo;
+
+import com.ib.client.ContractLookuper;
+import com.ib.client.VolumeCondition;
+
+public class VolumeConditionPanel extends ContractConditionPanel<VolumeCondition> {
+
+ VolumeConditionPanel(VolumeCondition condition, ContractLookuper lookuper) {
+ super(condition, lookuper);
+
+ m_value.setText(condition().volume());
+
+ add("Operator", m_operator);
+ add("Volume", m_value);
+ }
+
+ @Override
+ public VolumeCondition onOK() {
+ super.onOK();
+
+ condition().volume(m_value.getInt());
+
+ return condition();
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/HtmlButton.java b/demo/src/main/java/apidemo/util/HtmlButton.java
new file mode 100755
index 0000000..0b9eef0
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/HtmlButton.java
@@ -0,0 +1,137 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+
+import javax.swing.JLabel;
+import javax.swing.border.Border;
+
+public class HtmlButton extends JLabel {
+ static Color light = new Color( 220, 220, 220);
+
+ private String m_text;
+ protected boolean m_selected;
+ private ActionListener m_al;
+ private Color m_bg = getBackground();
+
+ public boolean isSelected() { return m_selected; }
+
+ public void setSelected( boolean v) { m_selected = v; }
+ public void addActionListener(ActionListener v) { m_al = v; }
+
+ public HtmlButton( String text) {
+ this( text, null);
+ }
+
+ @Override
+ public void setText(String text) {
+ m_text = text;
+ super.setText(text);
+ }
+
+ public HtmlButton( String text, ActionListener v) {
+ super( text);
+ m_text = text;
+ m_al = v;
+ setOpaque( true);
+ setForeground( Color.blue);
+
+ MouseAdapter a = new MouseAdapter() {
+ public void mousePressed(MouseEvent e) {
+ onPressed( e);
+ }
+ public void mouseReleased(MouseEvent e) {
+ onClicked(e);
+ setBackground( m_bg);
+ }
+ public void mouseEntered(MouseEvent e) {
+ onEntered(e);
+ }
+ public void mouseExited(MouseEvent e) {
+ onExited();
+ }
+ @Override public void mouseMoved(MouseEvent e) {
+ onMouseMoved( e);
+ }
+ };
+ addMouseListener( a);
+ addMouseMotionListener(a);
+ setFont( getFont().deriveFont( Font.PLAIN) );
+ }
+
+ protected void onMouseMoved(MouseEvent e) {
+ }
+
+ protected void onEntered(MouseEvent e) {
+ if (!m_selected) {
+ setText( underline( m_text) );
+ }
+ }
+
+ protected void onExited() {
+ setText( m_text);
+ }
+
+ protected void onPressed(MouseEvent e) {
+ if (!m_selected) {
+ setBackground( light);
+ }
+ }
+
+ protected void onClicked(MouseEvent e) {
+ actionPerformed();
+ }
+
+ protected void actionPerformed() {
+ if( m_al != null) {
+ m_al.actionPerformed( null);
+ }
+ }
+
+ public static class HtmlRadioButton extends HtmlButton {
+ private HashSet<HtmlRadioButton> m_group;
+ HtmlRadioButton( String text, HashSet<HtmlRadioButton> group) {
+ super( text);
+ m_group = group;
+ group.add( this);
+ }
+ @Override protected void actionPerformed() {
+ for( HtmlRadioButton but : m_group) {
+ but.setSelected( false);
+ }
+ setSelected( true);
+ super.actionPerformed();
+ }
+ }
+
+ static String underline( String str) {
+ return String.format( "<html><u>%s</html>", str);
+ }
+
+ static String bold( String str) {
+ return String.format( "<html><b>%s</html>", str);
+ }
+
+ static class B implements Border {
+ @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+ }
+
+ @Override public Insets getBorderInsets(Component c) {
+ return null;
+ }
+
+ @Override public boolean isBorderOpaque() {
+ return false;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/IConnectionConfiguration.java b/demo/src/main/java/apidemo/util/IConnectionConfiguration.java
new file mode 100755
index 0000000..6527ee8
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/IConnectionConfiguration.java
@@ -0,0 +1,20 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+
+/** Delegate for connection parameters */
+public interface IConnectionConfiguration {
+
+ String getDefaultHost();
+ String getDefaultPort();
+ String getDefaultConnectOptions();
+
+ /** Standard ApiDemo configuration for pre-v100 connection */
+ class DefaultConnectionConfiguration implements IConnectionConfiguration {
+ @Override public String getDefaultHost() { return ""; }
+ @Override public String getDefaultPort() { return "7496"; }
+ @Override public String getDefaultConnectOptions() { return null; }
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/NewLookAndFeel.java b/demo/src/main/java/apidemo/util/NewLookAndFeel.java
new file mode 100755
index 0000000..c8e8af7
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/NewLookAndFeel.java
@@ -0,0 +1,108 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import java.awt.*;
+
+import javax.swing.JComponent;
+import javax.swing.JTable;
+import javax.swing.Timer;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.border.EmptyBorder;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicTableUI;
+import javax.swing.plaf.metal.MetalCheckBoxUI;
+import javax.swing.plaf.metal.MetalComboBoxUI;
+import javax.swing.plaf.metal.MetalLabelUI;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+public class NewLookAndFeel extends MetalLookAndFeel {
+ @Override protected void initClassDefaults(UIDefaults table) {
+ super.initClassDefaults(table);
+
+ Object[] uiDefaults = new Object[] {
+ "CheckBoxUI", NewCheckUI.class.getName(),
+ "LabelUI", NewLabelUI.class.getName(),
+ "ComboBoxUI", NewComboUI.class.getName(),
+ "TableUI", NewTableUI.class.getName()
+ };
+
+ table.putDefaults(uiDefaults);
+ }
+
+ public static void register() {
+ try {
+ UIManager.setLookAndFeel( new NewLookAndFeel() );
+ } catch (UnsupportedLookAndFeelException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static class NewLabelUI extends MetalLabelUI {
+ private static final NewLabelUI UI = new NewLabelUI();
+
+ public static ComponentUI createUI(JComponent c) {
+ return UI;
+ }
+
+ @Override public void installUI(JComponent c) {
+ super.installUI(c);
+ c.setFont( c.getFont().deriveFont(Font.PLAIN) );
+ }
+ }
+
+ public static class NewCheckUI extends MetalCheckBoxUI {
+ private static final NewCheckUI UI = new NewCheckUI();
+
+ public static ComponentUI createUI(JComponent c) {
+ return UI;
+ }
+
+ @Override public void installUI(JComponent c) {
+ super.installUI(c);
+ c.setBorder( new EmptyBorder( 3, 0, 3, 0) );
+ }
+ }
+
+ public static class NewComboUI extends MetalComboBoxUI {
+ public static ComponentUI createUI(JComponent c) {
+ return new NewComboUI();
+ }
+
+ @Override public void installUI(JComponent c) {
+ super.installUI(c);
+ c.setFont( c.getFont().deriveFont(Font.PLAIN) );
+ c.setPreferredSize( new Dimension( c.getPreferredSize().width, 19));
+ }
+ }
+
+ public static class NewTableUI extends BasicTableUI {
+ public static ComponentUI createUI(JComponent c) {
+ return new NewTableUI();
+ }
+
+ @Override public void installUI(JComponent c) {
+ super.installUI(c);
+
+ final JTable table = (JTable)c;
+ table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF);
+
+ TableColumnModel mod = table.getColumnModel();
+ for (int iCol = 0; iCol < mod.getColumnCount(); iCol++) {
+ TableColumn col = mod.getColumn( iCol);
+ col.setPreferredWidth( 40);
+ }
+
+ final Timer timer = new Timer( 300, e -> Util.resizeColumns( table));
+ timer.setRepeats( false);
+ timer.start();
+
+ table.getModel().addTableModelListener(e -> timer.restart());
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/NewTabbedPanel.java b/demo/src/main/java/apidemo/util/NewTabbedPanel.java
new file mode 100755
index 0000000..3924d83
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/NewTabbedPanel.java
@@ -0,0 +1,313 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+
+
+public class NewTabbedPanel extends JPanel {
+ private static final Color COLOR = new Color( 184, 207, 229);
+ private static final int V1 = 1; // bottom line is raised this amount
+ private static final int EXTRA_TAB_WIDTH = 14; // extra width on each side
+ private static final int BUFF = 22; // extra width on each side (should be WID + BUF)
+ private static final int TAB_HEIGHT = 20; // total height of button
+ private static final Border B1 = new TabBorder();
+ private static final Border B2 = new UnderBorder();
+ private static final TabIcon ICON1 = new TabIcon(false);
+ private static final TabIcon ICON2 = new TabIcon(true);
+
+ private final JPanel m_topPanel = new JPanel();
+ private final CardLayout m_cardLayout = new CardLayout();
+ private final JPanel m_cardPanel = new JPanel( m_cardLayout);
+ private final Map<String, Tab> m_map = new HashMap<>();
+ private final boolean m_underline; // if true, draws a horizontal line all the way across
+ private int m_count = 2;
+
+ public NewTabbedPanel() {
+ this( false);
+ }
+
+ public NewTabbedPanel( boolean underline) {
+ m_underline = underline;
+
+ setLayout( new BorderLayout() );
+ m_topPanel.setLayout( new FlowLayout( FlowLayout.LEFT, 0, 15) );
+
+ add( new JScrollPane( m_topPanel, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.NORTH);
+ add( m_cardPanel);
+ }
+
+ public void addTab( final String title, final JComponent tab) {
+ addTab( title, tab, false, false);
+ }
+
+ public void addTab( final String titleIn, final JComponent comp, boolean select, boolean canClose) {
+ final String title = m_map.containsKey( titleIn)
+ ? titleIn + " " + m_count++ : titleIn;
+
+ HtmlButton button = new But( title, canClose, e -> select( title));
+
+ Tab tab = new Tab( title, comp, button);
+ m_map.put( title, tab);
+
+ m_topPanel.add( createSpacer( 15) );
+ m_topPanel.add( button);
+ m_cardPanel.add( comp, title);
+
+ if (m_map.size() == 1 || select) {
+ select( title);
+ }
+ else {
+ button.setSelected( false);
+ }
+ }
+
+ /** Select the correct panel and underline the correct html button. */
+ public void select(String title) {
+ // show selected tab
+ m_cardLayout.show( m_cardPanel, title);
+
+ Tab selectedTab = m_map.get( title);
+
+ // select or deselect all buttons
+ for( Tab tab : m_map.values() ) {
+ tab.m_button.setSelected( tab == selectedTab);
+ }
+
+ // call activated() on selected tab?
+ if (!selectedTab.m_activated && selectedTab.m_comp instanceof INewTab) {
+ ((INewTab)selectedTab.m_comp).activated();
+ selectedTab.m_activated = true;
+ }
+ }
+
+ private Tab getSelectedTab() {
+ for( Tab tab : m_map.values() ) {
+ if (tab.m_button.isSelected() ) {
+ return tab;
+ }
+ }
+ return null;
+ }
+
+ private Component createSpacer( final int i) {
+ JPanel h = new JPanel();
+ h.setPreferredSize( new Dimension( i, TAB_HEIGHT) );
+ if (m_underline) {
+ h.setBorder( B2);
+ }
+ return h;
+ }
+
+ public interface INewTab {
+ void activated(); // called when the tab is first visited
+ void closed(); // called when the tab is closed
+ }
+
+ public static abstract class NewTabPanel extends JPanel implements INewTab {
+ }
+
+ private static class Tab {
+ String m_title;
+ JComponent m_comp;
+ HtmlButton m_button;
+ boolean m_activated;
+
+ Tab(String title, JComponent comp, HtmlButton button) {
+ m_title = title;
+ m_comp = comp;
+ m_button = button;
+ }
+ }
+
+ static class TabBorder implements Border {
+ @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+ g.setColor( COLOR);
+ int L = x + 4;
+ int R = width - 5;
+ int T = y;
+ int B = height - V1;
+ g.drawLine( L, T, R, T);
+ g.drawLine( L, T, L, B);
+ g.drawLine( R, T, R, B);
+ g.drawLine( x, B, L, B);
+ g.drawLine( R, B, width, B);
+ }
+
+ @Override public Insets getBorderInsets(Component c) {
+ return new Insets( 0, 0, 0, 0);
+ }
+
+ @Override public boolean isBorderOpaque() {
+ return false;
+ }
+ }
+
+ static class UnderBorder implements Border {
+ @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+ g.setColor( COLOR);
+ int L = x;
+ int R = width;
+ int B = height - V1;
+ g.drawLine( L, B, R, B);
+ }
+
+ @Override public Insets getBorderInsets(Component c) {
+ return new Insets( 0, 0, 0, 0);
+ }
+
+ @Override public boolean isBorderOpaque() {
+ return false;
+ }
+ }
+
+ public static class TabIcon implements Icon {
+ private static final int ICON_SIZE = 5;
+ private final boolean m_under;
+
+ TabIcon( boolean under) {
+ m_under = under;
+ }
+
+ @Override public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.setColor( Color.black);
+ int left = x + 1;
+ int right = left + ICON_SIZE - 1;
+ int top = y;
+ int bot = top + ICON_SIZE - 1;
+ g.drawLine(left, top, right, bot);
+ g.drawLine(left, bot, right, top);
+ if (m_under) {
+ g.drawLine( left, bot + 2, right, bot + 2);
+ }
+ }
+
+ @Override public int getIconWidth() {
+ return ICON_SIZE + 4;
+ }
+
+ @Override public int getIconHeight() {
+ return ICON_SIZE;
+ }
+ }
+
+ class But extends HtmlButton {
+ boolean m_canClose;
+
+ But(String text, boolean canClose, ActionListener v) {
+ super(text, v);
+ m_canClose = canClose;
+ setHorizontalAlignment(SwingConstants.CENTER);
+ setHorizontalTextPosition(SwingConstants.LEFT);
+ }
+
+ @Override public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+ d.height = TAB_HEIGHT;
+ d.width += EXTRA_TAB_WIDTH;
+ return d;
+ }
+
+ @Override public void setSelected(boolean v) {
+ super.setSelected( v);
+ setBorder( v ? B1 : m_underline ? B2 : null);
+ setIcon( v && m_canClose ? ICON1 : null);
+ }
+
+ protected void onPressed(MouseEvent e) {
+ if (overX(e) ) {
+ setBackground( light);
+ }
+ else {
+ super.onPressed( e);
+ }
+ }
+
+ @Override protected void onClicked(MouseEvent e) {
+ if (overX(e) ) {
+ onClosed();
+ }
+ else {
+ super.onClicked(e);
+ }
+ }
+
+ @Override protected void onEntered(MouseEvent e) {
+ super.onEntered(e);
+ if (overX( e) ) {
+ setIcon( ICON2);
+ }
+ }
+
+ @Override protected void onMouseMoved(MouseEvent e) {
+ super.onMouseMoved(e);
+ if (overX( e) ) {
+ setIcon( ICON2);
+ }
+ else if (m_canClose && isSelected() ) {
+ setIcon( ICON1);
+ }
+ }
+
+ @Override protected void onExited() {
+ super.onExited();
+ if (m_canClose && isSelected() ) {
+ setIcon( ICON1);
+ }
+ }
+
+ private boolean overX(MouseEvent e) {
+ return m_canClose && isSelected() && e.getX() > getWidth() - BUFF;
+ }
+ }
+
+ void onClosed() {
+ Tab tab = getSelectedTab();
+ if (tab == null) return;
+ if (tab.m_comp instanceof INewTab) {
+ ((INewTab)tab.m_comp).closed();
+ }
+ m_topPanel.remove(Arrays.asList(m_topPanel.getComponents()).indexOf(tab.m_button) - 1);
+ m_topPanel.remove( tab.m_button);
+ m_topPanel.repaint();
+
+ m_cardPanel.remove( tab.m_comp);
+ m_map.remove( tab.m_title);
+
+ if (!m_map.isEmpty() ) {
+ Entry<String, Tab> entry = m_map.entrySet().iterator().next();
+ select( entry.getValue().m_title);
+ }
+ }
+
+ public static void main(String[] args) {
+ NewTabbedPanel p = new NewTabbedPanel();
+ p.addTab( "Tab1", new JLabel( "tab 1"), true, true);
+ p.addTab( "Tab2", new JLabel( "tab 2"), false, true);
+ p.addTab( "Tab3", new JLabel( "tab 3"), false, true);
+
+ JFrame f = new JFrame();
+ f.add( p);
+ f.setSize( 200, 200);
+ f.setVisible( true);
+ f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ }
+
+}
diff --git a/demo/src/main/java/apidemo/util/TCombo.java b/demo/src/main/java/apidemo/util/TCombo.java
new file mode 100755
index 0000000..c9641b9
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/TCombo.java
@@ -0,0 +1,23 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import javax.swing.*;
+
+public class TCombo<T> extends JComboBox<T> {
+ @SafeVarargs
+ public TCombo(T... items) {
+ super(items);
+ }
+
+ public String getText() {
+ return getSelectedItem() == null ? null : getSelectedItem().toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getSelectedItem() {
+ return (T) super.getSelectedItem();
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/UpperField.java b/demo/src/main/java/apidemo/util/UpperField.java
new file mode 100755
index 0000000..c766433
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/UpperField.java
@@ -0,0 +1,80 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import javax.swing.JTextField;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+
+public class UpperField extends JTextField {
+ int m_ival;
+ double m_dval;
+
+ public UpperField() {
+ this( null);
+ }
+
+ public UpperField( int i) {
+ this( null, i);
+ }
+
+ public UpperField( String s) {
+ this( s, 7);
+ }
+
+ public UpperField( String s, int i) {
+ super( i);
+
+ setDocument( new PlainDocument() {
+ @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
+ super.insertString(offs, str.toUpperCase(), a);
+ }
+ });
+
+ setText( s);
+ }
+
+ public void setText( double v) {
+ if (v == Double.MAX_VALUE || v == 0) {
+ m_dval = v;
+ setText( null);
+ }
+ else {
+ super.setText( "" + v);
+ }
+ }
+
+ public void setText( int v) {
+ if (v == Integer.MAX_VALUE || v == 0) {
+ m_ival = v;
+ setText( null);
+ }
+ else {
+ super.setText( "" + v);
+ }
+ }
+
+ public double getDouble() {
+ try {
+ String str = super.getText();
+ return str == null || str.length() == 0
+ ? m_dval : Double.parseDouble( super.getText().trim() );
+ }
+ catch( Exception e) {
+ return 0;
+ }
+ }
+
+ public int getInt() {
+ try {
+ String str = super.getText();
+ return str == null || str.length() == 0
+ ? m_ival : Integer.parseInt( super.getText().trim() );
+ }
+ catch( Exception e) {
+ return 0;
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/Util.java b/demo/src/main/java/apidemo/util/Util.java
new file mode 100755
index 0000000..b927597
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/Util.java
@@ -0,0 +1,75 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import java.awt.FontMetrics;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+public class Util {
+ private static final int BUF = 14;
+ private static final int MAX = 300;
+
+ /** Resize all columns in the table to fit widest row including header. */
+ public static void resizeColumns( JTable table) {
+ if (table.getGraphics() == null) {
+ return;
+ }
+
+ DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
+ FontMetrics fm = table.getFontMetrics( renderer.getFont() );
+
+ TableColumnModel mod = table.getColumnModel();
+ for (int iCol = 0; iCol < mod.getColumnCount(); iCol++) {
+ TableColumn col = mod.getColumn( iCol);
+
+ int max = col.getPreferredWidth() - BUF;
+
+ String header = table.getModel().getColumnName( iCol);
+ if (header != null) {
+ max = Math.max( max, fm.stringWidth( header) );
+ }
+
+ for (int iRow = 0; iRow < table.getModel().getRowCount(); iRow++) {
+ Object obj = table.getModel().getValueAt(iRow, iCol);
+ String str = obj == null ? "" : obj.toString();
+ max = Math.max( max, fm.stringWidth( str) );
+ }
+
+ col.setPreferredWidth( max + BUF);
+ col.setMaxWidth( MAX);
+ }
+ table.revalidate();
+ table.repaint();
+ }
+
+ /** Configure dialog to close when Esc is pressed. */
+ public static void closeOnEsc( final JDialog dlg) {
+ dlg.getRootPane().getActionMap().put( "Cancel", new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ dlg.dispose();
+ }
+ });
+
+ dlg.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
+ }
+
+ public static void sleep( int ms) {
+ try {
+ Thread.sleep( ms);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/demo/src/main/java/apidemo/util/VerticalPanel.java b/demo/src/main/java/apidemo/util/VerticalPanel.java
new file mode 100755
index 0000000..384b058
--- /dev/null
+++ b/demo/src/main/java/apidemo/util/VerticalPanel.java
@@ -0,0 +1,160 @@
+/* Copyright (C) 2018 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 apidemo.util;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+
+public class VerticalPanel extends JPanel {
+ private static class FlowPanel extends JPanel {
+ FlowPanel( Component[] comps) {
+ setLayout( new FlowLayout( FlowLayout.LEFT, 5, 2) );
+ for( Component comp : comps) {
+ add( comp);
+ }
+ setAlignmentX(0);
+ }
+
+ @Override public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+
+ int wid() {
+ return getComponent( 0).getPreferredSize().width;
+ }
+
+ int wid2() {
+ return getComponentCount() > 1 ? getComponent(1).getPreferredSize().width : 0;
+ }
+
+ void wid( int i) {
+ Dimension d = getComponent( 0).getPreferredSize();
+ d.width = i;
+ getComponent( 0).setPreferredSize( d);
+ }
+
+ void wid2(int i) {
+ if (getComponentCount() < 2) {
+ return;
+ }
+
+ Dimension d = getComponent(1).getPreferredSize();
+ d.width = i;
+ getComponent(1).setPreferredSize( d);
+ }
+ }
+
+ public VerticalPanel() {
+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS));
+ }
+
+ @Override public Component add(Component comp) {
+ add( new Component[] { comp } );
+ return comp;
+ }
+
+ @Override public Component add(String str, Component comp) {
+ add( new Component[] { new JLabel( str), comp } );
+ return null;
+ }
+
+ public void add( String str, Component... cs) {
+ Component[] ar = new Component[cs.length + 1];
+ ar[0] = new JLabel( str);
+ System.arraycopy(cs, 0, ar, 1, cs.length);
+ add( ar);
+ }
+
+ public void add( Component... comps) {
+ add( -1, comps);
+ }
+
+ @Override public Component add(Component comp, int index) {
+ add( index, comp);
+ return null;
+ }
+
+ public void add( int index, Component... comps) {
+ super.add( new FlowPanel( comps), index);
+
+ recalculateChildSizes();
+ }
+
+ private void recalculateChildSizes() {
+ int max = 0;
+ for (int i = 0; i < getComponentCount(); i++) {
+ FlowPanel comp = (FlowPanel)getComponent( i);
+ max = Math.max( comp.wid(), max);
+ }
+
+ for (int i = 0; i < getComponentCount(); i++) {
+ FlowPanel comp = (FlowPanel)getComponent( i);
+ comp.wid( max);
+ }
+
+ max = 0;
+ for (int i = 0; i < getComponentCount(); i++) {
+ FlowPanel comp = (FlowPanel)getComponent( i);
+ max = Math.max( comp.wid2(), max);
+ }
+
+ for (int i = 0; i < getComponentCount(); i++) {
+ FlowPanel comp = (FlowPanel)getComponent( i);
+ comp.wid2( max);
+ }
+ }
+
+ public void add(String str, Component comp, int index) {
+ add( index, new JLabel( str), comp);
+ }
+
+ @Override public void add(Component comp, Object constraints) {
+ throw new RuntimeException(); // not valid
+ }
+
+ @Override public void add(Component comp, Object constraints, int index) {
+ throw new RuntimeException(); // not valid
+ }
+
+
+ public static class HorzPanel extends JPanel {
+ public HorzPanel() {
+ setLayout( new BoxLayout( this, BoxLayout.X_AXIS));
+ }
+
+ public void add(JComponent comp) {
+ comp.setAlignmentY(0);
+ super.add( comp);
+ }
+ }
+
+ public static class BorderPanel extends JPanel {
+ public BorderPanel() {
+ setLayout( new BorderLayout() );
+ }
+ }
+
+ public static class StackPanel extends JPanel {
+ public StackPanel() {
+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS));
+ }
+
+ public void add(JComponent comp) {
+ comp.setAlignmentX( 0);
+ super.add( comp);
+ }
+
+ @Override public Dimension getMaximumSize() {
+ return super.getPreferredSize();
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 84f169a..63a38ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,6 +8,7 @@
<modules>
<module>api</module>
+ <module>demo</module>
</modules>
</project>