├── project
├── build.properties
├── assembly.sbt
└── plugins.sbt
├── data
└── limits.dat
├── src
├── main
│ ├── scala
│ │ └── com
│ │ │ └── larroy
│ │ │ └── ibclient
│ │ │ ├── order
│ │ │ ├── kind
│ │ │ │ ├── Kind.scala
│ │ │ │ ├── Market.scala
│ │ │ │ ├── Stop.scala
│ │ │ │ ├── Limit.scala
│ │ │ │ ├── TrailStop.scala
│ │ │ │ ├── TrailStopLimit.scala
│ │ │ │ ├── TrailMarketIfTouched.scala
│ │ │ │ ├── TrailLimitIfTouched.scala
│ │ │ │ └── StopLimit.scala
│ │ │ ├── Sell.scala
│ │ │ ├── Buy.scala
│ │ │ ├── ExecutionStatus.scala
│ │ │ └── Order.scala
│ │ │ ├── IBClientError.scala
│ │ │ ├── account
│ │ │ ├── Value.scala
│ │ │ ├── Position.scala
│ │ │ ├── AccountUpdate.scala
│ │ │ └── AccountUpdateSubscription.scala
│ │ │ ├── handler
│ │ │ ├── Handler.scala
│ │ │ ├── package.scala
│ │ │ ├── OrderStatusHandler.scala
│ │ │ ├── HistoricalDataHandler.scala
│ │ │ ├── PositionHandler.scala
│ │ │ ├── OpenOrdersHandler.scala
│ │ │ ├── ContractDetailsHandler.scala
│ │ │ ├── MarketDataHandler.scala
│ │ │ ├── RealtimeBarsHandler.scala
│ │ │ └── AccountUpdateHandler.scala
│ │ │ ├── Position.scala
│ │ │ ├── IBApiError.scala
│ │ │ ├── util
│ │ │ ├── ValidDuration.scala
│ │ │ ├── CaseClassBeautifier.scala
│ │ │ ├── HistoricalRequest.scala
│ │ │ ├── HistoryDuration.scala
│ │ │ ├── package.scala
│ │ │ ├── HistoricalRateLimiter.scala
│ │ │ └── HistoryLimits.scala
│ │ │ ├── OpenOrder.scala
│ │ │ ├── OrderStatus.scala
│ │ │ ├── contract
│ │ │ ├── StockContract.scala
│ │ │ ├── CashContract.scala
│ │ │ ├── GenericContract.scala
│ │ │ └── FutureContract.scala
│ │ │ ├── Bar.scala
│ │ │ ├── MarketDataSubscription.scala
│ │ │ ├── RealtimeBarsSubscription.scala
│ │ │ ├── package.scala
│ │ │ ├── Tick.scala
│ │ │ └── Main.scala
│ ├── java
│ │ └── com
│ │ │ └── ib
│ │ │ ├── controller
│ │ │ ├── PairPanel.java
│ │ │ ├── TradeId.java
│ │ │ ├── Instrument.java
│ │ │ ├── Alias.java
│ │ │ ├── MarketValueTag.java
│ │ │ ├── Group.java
│ │ │ ├── ConcurrentHashSet.java
│ │ │ ├── AccountSummaryTag.java
│ │ │ ├── Bar.java
│ │ │ ├── Formats.java
│ │ │ ├── LocationCode.java
│ │ │ ├── Position.java
│ │ │ ├── Profile.java
│ │ │ ├── ScanCode.java
│ │ │ ├── AdvisorUtil.java
│ │ │ └── ApiConnection.java
│ │ │ ├── client
│ │ │ ├── IApiEnum.java
│ │ │ ├── OrderStatus.java
│ │ │ ├── TagValue.java
│ │ │ ├── OrderComboLeg.java
│ │ │ ├── MarketDataType.java
│ │ │ ├── CommissionReport.java
│ │ │ ├── DeltaNeutralContract.java
│ │ │ ├── OrderType.java
│ │ │ ├── Util.java
│ │ │ ├── ExecutionFilter.java
│ │ │ ├── TickType.java
│ │ │ ├── OrderState.java
│ │ │ ├── Builder.java
│ │ │ ├── EWrapper.java
│ │ │ ├── ScannerSubscription.java
│ │ │ ├── Execution.java
│ │ │ ├── ComboLeg.java
│ │ │ ├── EClientErrors.java
│ │ │ ├── ContractDetails.java
│ │ │ ├── Types.java
│ │ │ └── Contract.java
│ │ │ └── contracts
│ │ │ ├── StkContract.java
│ │ │ ├── FutContract.java
│ │ │ ├── ComboContract.java
│ │ │ └── OptContract.java
│ └── resources
│ │ └── reference.conf
└── test
│ ├── scala
│ └── com
│ │ └── larroy
│ │ └── ibclient
│ │ ├── util
│ │ ├── UtilSpec.scala
│ │ ├── CaseClassBeautifierSpec.scala
│ │ ├── HistoricalRateLimiterSpec.scala
│ │ └── HistoryLimitsSpec.scala
│ │ └── IBClientSpec.scala
│ └── resources
│ └── logback.xml
├── .gitignore
├── LICENSE.md
├── notes
├── README.md
└── scalastyle-config.xml
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.2.8
2 |
--------------------------------------------------------------------------------
/project/assembly.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10")
2 |
--------------------------------------------------------------------------------
/data/limits.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openquant/ibclient/HEAD/data/limits.dat
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/Kind.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 19.02.15
5 | */
6 | trait Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/Market.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 19.02.15
5 | */
6 | case class Market() extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/Stop.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 19.02.15
5 | */
6 | case class Stop(stop: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/IBClientError.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | /**
4 | * @author piotr 16.02.15
5 | */
6 | case class IBClientError(msg: String) extends Exception(msg)
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/account/Value.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.account
2 |
3 | /**
4 | * @author piotr 08.04.15
5 | */
6 | case class Value(value: Double, currency: String)
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/Limit.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 19.02.15
5 | */
6 | case class Limit(limit: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/TrailStop.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 20.02.15
5 | */
6 | case class TrailStop(stop: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/TrailStopLimit.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 20.02.15
5 | */
6 | case class TrailStopLimit(stop: Double, trail: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/Handler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | /**
4 | * @author piotr 20.02.15
5 | */
6 | trait Handler {
7 | def error(throwable: Throwable): Unit = {}
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
2 | addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.12")
3 | resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/"
4 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/TrailMarketIfTouched.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 20.02.15
5 | */
6 | case class TrailMarketIfTouched(stop: Double, trail: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/TrailLimitIfTouched.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 20.02.15
5 | */
6 | case class TrailLimitIfTouched(stop: Double, limit: Double, trail: Double) extends Kind
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/Position.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import com.ib.client.Contract
4 |
5 | /**
6 | * @author piotr 19.02.15
7 | */
8 | case class Position(account: String, contract: Contract, position: Int, avgCost: Double)
9 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/IBApiError.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | /**
4 | * @author piotr 2/10/15
5 | */
6 | case class IBApiError(code: Int, msg: String, reqId: Int) extends Exception(msg) {
7 | def this(msg: String) = this(-1, msg, -1)
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Editor backup
2 | .*.swp
3 | .*.kate-swp
4 | *~
5 | *.tmp
6 |
7 | # Idea and sbt artifacts
8 | /.ensime*
9 | /.idea
10 | /.idea_modules
11 | /target
12 | /project/project
13 | /project/target
14 |
15 | # Python
16 | *.pyc
17 | __pycache__
18 |
19 | /*.log
20 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/package.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | /**
4 | * These are internal classes used by [[IBClient]] to handle events from the underlying java client.
5 | * There are not of interest for users.
6 | */
7 | package object handler {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/Sell.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order
2 |
3 | import com.larroy.ibclient.order.kind.Kind
4 |
5 | /**
6 | * Sell order
7 | * @author piotr 19.02.15
8 | */
9 | case class Sell(override val kind: Kind, override val quantity: Int) extends Order
10 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/ValidDuration.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import com.ib.client.Types.{DurationUnit, BarSize}
4 |
5 | /**
6 | * @author piotr 01.03.15
7 | */
8 | case class ValidDurations(barSize: BarSize, durationUnit: DurationUnit, durations: Array[Int])
9 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/PairPanel.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/Buy.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order
2 |
3 | import com.larroy.ibclient.order.kind.Kind
4 |
5 |
6 | /**
7 | * Buy Order
8 | * @author piotr 19.02.15
9 | */
10 | case class Buy(override val kind: Kind, override val quantity: Int) extends Order
11 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/OpenOrder.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import com.ib.client.{Contract, OrderState}
4 | import com.ib.client.{Order ⇒ IBOrder, _}
5 |
6 | /**
7 | * Created by piotr on 6/13/15.
8 | */
9 | case class OpenOrder(orderId: Int, contract: Contract, order: IBOrder, orderState: OrderState)
10 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/OrderStatusHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.OrderStatus
4 | import rx.lang.scala.Subject
5 |
6 | /**
7 | * Created by piotr on 6/13/15.
8 | */
9 | case class OrderStatusHandler(subject: Subject[OrderStatus]) extends Handler {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/kind/StopLimit.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order.kind
2 |
3 | /**
4 | * @author piotr 19.02.15
5 | */
6 |
7 | /**
8 | * @param stop stop price, in which the order is triggered
9 | * @param limit price limit
10 | */
11 | case class StopLimit(stop: Double, limit: Double) extends Kind
12 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/HistoricalDataHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.Bar
4 |
5 | import scala.collection.mutable
6 | /**
7 | * @author piotr 20.02.15
8 | */
9 | case class HistoricalDataHandler(queue: mutable.Queue[Bar] = mutable.Queue.empty[Bar]) extends Handler
10 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/IApiEnum.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public interface IApiEnum {
7 | String getApiString();
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/PositionHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.Position
4 |
5 | import scala.collection.mutable
6 |
7 | /**
8 | * @author piotr 20.02.15
9 | */
10 | case class PositionHandler(queue: mutable.Queue[Position] = mutable.Queue.empty[Position]) extends Handler
11 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/OpenOrdersHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.OpenOrder
4 |
5 | import scala.collection.mutable
6 |
7 | /**
8 | * @author piotr 20.02.15
9 | */
10 | case class OpenOrdersHandler(openOrders: mutable.Map[Int, OpenOrder] = mutable.Map.empty[Int, OpenOrder]) extends Handler
11 |
--------------------------------------------------------------------------------
/src/test/scala/com/larroy/ibclient/util/UtilSpec.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import org.specs2.mutable._
4 |
5 | class UtilSpec extends Specification {
6 | "dateEpoch_s" should {
7 | "return seconds since the epoch" in {
8 | dateEpoch_s("20150303") shouldEqual(1425340800)
9 | dateEpoch_s("1425337200") shouldEqual(1425337200)
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/ContractDetailsHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.ib.client.ContractDetails
4 |
5 | import scala.collection.mutable
6 |
7 | /**
8 | * @author piotr 20.02.15
9 | */
10 | case class ContractDetailsHandler(
11 | details: mutable.ArrayBuffer[ContractDetails] = mutable.ArrayBuffer.empty[ContractDetails]
12 | ) extends Handler
13 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/ExecutionStatus.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order
2 |
3 | /**
4 | * Created by piotr on 6/13/15.
5 | */
6 | object ExecutionStatus extends Enumeration {
7 | type ExecutionStatus = Value
8 | val
9 | PendingSubmit,
10 | PendingCancel,
11 | PreSubmitted,
12 | Submitted,
13 | Cancelled,
14 | Filled,
15 | Inactive = Value
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/OrderStatus.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 | import com.larroy.ibclient.order.ExecutionStatus._
3 |
4 | /**
5 | * Created by piotr on 6/13/15.
6 | */
7 | case class OrderStatus(orderId: Int, status: ExecutionStatus, filled: Int, remaining: Int, avgFillPrice: Double,
8 | permId: Int, parentId: Int, lastFilledPrice: Double, clientId: Int, whyHeld: String)
9 |
--------------------------------------------------------------------------------
/src/test/scala/com/larroy/ibclient/util/CaseClassBeautifierSpec.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import org.specs2.mutable._
4 |
5 | case class C(a: String, b: Int)
6 |
7 | class CaseClassBeautifierSpec extends Specification {
8 | "CaseClassBeautifierSpec" should {
9 | "do" in {
10 | val c = C("h", 1)
11 | CaseClassBeautifier(c) should_==("C(a = h, b = 1)")
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/account/Position.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.account
2 |
3 | import com.ib.client.Contract
4 |
5 | /**
6 | * @author piotr 08.04.15
7 | */
8 | case class Position(
9 | contract: Contract,
10 | position: Int,
11 | marketPrice: Double,
12 | marketValue: Double,
13 | averageCost: Double,
14 | unrealizedPNL: Double,
15 | realizedPNL: Double,
16 | accountName: String)
17 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/account/AccountUpdate.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.account
2 |
3 | import scala.collection.mutable
4 | /**
5 | * @author piotr 08.04.15
6 | */
7 | case class AccountUpdate(
8 | positions: mutable.ArrayBuffer[Position] = mutable.ArrayBuffer.empty[Position],
9 | accountInfo: mutable.Map[String, Value] = mutable.Map.empty[String,Value],
10 | netLiquidation: Value = Value(-1, "USD")
11 | )
12 |
13 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/account/AccountUpdateSubscription.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.account
2 |
3 | import com.larroy.ibclient.IBClient
4 | import rx.lang.scala.Observable
5 | /**
6 | * @author piotr 08.04.15
7 | */
8 | case class AccountUpdateSubscription(iBClient: IBClient, observableAccountUpdate: Observable[AccountUpdate]) {
9 |
10 | def close(): Unit = {
11 | iBClient.closeAccountUpdateSubscription()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/contract/StockContract.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.contract
2 |
3 | import com.ib.client.Contract
4 | import com.ib.client.Types.SecType
5 |
6 | /**
7 | * @author piotr 10.02.15
8 | */
9 | class StockContract(symbol: String, exchange: String = "SMART", currency: String = "USD") extends Contract {
10 | symbol(symbol)
11 | secType(SecType.STK.name())
12 | exchange(exchange)
13 | currency(currency)
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/MarketDataHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.{Tick, MarketDataSubscription}
4 | import rx.lang.scala.Subject
5 |
6 | /**
7 | * @author piotr 20.02.15
8 | */
9 | case class MarketDataHandler(subscription: MarketDataSubscription, subject: Subject[Tick]) extends Handler {
10 | override def error(throwable: Throwable): Unit = {
11 | subject.onError(throwable)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/RealtimeBarsHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.{Bar, RealtimeBarsSubscription}
4 | import rx.lang.scala.Subject
5 |
6 | /**
7 | * Created by piotr on 23/03/15.
8 | */
9 | case class RealtimeBarsHandler(subscription: RealtimeBarsSubscription, subject: Subject[Bar]) extends Handler {
10 | override def error(throwable: Throwable): Unit = {
11 | subject.onError(throwable)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/contract/CashContract.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.contract
2 |
3 | import com.ib.client.Contract
4 | import com.ib.client.Types.SecType
5 |
6 | /**
7 | * @author piotr 16.02.15
8 | */
9 | class CashContract(symbol: String, localSymbol: String, exchange: String = "IDEALPRO", currency: String = "USD") extends Contract {
10 | symbol(symbol)
11 | localSymbol(localSymbol)
12 | secType(SecType.CASH.name())
13 | exchange(exchange)
14 | currency(currency)
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/Bar.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | /**
4 | * @author piotr 10.02.15
5 | * @param time seconds since epoch 1/1/1970 GMT.
6 | * @param count number of trades that occurred
7 | * @param wap is weighted average price during the time of the bar
8 | */
9 | case class Bar(time: Long, high: Double, low: Double, open: Double, close: Double, volume: Int, count: Int, wap: Double, hasGaps: Boolean) extends Ordered[Bar] {
10 | def compare(that: Bar): Int = time.compare(that.time)
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/contract/GenericContract.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.contract
2 |
3 | import com.ib.client.Contract
4 | import com.ib.client.Types.SecType
5 |
6 |
7 | // TODO: create case classes for other contract types
8 |
9 | class GenericContract(
10 | sectTypeEnum: SecType,
11 | symbol: String,
12 | exchange: String = "IDEALPRO",
13 | currency: String = "USD") extends Contract {
14 |
15 | symbol(symbol)
16 | secType(sectTypeEnum.name())
17 | exchange(exchange)
18 | currency(currency)
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/handler/AccountUpdateHandler.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.handler
2 |
3 | import com.larroy.ibclient.account.{AccountUpdate, AccountUpdateSubscription}
4 | import rx.lang.scala.Subject
5 |
6 | /**
7 | * @author piotr 08.04.15
8 | */
9 | case class AccountUpdateHandler(accountUpdateSubscription: AccountUpdateSubscription, subject: Subject[AccountUpdate]) extends Handler {
10 | val nextAccountUpdate = new AccountUpdate()
11 | override def error(throwable: Throwable): Unit = {
12 | subject.onError(throwable)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/contracts/StkContract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.contracts;
5 |
6 | import com.ib.client.Contract;
7 | import com.ib.client.Types.SecType;
8 |
9 | public class StkContract extends Contract {
10 | public StkContract(String symbol) {
11 | symbol(symbol);
12 | secType(SecType.STK.name());
13 | exchange("SMART");
14 | currency("USD");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/TradeId.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | public class TradeId {
7 | private String m_key;
8 | private String m_full;
9 |
10 | public String key() { return m_key; }
11 | public String full() { return m_full; }
12 |
13 | public TradeId( String id) {
14 | m_full = id;
15 | int i = id.lastIndexOf( '.');
16 | m_key = id.substring( i + 1);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Instrument.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | public enum Instrument {
7 | STK,
8 | BOND,
9 | EFP,
10 | FUT_EU,
11 | FUT_HK,
12 | FUT_NA,
13 | FUT_US,
14 | IND_EU,
15 | IND_HK,
16 | IND_US,
17 | PMONITOR,
18 | PMONITORM,
19 | SLB_US,
20 | STOCK_EU,
21 | STOCK_HK,
22 | STOCK_NA,
23 | WAR_EU;
24 |
25 | public String toString() {
26 | return super.toString().replace( '_', '.');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/contract/FutureContract.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.contract
2 |
3 | import com.ib.client.Contract
4 | import com.ib.client.Types.SecType
5 |
6 |
7 | /**
8 | * @param symbol
9 | * @param expiry Expiry date in format yyyymmdd identifies the futures contract
10 | * @param exchange for example NYMEX for commodity futures
11 | * @param currency in which the future is denominated
12 | */
13 | class FutureContract(symbol: String, expiry: String, exchange: String = "GLOBEX", currency: String = "USD") extends Contract {
14 | symbol(symbol)
15 | expiry(expiry)
16 | secType(SecType.FUT.name())
17 | exchange(exchange)
18 | currency(currency)
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Alias.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | public class Alias {
7 | private String m_account;
8 | private String m_alias;
9 |
10 | public String alias() { return m_alias; }
11 | public String account() { return m_account; }
12 |
13 | public void alias( String v) { m_alias = v; }
14 | public void account( String v) { m_account = v; }
15 |
16 | @Override public String toString() {
17 | return m_account + " / " + m_alias;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/MarketDataSubscription.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import com.ib.client.Contract
4 | import rx.lang.scala.Observable
5 |
6 | /**
7 | * Market data line returned by call to [[IBClient]] method marketData.
8 | * This class is not intended to be instantiated by the user of [[IBClient]]
9 | */
10 | case class MarketDataSubscription(ibclient: IBClient, id: Int, contract: Contract, observableTick: Observable[Tick]) {
11 | /**
12 | * Close this market data line and free all associated resources. There's no need to call any [[IBClient]] additional methods
13 | */
14 | def close(): Unit = {
15 | ibclient.closeMarketData(id)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/contracts/FutContract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.contracts;
5 |
6 | import com.ib.client.Contract;
7 | import com.ib.client.Types.SecType;
8 |
9 | public class FutContract extends Contract {
10 | public FutContract(String symbol, String expiry) {
11 | symbol(symbol);
12 | secType(SecType.FUT);
13 | exchange("ONE");
14 | currency("USD");
15 | }
16 |
17 | public FutContract(String symbol, String expiry, String currency) {
18 | symbol(symbol);
19 | secType(SecType.FUT.name());
20 | currency(currency);
21 | expiry(expiry);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/contracts/ComboContract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.contracts;
5 |
6 | import com.ib.client.Contract;
7 | import com.ib.client.Types.SecType;
8 |
9 | public class ComboContract extends Contract {
10 | public ComboContract(String symbol) {
11 | this(symbol, "USD", "SMART");
12 | }
13 |
14 | public ComboContract(String symbol, String currency) {
15 | this(symbol, currency, "SMART");
16 | }
17 |
18 | public ComboContract(String symbol, String currency, String exchange) {
19 | symbol(symbol);
20 | secType(SecType.BAG.name());
21 | currency(currency);
22 | exchange(exchange);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/contracts/OptContract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.contracts;
5 |
6 | import com.ib.client.Contract;
7 | import com.ib.client.Types.SecType;
8 |
9 | public class OptContract extends Contract {
10 | public OptContract(String symbol, String expiry, double strike, String right) {
11 | this(symbol, "SMART", expiry, strike, right);
12 | }
13 |
14 | public OptContract(String symbol, String exchange, String expiry, double strike, String right) {
15 | symbol(symbol);
16 | secType(SecType.OPT.name());
17 | exchange(exchange);
18 | currency("USD");
19 | expiry(expiry);
20 | strike(strike);
21 | right(right);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/RealtimeBarsSubscription.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import com.ib.client.Contract
4 | import rx.lang.scala.Observable
5 |
6 | /**
7 | * This class represents a market data line (realtimeBar) for a given contract. It's returned by a call
8 | * to the realtimeBars method of [[IBClient]]. It's not to be instatiated by the user of [[IBClient]]
9 | * @param ibclient
10 | * @param id request Id of this subscription
11 | * @param contract
12 | * @param observableBar
13 | */
14 | case class RealtimeBarsSubscription (ibclient: IBClient, id: Int, contract: Contract, observableBar: Observable[Bar]) {
15 | /**
16 | * Close this subscription freeing resources and closing the market data line if there's any
17 | * There's no need to call any [[IBClient]] additional methods after this call is made.
18 | */
19 | def close(): Unit = {
20 | ibclient.closeRealtimeBar(id)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/OrderStatus.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public enum OrderStatus {
8 | ApiPending,
9 | ApiCancelled,
10 | PreSubmitted,
11 | PendingCancel,
12 | Cancelled,
13 | Submitted,
14 | Filled,
15 | Inactive,
16 | PendingSubmit,
17 | Unknown;
18 |
19 | public static OrderStatus get(String apiString) {
20 | for( OrderStatus type : values() ) {
21 | if( type.name().equalsIgnoreCase(apiString) ) {
22 | return type;
23 | }
24 | }
25 | return Unknown;
26 | }
27 |
28 | public boolean isActive() {
29 | return this == PreSubmitted || this == PendingCancel || this == Submitted || this == PendingSubmit;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/CaseClassBeautifier.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import scala.reflect.ClassTag
4 | import scala.reflect.runtime.universe._
5 |
6 | object CaseClassBeautifier {
7 | def getCaseAccessors[T: TypeTag] = typeOf[T].members.collect {
8 | case m: MethodSymbol if m.isCaseAccessor => m
9 | }.toList
10 |
11 | def apply[T:TypeTag](x: T)(implicit classTag: ClassTag[T]) : String = {
12 | val instance = x.asInstanceOf[T]
13 | val mirror = runtimeMirror(instance.getClass.getClassLoader)
14 | val accessors = getCaseAccessors[T]
15 | var res = List.empty[String]
16 | accessors.foreach { z ⇒
17 | val instanceMirror = mirror.reflect(instance)
18 | val fieldMirror = instanceMirror.reflectField(z.asTerm)
19 | val s = s"${z.name} = ${fieldMirror.get}"
20 | res = s :: res
21 | }
22 | val beautified = x.getClass.getSimpleName + "(" + res.mkString(", ") + ")"
23 | beautified
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/TagValue.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class TagValue {
7 |
8 | public String m_tag;
9 | public String m_value;
10 |
11 | public TagValue() {
12 | }
13 |
14 | public TagValue(String p_tag, String p_value) {
15 | m_tag = p_tag;
16 | m_value = p_value;
17 | }
18 |
19 | public boolean equals(Object p_other) {
20 |
21 | if( this == p_other)
22 | return true;
23 |
24 | if( p_other == null)
25 | return false;
26 |
27 | TagValue l_theOther = (TagValue)p_other;
28 |
29 | if( Util.StringCompare(m_tag, l_theOther.m_tag) != 0 ||
30 | Util.StringCompare(m_value, l_theOther.m_value) != 0) {
31 | return false;
32 | }
33 |
34 | return true;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/HistoricalRequest.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import java.util.Date
4 |
5 | import com.ib.client.Types.{DurationUnit, BarSize}
6 |
7 | /**
8 | * @author piotr 01.03.15
9 | */
10 | case class HistoricalRequest(contract: String, exchange: String, endDate: Date, duration: Int, durationUnit: DurationUnit, barSize: BarSize) extends Ordered[HistoricalRequest] {
11 | //import scala.math.Ordered.orderingToOrdered
12 | def compare(that: HistoricalRequest): Int = {
13 | //(this.contract, this.exchange, this.durationUnit, this.barSize, this.duration) compare (that.contract, that.exchange, that.durationUnit, that.barSize, that.duration)
14 | Ordering[(String, String, Date, Int, DurationUnit, BarSize)].compare(
15 | (this.contract, this.exchange, this.endDate, this.duration, this.durationUnit, this.barSize),
16 | (that.contract, that.exchange, that.endDate, that.duration, that.durationUnit, that.barSize)
17 | )
18 | }
19 |
20 |
21 |
22 |
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 | ibclient.log
11 | true
12 |
13 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/OrderComboLeg.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public class OrderComboLeg {
8 | private double m_price; // price per leg
9 |
10 | public double price() { return m_price; }
11 | public void price(double v) { m_price = v; }
12 |
13 | public OrderComboLeg() {
14 | m_price = Double.MAX_VALUE;
15 | }
16 |
17 | public OrderComboLeg(double p_price) {
18 | m_price = p_price;
19 | }
20 |
21 | public boolean equals(Object p_other) {
22 | if ( this == p_other ) {
23 | return true;
24 | }
25 | else if ( p_other == null ) {
26 | return false;
27 | }
28 |
29 | OrderComboLeg l_theOther = (OrderComboLeg)p_other;
30 |
31 | if (m_price != l_theOther.m_price) {
32 | return false;
33 | }
34 | return true;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/MarketDataType.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class MarketDataType {
7 | // constants - market data types
8 | public static final int REALTIME = 1;
9 | public static final int FROZEN = 2;
10 |
11 | public static String getField( int marketDataType) {
12 | switch( marketDataType) {
13 | case REALTIME: return "Real-Time";
14 | case FROZEN: return "Frozen";
15 |
16 | default: return "Unknown";
17 | }
18 | }
19 |
20 | public static String[] getFields(){
21 | int totalFields = MarketDataType.class.getFields().length;
22 | String [] fields = new String[totalFields];
23 | for (int i = 0; i < totalFields; i++){
24 | fields[i] = MarketDataType.getField(i + 1);
25 | }
26 | return fields;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/CommissionReport.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class CommissionReport {
7 |
8 | public String m_execId;
9 | public double m_commission;
10 | public String m_currency;
11 | public double m_realizedPNL;
12 | public double m_yield;
13 | public int m_yieldRedemptionDate; // YYYYMMDD format
14 |
15 | public CommissionReport() {
16 | m_commission = 0;
17 | m_realizedPNL = 0;
18 | m_yield = 0;
19 | m_yieldRedemptionDate = 0;
20 | }
21 |
22 | public boolean equals(Object p_other) {
23 | boolean l_bRetVal = false;
24 |
25 | if ( p_other == null ) {
26 | l_bRetVal = false;
27 | }
28 | else if ( this == p_other ) {
29 | l_bRetVal = true;
30 | }
31 | else {
32 | CommissionReport l_theOther = (CommissionReport)p_other;
33 | l_bRetVal = m_execId.equals( l_theOther.m_execId);
34 | }
35 | return l_bRetVal;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/MarketValueTag.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import com.ib.client.Types;
7 |
8 | public enum MarketValueTag {
9 | NetLiquidationByCurrency,
10 | CashBalance,
11 | TotalCashBalance,
12 | AccruedCash,
13 | StockMarketValue,
14 | OptionMarketValue,
15 | FutureOptionValue,
16 | FuturesPNL,
17 | UnrealizedPnL,
18 | RealizedPnL,
19 | ExchangeRate,
20 | FundValue,
21 | NetDividend,
22 | MutualFundValue,
23 | MoneyMarketFundValue,
24 | CorporateBondValue,
25 | TBondValue,
26 | TBillValue,
27 | WarrantValue,
28 | FxCashBalance;
29 |
30 | public static MarketValueTag get( int i) {
31 | return Types.getEnum( i, values() );
32 | }
33 |
34 | @Override public String toString() {
35 | switch( this) {
36 | case NetLiquidationByCurrency: return "Net Liq";
37 | case StockMarketValue: return "Stocks";
38 | case OptionMarketValue: return "Options";
39 | case FutureOptionValue: return "Futures";
40 | }
41 | return super.toString().replaceAll("Value", "");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | IB API software subject to the terms of license here: http://interactivebrokers.github.io/
2 |
3 |
4 | The Scala client is licensed under the MIT license.
5 |
6 |
7 | Copyright 2020 Pedro Larroy
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | End license text.
16 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Group.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.util.ArrayList;
7 | import java.util.StringTokenizer;
8 |
9 | import com.ib.client.Types.Method;
10 |
11 | public class Group {
12 | private String m_name;
13 | private Method m_defaultMethod;
14 | private ArrayList m_accounts = new ArrayList();
15 |
16 | public String name() { return m_name; }
17 | public Method defaultMethod() { return m_defaultMethod; }
18 | public ArrayList accounts() { return m_accounts; }
19 |
20 | public void name( String v) { m_name = v; }
21 | public void defaultMethod( Method v) { m_defaultMethod = v; }
22 | public void addAccount( String acct) { m_accounts.add( acct); }
23 |
24 | /** @param val is a comma or space delimited string of accounts */
25 | public void setAllAccounts(String val) {
26 | m_accounts.clear();
27 |
28 | StringTokenizer st = new StringTokenizer( val, " ,");
29 | while( st.hasMoreTokens() ) {
30 | m_accounts.add( st.nextToken() );
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/ConcurrentHashSet.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.util.AbstractSet;
7 | import java.util.Iterator;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | public class ConcurrentHashSet extends AbstractSet {
11 | static Object OBJECT = new Object();
12 |
13 | private ConcurrentHashMap m_map = new ConcurrentHashMap(16,0.75f,1); // use write concurrency level 1 (last param) to decrease memory consumption by ConcurrentHashMap
14 |
15 | /** return true if object was added as "first value" for this key */
16 | public boolean add( Key key) {
17 | return m_map.put( key, OBJECT) == null; // null means there was no value for given key previously
18 | }
19 |
20 | public boolean contains( Object key) {
21 | return m_map.containsKey( key);
22 | }
23 |
24 | public Iterator iterator() {
25 | return m_map.keySet().iterator();
26 | }
27 |
28 | /** return true if key was indeed removed */
29 | public boolean remove( Object key) {
30 | return m_map.remove( key) == OBJECT; // if value not null it was existing in the map
31 | }
32 |
33 | public boolean isEmpty() {
34 | return m_map.isEmpty();
35 | }
36 |
37 | public int size() {
38 | return m_map.size();
39 | }
40 |
41 | public void clear() {
42 | m_map.clear();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | ibclient {
2 | tws {
3 | host = "localhost"
4 | port = 7496
5 | clientId = 3
6 | timeout_s = 60
7 | }
8 | params {
9 | stock {
10 | contract = "SPY"
11 | exchange = "SMART"
12 | currency = "USD"
13 | }
14 | future {
15 | contract = "CL"
16 | exchange = "NYMEX"
17 | currency = "USD"
18 | }
19 | }
20 | historyRequestTimeout = "Inf"
21 | historyRequestPacingViolationRetry {
22 | length = 60
23 | unit = "s"
24 | count = 4
25 | }
26 |
27 | # Max number of bars that can be requested for duration.barsize
28 | historyLimits {
29 | SECOND._5_secs = 10000
30 | SECOND._10_secs = 10000
31 | SECOND._15_secs = 10000
32 | SECOND._30_secs = 10000
33 | SECOND._1_min = 86400
34 | SECOND._2_mins = 86400
35 | SECOND._3_mins = 86400
36 | SECOND._5_mins = 86400
37 | SECOND._10_mins = 86400
38 | SECOND._15_mins = 86400
39 | SECOND._30_mins = 86400
40 | SECOND._1_hour = 86400
41 |
42 | DAY._30_secs = 1
43 | DAY._1_min = 10
44 | DAY._5_mins = 10
45 | DAY._15_mins = 20
46 | DAY._1_hour = 34
47 | DAY._4_hour = 34
48 | DAY._1_day = 60
49 |
50 | WEEK._15_mins = 2
51 | WEEK._1_hour = 4
52 | WEEK._4_hours = 4
53 | WEEK._1_day = 52
54 | WEEK._1_week = 52
55 |
56 | MONTH._1_hour= 1
57 | MONTH._4_hours = 1
58 | MONTH._1_day = 12
59 | MONTH._1_week = 12
60 |
61 | YEAR._1_day = 5
62 | YEAR._1_week = 5
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/DeltaNeutralContract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class DeltaNeutralContract {
7 | private int m_conid;
8 | private double m_delta;
9 | private double m_price;
10 |
11 | // Get
12 | public int conid() { return m_conid; }
13 | public double delta() { return m_delta; }
14 | public double price() { return m_price; }
15 |
16 | // Set
17 | public void conid(int conid) { m_conid = conid; }
18 | public void delta(double delta) { m_delta = delta; }
19 | public void price(double price) { m_price = price; }
20 |
21 | public DeltaNeutralContract() {
22 | m_conid = 0;
23 | m_delta = 0;
24 | m_price = 0;
25 | }
26 |
27 | public DeltaNeutralContract(int conid, double delta, double price) {
28 | m_conid = conid;
29 | m_delta = delta;
30 | m_price = price;
31 | }
32 |
33 | public boolean equals(Object p_other) {
34 | if (this == p_other) {
35 | return true;
36 | }
37 |
38 | if (p_other == null || !(p_other instanceof DeltaNeutralContract)) {
39 | return false;
40 | }
41 |
42 | DeltaNeutralContract l_theOther = (DeltaNeutralContract)p_other;
43 |
44 | if (m_conid != l_theOther.m_conid) {
45 | return false;
46 | }
47 | if (m_delta != l_theOther.m_delta) {
48 | return false;
49 | }
50 | if (m_price != l_theOther.m_price) {
51 | return false;
52 | }
53 |
54 | return true;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/HistoryDuration.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import java.util.Date
4 |
5 | import com.ib.client.Types.DurationUnit
6 | import org.joda.time.DateTime
7 |
8 | /**
9 | * @param total
10 | * @param eachRequest
11 | * @param durationUnit
12 | */
13 | case class HistoryDuration(total: Int, eachRequest: Int, durationUnit: DurationUnit) {
14 | /**
15 | * @return number of requests to perform with the available parameters
16 | */
17 | def numRequests: Int = divRoundAwayZero(total, eachRequest)
18 |
19 | def minusDuration(dateTime: DateTime, amt: Int, durationUnit: DurationUnit): DateTime = {
20 | import com.ib.client.Types.DurationUnit._
21 | durationUnit match {
22 | case SECOND ⇒ dateTime.minusSeconds(amt)
23 | case DAY ⇒ dateTime.minusDays(amt)
24 | case WEEK ⇒ dateTime.minusWeeks(amt)
25 | case MONTH ⇒ dateTime.minusMonths(amt)
26 | case YEAR ⇒ dateTime.minusYears(amt)
27 | }
28 | }
29 |
30 | /**
31 | * @param endDate the end date of the request
32 | * @return a vector of dates to request
33 | */
34 | def endDates(endDate: Date): Vector[Date] = {
35 | val endDateTime = new DateTime(endDate)
36 | Vector.tabulate(numRequests) { reqNum ⇒
37 | minusDuration(endDateTime, reqNum * eachRequest, durationUnit).toDate
38 | }
39 | }
40 |
41 | /**
42 | * @return a vector of durations to request from past to present
43 | */
44 | def durations: Vector[Int] = {
45 | val res = Vector.tabulate(numRequests){ reqNum ⇒
46 | val rem = total % eachRequest
47 | if (reqNum == numRequests - 1 && rem != 0)
48 | rem
49 | else
50 | eachRequest
51 | }
52 | res.foreach { x ⇒ assert(x != 0, "calculated duration can't be 0") }
53 | res
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/AccountSummaryTag.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | public enum AccountSummaryTag {
7 | AccountType,
8 |
9 | // balances
10 | NetLiquidation,
11 | TotalCashValue, // Total cash including futures pnl
12 | SettledCash, // For cash accounts, this is the same as TotalCashValue
13 | AccruedCash, // Net accrued interest
14 | BuyingPower, // The maximum amount of marginable US stocks the account can buy
15 | EquityWithLoanValue, // Cash + stocks + bonds + mutual funds
16 | PreviousEquityWithLoanValue,
17 | GrossPositionValue, // The sum of the absolute value of all stock and equity option positions
18 | RegTEquity,
19 | RegTMargin,
20 | SMA, // Special Memorandum Account
21 |
22 | // current margin
23 | InitMarginReq,
24 | MaintMarginReq,
25 | AvailableFunds,
26 | ExcessLiquidity,
27 | Cushion, // Excess liquidity as a percentage of net liquidation value
28 |
29 | // overnight margin
30 | FullInitMarginReq,
31 | FullMaintMarginReq,
32 | FullAvailableFunds,
33 | FullExcessLiquidity,
34 |
35 | // look-ahead margin
36 | LookAheadNextChange, // Time when look-ahead values take effect
37 | LookAheadInitMarginReq,
38 | LookAheadMaintMarginReq,
39 | LookAheadAvailableFunds,
40 | LookAheadExcessLiquidity,
41 |
42 | // misc
43 | HighestSeverity, // A measure of how close the account is to liquidation
44 | DayTradesRemaining, // The Number of Open/Close trades one could do before Pattern Day Trading is detected; a value of "-1" means user can do unlimited day trades.
45 | Leverage, // GrossPositionValue / NetLiquidation
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Bar.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.text.SimpleDateFormat;
7 | import java.util.Date;
8 |
9 | public class Bar {
10 | private static final SimpleDateFormat FORMAT = new SimpleDateFormat( "yyyyMMdd HH:mm:ss"); // format for historical query
11 |
12 | private final long m_time;
13 | private final double m_high;
14 | private final double m_low;
15 | private final double m_open;
16 | private final double m_close;
17 | private final double m_wap;
18 | private final long m_volume;
19 | private final int m_count;
20 |
21 | public long time() { return m_time; }
22 | public double high() { return m_high; }
23 | public double low() { return m_low; }
24 | public double open() { return m_open; }
25 | public double close() { return m_close; }
26 | public double wap() { return m_wap; }
27 | public long volume() { return m_volume; }
28 | public int count() { return m_count; }
29 |
30 | public Bar( long time, double high, double low, double open, double close, double wap, long volume, int count) {
31 | m_time = time;
32 | m_high = high;
33 | m_low = low;
34 | m_open = open;
35 | m_close = close;
36 | m_wap = wap;
37 | m_volume = volume;
38 | m_count = count;
39 | }
40 |
41 | public String formattedTime() {
42 | return Formats.fmtDate( m_time * 1000);
43 | }
44 |
45 | /** Format for query. */
46 | public static String format( long ms) {
47 | return FORMAT.format( new Date( ms) );
48 | }
49 |
50 | @Override public String toString() {
51 | return String.format( "%s %s %s %s %s", formattedTime(), m_open, m_high, m_low, m_close);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/OrderType.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public enum OrderType implements IApiEnum {
8 | None( ""),
9 | MKT( "MKT"),
10 | LMT( "LMT"),
11 | STP( "STP"),
12 | STP_LMT( "STP LMT"),
13 | REL( "REL"),
14 | TRAIL( "TRAIL"),
15 | BOX_TOP( "BOX TOP"),
16 | FIX_PEGGED( "FIX PEGGED"),
17 | LIT( "LIT"),
18 | LMT_PLUS_MKT( "LMT + MKT"),
19 | LOC( "LOC"),
20 | MIT( "MIT"),
21 | MKT_PRT( "MKT PRT"),
22 | MOC( "MOC"),
23 | MTL( "MTL"),
24 | PASSV_REL( "PASSV REL"),
25 | PEG_BENCH( "PEG BENCH"),
26 | PEG_MID( "PEG MID"),
27 | PEG_MKT( "PEG MKT"),
28 | PEG_PRIM( "PEG PRIM"),
29 | PEG_STK( "PEG STK"),
30 | REL_PLUS_LMT( "REL + LMT"),
31 | REL_PLUS_MKT( "REL + MKT"),
32 | STP_PRT( "STP PRT"),
33 | TRAIL_LIMIT( "TRAIL LIMIT"),
34 | TRAIL_LIT( "TRAIL LIT"),
35 | TRAIL_LMT_PLUS_MKT( "TRAIL LMT + MKT"),
36 | TRAIL_MIT( "TRAIL MIT"),
37 | TRAIL_REL_PLUS_MKT( "TRAIL REL + MKT"),
38 | VOL( "VOL"),
39 | VWAP( "VWAP"),
40 | QUOTE("QUOTE");
41 |
42 | private String m_apiString;
43 |
44 | private OrderType( String apiString) {
45 | m_apiString = apiString;
46 | }
47 |
48 | public static OrderType get(String apiString) {
49 | if (apiString != null && apiString.length() > 0 && !apiString.equals( "None") ) {
50 | for (OrderType type : values() ) {
51 | if (type.m_apiString.equals( apiString) ) {
52 | return type;
53 | }
54 | }
55 | }
56 | return None;
57 | }
58 |
59 | @Override public String toString() {
60 | return this == None ? super.toString() : m_apiString;
61 | }
62 |
63 | @Override public String getApiString() {
64 | return m_apiString;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Formats.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.text.DecimalFormat;
7 | import java.text.Format;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Date;
10 |
11 | public class Formats {
12 | private static final Format FMT2 = new DecimalFormat( "#,##0.00");
13 | private static final Format FMT0 = new DecimalFormat( "#,##0");
14 | private static final Format PCT = new DecimalFormat( "0.0%");
15 | private static final SimpleDateFormat DATE_TIME = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); // format for display
16 | private static final SimpleDateFormat TIME = new SimpleDateFormat( "HH:mm:ss"); // format for display
17 |
18 | /** Format with two decimals. */
19 | public static String fmt( double v) {
20 | return v == Double.MAX_VALUE ? null : FMT2.format( v);
21 | }
22 |
23 | /** Format with two decimals; return null for zero. */
24 | public static String fmtNz( double v) {
25 | return v == Double.MAX_VALUE || v == 0 ? null : FMT2.format( v);
26 | }
27 |
28 | /** Format with no decimals. */
29 | public static String fmt0( double v) {
30 | return v == Double.MAX_VALUE ? null : FMT0.format( v);
31 | }
32 |
33 | /** Format as percent with one decimal. */
34 | public static String fmtPct( double v) {
35 | return v == Double.MAX_VALUE ? null : PCT.format( v);
36 | }
37 |
38 | /** Format date/time for display. */
39 | public static String fmtDate( long ms) {
40 | return DATE_TIME.format( new Date( ms) );
41 | }
42 |
43 | /** Format time for display. */
44 | public static String fmtTime( long ms) {
45 | return TIME.format( new Date( ms) );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/scala/com/larroy/ibclient/util/HistoricalRateLimiterSpec.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | /**
4 | * @author piotr 01.03.15
5 | */
6 |
7 | import java.util.Date
8 |
9 | import com.ib.client.Types.{BarSize, DurationUnit}
10 | import org.specs2.mutable._
11 |
12 | class HistoricalRateLimiterSpec extends Specification {
13 | "HistoricalRateLimiterSpec" should {
14 | "limit requests" in {
15 | val rl = new HistoricalRateLimiter
16 | rl.nextSlot_ms(10, 2) mustEqual 0L
17 | val request = new HistoricalRequest("SPY", "GLOBEX", new Date(), 5, DurationUnit.DAY, BarSize._10_mins)
18 |
19 | val now = rl.now_ms
20 | rl.requested(request, Some(now))
21 |
22 | // we have one request, the next slot is after 1000 ms
23 | rl.nextSlot_ms(1000, 1, None, now) mustEqual 1000L
24 | // we can have another request right away
25 | rl.nextSlot_ms(1000, 2, None, now) mustEqual 0L
26 |
27 | rl.requested(request)
28 | rl.nextSlot_ms(1000, 2, None, now) must beGreaterThan(0L)
29 | rl.nextSlot_ms(1000, 3, None, now) mustEqual 0L
30 | }
31 | "honor ib limits" in {
32 | val rl = new HistoricalRateLimiter
33 | val date = new Date()
34 | val request = new HistoricalRequest("SPY", "GLOBEX", date, 5, DurationUnit.DAY, BarSize._10_mins)
35 | rl.nextRequestAfter_ms(request) mustEqual 0L
36 | rl.requested(request)
37 | rl.nextRequestAfter_ms(request) must beGreaterThan(0L)
38 |
39 | val request2 = new HistoricalRequest("CL", "NYMEX", date, 5, DurationUnit.DAY, BarSize._10_mins)
40 | rl.nextRequestAfter_ms(request2) mustEqual 0L
41 | rl.requested(request2)
42 | rl.nextRequestAfter_ms(request2) must beGreaterThan(0L)
43 |
44 | rl.cleanupAfter(rl.now_ms)
45 | rl.nextRequestAfter_ms(request2) mustEqual 0L
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/LocationCode.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | // BOND.US
7 | // EFP
8 | // FUT.ECBOT
9 | // FUT.EU.BELFOX
10 | // FUT.EU.DTB
11 | // FUT.EU.FTA
12 | // FUT.EU.IDEM
13 | // FUT.EU.LIFFE
14 | // FUT.EU.MEFFRV
15 | // FUT.EU.MONEP
16 | // FUT.EU
17 | // FUT.GLOBEX
18 | // FUT.HK.HKFE
19 | // FUT.HK.JAPAN
20 | // FUT.HK.KSE
21 | // FUT.HK.NSE
22 | // FUT.HK.OSE.JPN
23 | // FUT.HK.SGX
24 | // FUT.HK.SNFE
25 | // FUT.HK.TSE.JPN
26 | // FUT.HK
27 | // FUT.IPE
28 | // FUT.NA.CDE
29 | // FUT.NA
30 | // FUT.NYBOT
31 | // FUT.NYMEX
32 | // FUT.NYSELIFFE
33 | // FUT.US
34 | // IND.EU.BELFOX
35 | // IND.EU.DTB
36 | // IND.EU.FTA
37 | // IND.EU.LIFFE
38 | // IND.EU.MONEP
39 | // IND.EU
40 | // IND.HK.HKFE
41 | // IND.HK.JAPAN
42 | // IND.HK.KSE
43 | // IND.HK.NSE
44 | // IND.HK.OSE.JPN
45 | // IND.HK.SGX
46 | // IND.HK.SNFE
47 | // IND.HK.TSE.JPN
48 | // IND.HK
49 | // IND.US
50 | // SLB.AQS
51 | // STK.AMEX
52 | // STK.ARCA
53 | // STK.EU.AEB
54 | // STK.EU.BM
55 | // STK.EU.BVME
56 | // STK.EU.EBS
57 | // STK.EU.IBIS
58 | // STK.EU.IBIS-ETF
59 | // STK.EU.IBIS-EUSTARS
60 | // STK.EU.IBIS-NEWX
61 | // STK.EU.IBIS-USSTARS
62 | // STK.EU.IBIS-XETRA
63 | // STK.EU.LSE
64 | // STK.EU.SBF
65 | // STK.EU.SBVM
66 | // STK.EU.SFB
67 | // STK.EU.SWISS
68 | // STK.EU.VIRTX
69 | // STK.EU
70 | // STK.HK.ASX
71 | // STK.HK.NSE
72 | // STK.HK.SEHK
73 | // STK.HK.SGX
74 | // STK.HK.TSE.JPN
75 | // STK.HK
76 | // STK.NA.CANADA
77 | // STK.NA.TSE
78 | // STK.NA.VENTURE
79 | // STK.NA
80 | // STK.NASDAQ.NMS
81 | // STK.NASDAQ.SCM
82 | // STK.NASDAQ
83 | // STK.NYSE
84 | // STK.OTCBB
85 | // STK.PINK
86 | // STK.US.MAJOR
87 | // STK.US.MINOR
88 | // STK.US
89 | // WAR.EU.ALL
90 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Position.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import com.ib.client.Contract;
7 |
8 |
9 | public class Position {
10 | private Contract m_contract;
11 | private String m_account;
12 | private int m_position;
13 | private double m_marketPrice;
14 | private double m_marketValue;
15 | private double m_averageCost;
16 | private double m_unrealPnl;
17 | private double m_realPnl;
18 |
19 | public Contract contract() { return m_contract; }
20 | public int conid() { return m_contract.conid(); }
21 | public double averageCost() { return m_averageCost;}
22 | public double marketPrice() { return m_marketPrice;}
23 | public double marketValue() { return m_marketValue;}
24 | public double realPnl() { return m_realPnl;}
25 | public double unrealPnl() { return m_unrealPnl;}
26 | public int position() { return m_position;}
27 | public String account() { return m_account;}
28 |
29 | // public void account(String v) { m_account = v;}
30 | // public void averageCost(double v) { m_averageCost = v;}
31 | // public void marketPrice(double v) { m_marketPrice = v;}
32 | // public void marketValue(double v) { m_marketValue = v;}
33 | // public void position(int v) { m_position = v;}
34 | // public void realPnl(double v) { m_realPnl = v;}
35 | // public void unrealPnl(double v) { m_unrealPnl = v;}
36 |
37 | public Position( Contract contract, String account, int position, double marketPrice, double marketValue, double averageCost, double unrealPnl, double realPnl) {
38 | m_contract = contract;
39 | m_account = account;
40 | m_position = position;
41 | m_marketPrice = marketPrice;
42 | m_marketValue =marketValue;
43 | m_averageCost = averageCost;
44 | m_unrealPnl = unrealPnl;
45 | m_realPnl = realPnl;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/Profile.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.util.ArrayList;
7 | import java.util.StringTokenizer;
8 |
9 | import com.ib.client.Types;
10 |
11 | public class Profile {
12 | static final String SEP = "/";
13 |
14 | private String m_name;
15 | private Type m_type;
16 | private ArrayList m_allocations = new ArrayList();
17 |
18 | public String name() { return m_name; }
19 | public Type type() { return m_type; }
20 | public ArrayList allocations() { return m_allocations; }
21 |
22 | public void name( String v) { m_name = v; }
23 | public void type( Type v) { m_type = v; }
24 | public void add( Allocation v) { m_allocations.add( v); }
25 |
26 | public void setAllocations(String val) {
27 | m_allocations.clear();
28 |
29 | StringTokenizer st = new StringTokenizer( val, ", ");
30 | while( st.hasMoreTokens() ) {
31 | String tok = st.nextToken();
32 | StringTokenizer st2 = new StringTokenizer( tok, SEP);
33 |
34 | Allocation alloc = new Allocation();
35 | alloc.account( st2.nextToken() );
36 | alloc.amount( st2.nextToken() );
37 |
38 | m_allocations.add( alloc);
39 | }
40 | }
41 |
42 | public static enum Type {
43 | NONE, Percents, Ratios, Shares;
44 |
45 | public static Type get( int ordinal) {
46 | return Types.getEnum( ordinal, values() );
47 | }
48 | };
49 |
50 | public static class Allocation {
51 | private String m_account;
52 | private String m_amount;
53 |
54 | public String account() { return m_account; }
55 | public String amount() { return m_amount; }
56 |
57 | public void account( String v) { m_account = v; }
58 | public void amount( String v) { m_amount = v; }
59 |
60 | @Override public String toString() {
61 | return m_account + SEP + m_amount;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/order/Order.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.order
2 |
3 | import com.ib.client.{Order ⇒ IBOrder}
4 | import com.larroy.ibclient.order.kind.Kind
5 |
6 | /**
7 | * @author piotr 19.02.15
8 | */
9 | trait Order {
10 | def kind: Kind = ???
11 | def quantity: Int = ???
12 | def toIBOrder: IBOrder = {
13 | import com.larroy.ibclient.order.kind._
14 | val iBOrder = new IBOrder()
15 | this match {
16 | case Buy(_, qty) ⇒ {
17 | iBOrder.action("BUY")
18 | iBOrder.totalQuantity(qty)
19 | }
20 | case Sell(_, qty) ⇒ {
21 | iBOrder.action("SELL")
22 | iBOrder.totalQuantity(qty)
23 | }
24 | }
25 | this.kind match {
26 | case Limit(limit) ⇒ {
27 | iBOrder.orderType("LMT")
28 | iBOrder.lmtPrice(limit)
29 | iBOrder.auxPrice(0)
30 | }
31 | case Market() ⇒ {
32 | iBOrder.orderType("MKT")
33 | }
34 | case Stop(stop) ⇒ {
35 | iBOrder.orderType("STP")
36 | iBOrder.lmtPrice(0)
37 | iBOrder.auxPrice(stop)
38 | }
39 | case StopLimit(stop, limit) ⇒ {
40 | iBOrder.orderType("STPLMT")
41 | iBOrder.lmtPrice(limit)
42 | iBOrder.auxPrice(stop)
43 | }
44 | case TrailStop(stop) ⇒ {
45 | iBOrder.orderType("TRAIL")
46 | iBOrder.lmtPrice(0)
47 | iBOrder.auxPrice(0)
48 | }
49 | case TrailStopLimit(stop, trail) ⇒ {
50 | iBOrder.orderType("TRAIL LIMIT")
51 | iBOrder.lmtPrice(0)
52 | iBOrder.auxPrice(stop)
53 | iBOrder.trailStopPrice(trail)
54 | }
55 | case TrailLimitIfTouched(stop, limit, trail) ⇒ {
56 | iBOrder.orderType("TRAIL LIT")
57 | iBOrder.trailStopPrice(trail)
58 | iBOrder.lmtPrice(limit)
59 | iBOrder.auxPrice(stop)
60 | }
61 | case TrailMarketIfTouched(stop, trail) ⇒ {
62 | iBOrder.orderType("TRAIL MIT")
63 | iBOrder.trailStopPrice(trail)
64 | iBOrder.auxPrice(stop)
65 | iBOrder.lmtPrice(0)
66 | }
67 | }
68 | iBOrder
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/Util.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import java.util.ArrayList;
7 |
8 | public class Util {
9 | public static boolean StringIsEmpty(String str) {
10 | return str == null || str.length() == 0;
11 | }
12 |
13 | public static String NormalizeString(String str) {
14 | return str != null ? str : "";
15 | }
16 |
17 | public static int StringCompare(String lhs, String rhs) {
18 | return NormalizeString(lhs).compareTo(NormalizeString(rhs));
19 | }
20 |
21 | public static int StringCompareIgnCase(String lhs, String rhs) {
22 | return NormalizeString(lhs).compareToIgnoreCase(NormalizeString(rhs));
23 | }
24 |
25 | public static boolean ArrayEqualsUnordered(ArrayList> lhs, ArrayList> rhs) {
26 | if (lhs == rhs)
27 | return true;
28 |
29 | int lhsCount = lhs == null ? 0 : lhs.size();
30 | int rhsCount = rhs == null ? 0 : rhs.size();
31 |
32 | if (lhsCount != rhsCount)
33 | return false;
34 |
35 | if (lhsCount == 0)
36 | return true;
37 |
38 | boolean[] matchedRhsElems = new boolean[rhsCount];
39 |
40 | for (int lhsIdx = 0; lhsIdx < lhsCount; ++lhsIdx) {
41 | Object lhsElem = lhs.get(lhsIdx);
42 | int rhsIdx = 0;
43 | for (; rhsIdx < rhsCount; ++rhsIdx) {
44 | if (matchedRhsElems[rhsIdx]) {
45 | continue;
46 | }
47 | if (lhsElem.equals(rhs.get(rhsIdx))) {
48 | matchedRhsElems[rhsIdx] = true;
49 | break;
50 | }
51 | }
52 | if (rhsIdx >= rhsCount) {
53 | // no matching elem found
54 | return false;
55 | }
56 | }
57 | return true;
58 | }
59 |
60 | public static String IntMaxString(int value) {
61 | return (value == Integer.MAX_VALUE) ? "" : "" + value;
62 | }
63 |
64 | public static String DoubleMaxString(double value) {
65 | return (value == Double.MAX_VALUE) ? "" : "" + value;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/notes:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | der] WARN com.larroy.ibclient.IBClient - Warning -1 2104 Market data farm connection is OK:usfuture
5 | 06:15:39.938 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
6 | 06:15:40.833 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
7 | 06:15:45.194 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
8 | 06:15:52.496 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
9 | 06:15:57.761 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
10 | 06:16:00.498 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1100 msg: Connectivity between IB and Trader Workstation has been lost.
11 | 06:16:08.783 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.eufarm
12 | 06:16:08.784 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.eufarm
13 | 06:16:08.791 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.usfuture
14 | 06:16:08.802 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.cashfarm
15 | 06:16:08.803 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.usfuture
16 | 06:16:08.803 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2108 Market data farm connection is inactive but should be available upon demand.cashfarm
17 | 06:16:09.187 [EReader] ERROR com.larroy.ibclient.IBClient - Error requestId: -1 code: 1102 msg: Connectivity between IB and Trader Workstation has been restored - data maintained.
18 | 06:16:09.513 [EReader] WARN com.larroy.ibclient.IBClient - Warning -1 2103 Market data farm connection is broken:usfuture
19 |
20 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/package.scala:
--------------------------------------------------------------------------------
1 | package com.larroy
2 |
3 | /**
4 | * IB Scala client.
5 | * [[com.larroy.ibclient.IBClient]] is the main class to access IB from Scala
6 | */
7 | package object ibclient {
8 | val defaultConfig =
9 | s"""
10 | |ibclient {
11 | | tws {
12 | | host = "localhost"
13 | | port = 7496
14 | | clientId = 3
15 | | timeout_s = 60
16 | | }
17 | | params {
18 | | stock {
19 | | contract = "SPY"
20 | | exchange = "SMART"
21 | | currency = "USD"
22 | | }
23 | | future {
24 | | contract = "CL"
25 | | exchange = "NYMEX"
26 | | currency = "USD"
27 | | }
28 | | }
29 | | historyRequestTimeout = "2 min"
30 | | historyRequestPacingViolationRetry {
31 | | length = 60
32 | | unit = "s"
33 | | count = 4
34 | | }
35 | | historyLimits {
36 | | SECOND._5_secs = 10000
37 | | SECOND._10_secs = 10000
38 | | SECOND._15_secs = 10000
39 | | SECOND._30_secs = 10000
40 | | SECOND._1_min = 86400
41 | | SECOND._2_mins = 86400
42 | | SECOND._3_mins = 86400
43 | | SECOND._5_mins = 86400
44 | | SECOND._10_mins = 86400
45 | | SECOND._15_mins = 86400
46 | | SECOND._30_mins = 86400
47 | | SECOND._1_hour = 86400
48 | |
49 | | DAY._30_secs = 1
50 | | DAY._1_min = 10
51 | | DAY._5_mins = 10
52 | | DAY._15_mins = 20
53 | | DAY._1_hour = 34
54 | | DAY._4_hour = 34
55 | | DAY._1_day = 60
56 | |
57 | | WEEK._15_mins = 2
58 | | WEEK._1_hour = 4
59 | | WEEK._4_hours = 4
60 | | WEEK._1_day = 52
61 | | WEEK._1_week = 52
62 | |
63 | | MONTH._1_hour= 1
64 | | MONTH._4_hours = 1
65 | | MONTH._1_day = 12
66 | | MONTH._1_week = 12
67 | |
68 | | YEAR._1_day = 5
69 | | YEAR._1_week = 5
70 | | }
71 | |}
72 | |
73 | """.stripMargin
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/test/scala/com/larroy/ibclient/util/HistoryLimitsSpec.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | /**
4 | * @author piotr 03.03.15
5 | */
6 |
7 | import org.joda.time.format.DateTimeFormat
8 | import org.specs2.mutable._
9 |
10 | class HistoryLimitsSpec extends Specification {
11 | "HistoryLimitsSpec" should {
12 | "return propper HistoryDuration" in {
13 | val fmt = DateTimeFormat.forPattern("yyyyMMdd kk:mm:ss")
14 | def toDate(s: String) = fmt.parseDateTime(s).toDate
15 | import com.ib.client.Types.DurationUnit._
16 | import com.ib.client.Types.BarSize._
17 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _5_secs) shouldEqual new HistoryDuration(3600, 10000, SECOND)
18 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _30_secs) shouldEqual new HistoryDuration(3600, 10000, SECOND)
19 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _1_min) shouldEqual new HistoryDuration(3600, 86400, SECOND)
20 | HistoryLimits.bestDuration(toDate("20150101 09:00:00"), toDate("20150303 10:00:00"), _1_min) shouldEqual new HistoryDuration(62, 10, DAY)
21 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _5_mins) shouldEqual new HistoryDuration(3600, 86400, SECOND)
22 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _15_mins) shouldEqual new HistoryDuration(3600, 86400, SECOND)
23 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _30_mins) shouldEqual new HistoryDuration(3600, 86400, SECOND)
24 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _1_hour) shouldEqual new HistoryDuration(3600, 86400, SECOND)
25 | HistoryLimits.bestDuration(toDate("20150303 10:00:00"), toDate("20150303 10:00:00"), _1_day) shouldEqual new HistoryDuration(1, 60, DAY)
26 | HistoryLimits.bestDuration(toDate("20150303 09:00:00"), toDate("20150303 10:00:00"), _1_day) shouldEqual new HistoryDuration(1, 60, DAY)
27 | HistoryLimits.bestDuration(toDate("20150301 09:00:00"), toDate("20150303 09:00:00"), _1_day) shouldEqual new HistoryDuration(2, 60, DAY)
28 | HistoryLimits.bestDuration(toDate("20150203 09:00:00"), toDate("20150303 09:00:00"), _1_day) shouldEqual new HistoryDuration(4, 52, WEEK)
29 | HistoryLimits.bestDuration(toDate("20150201 09:00:00"), toDate("20150303 09:00:00"), _1_day) shouldEqual new HistoryDuration(1, 12, MONTH)
30 | HistoryLimits.bestDuration(toDate("20150131 09:00:00"), toDate("20150303 09:00:00"), _1_day) shouldEqual new HistoryDuration(2, 12, MONTH)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/package.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import java.time.{LocalDate, ZoneId, ZoneOffset, ZonedDateTime}
4 | import java.time.format.DateTimeFormatter
5 | import java.util.Calendar
6 |
7 | import org.joda.time.format.DateTimeFormat
8 |
9 | import scala.concurrent.{ExecutionContext, Future}
10 | import scala.util.control.NonFatal
11 | import scala.util.{Failure, Success, Try}
12 | import org.slf4j.{Logger, LoggerFactory}
13 |
14 | /**
15 | * @author piotr 03.03.15
16 | */
17 | package object util {
18 | //private val dateTimeFormat = DateTimeFormat.forPattern("yyyyMMdd")
19 | private val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd").withZone(ZoneOffset.UTC)
20 | def dateEpoch_s(date: String): Long = {
21 | if (date.length() == 8) {
22 | // http://stackoverflow.com/questions/23596530/unable-to-obtain-zoneddatetime-from-temporalaccessor-using-datetimeformatter-and
23 | val zonedDateTime = LocalDate.parse(date, dateFormatter)
24 | val x = zonedDateTime.atStartOfDay(ZoneOffset.UTC)
25 | x.toEpochSecond
26 | //dateTimeFormat.parseDateTime(date).toDate.getTime / 1000
27 | } else
28 | date.toLong
29 | }
30 |
31 | def divRoundAwayZero(x: Long, div: Long) = (x + (Math.abs(div) - 1)) / div
32 | def divRoundAwayZero(x: Int, div: Int) = (x + (Math.abs(div) - 1)) / div
33 |
34 | def defer[A](interval_ms: Long)(block: ⇒ A)(implicit ctx: ExecutionContext): Future[A] = {
35 | Future {
36 | Thread.sleep(interval_ms)
37 | block
38 | }(ctx)
39 | }
40 |
41 | def retry[T](n: Int)(block: => T, delay_ms: Long = 1000, base: Long = 2): T = {
42 | try {
43 | block
44 | } catch {
45 | case e if n > 1 => {
46 | val log: Logger = LoggerFactory.getLogger(this.getClass)
47 | log.debug(s"retry block: suspending thread for ${delay_ms} ms")
48 | log.debug(s"\texception was: ${e.toString}")
49 | Thread.sleep(delay_ms)
50 | retry(n - 1)(block, delay_ms * base, base)
51 | }
52 | }
53 | }
54 |
55 | def retryWhen[T](n: Int)(block: => T, when: (Throwable) ⇒ Boolean, delay_ms: Long = 1000, base: Long = 2): T = {
56 | try {
57 | block
58 | } catch {
59 | case e if n > 1 && when(e) ⇒ {
60 | val log: Logger = LoggerFactory.getLogger(this.getClass)
61 | log.debug(s"retry block: suspending thread for ${delay_ms} ms")
62 | log.debug(s"\texception was: ${e.toString}")
63 | Thread.sleep(delay_ms)
64 | retry(n - 1)(block, delay_ms * base, base)
65 | }
66 | }
67 | }
68 |
69 |
70 |
71 | /*
72 | @annotation.tailrec
73 | def retry[T](n: Int)(fn: => T): Try[T] = {
74 | Try { fn } match {
75 | case x: Success[T] => x
76 | case Failure(NonFatal(_)) if n > 1 => retry(n - 1)(fn)
77 | case f => f
78 | }
79 | }
80 | */
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/ExecutionFilter.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class ExecutionFilter{
7 | private int m_clientId; // zero means no filtering on this field
8 | private String m_acctCode;
9 | private String m_time;
10 | private String m_symbol;
11 | private String m_secType;
12 | private String m_exchange;
13 | private String m_side;
14 |
15 | // Get
16 | public int clientId() { return m_clientId; }
17 | public String acctCode() { return m_acctCode; }
18 | public String time() { return m_time; }
19 | public String symbol() { return m_symbol; }
20 | public String secType() { return m_secType; }
21 | public String exchange() { return m_exchange; }
22 | public String side() { return m_side; }
23 |
24 | // Set
25 | public void clientId(int clientId) { m_clientId = clientId; }
26 | public void acctCode(String acctCode) { m_acctCode = acctCode; }
27 | public void time(String time) { m_time = time; }
28 | public void symbol(String symbol) { m_symbol = symbol; }
29 | public void secType(String secType) { m_secType = secType; }
30 | public void exchange(String exchange) { m_exchange = exchange; }
31 | public void side(String side) { m_side = side; }
32 |
33 | public ExecutionFilter() {
34 | clientId(0);
35 | }
36 |
37 | public ExecutionFilter( int p_clientId, String p_acctCode, String p_time,
38 | String p_symbol, String p_secType, String p_exchange, String p_side) {
39 | m_clientId = p_clientId;
40 | m_acctCode = p_acctCode;
41 | m_time = p_time;
42 | m_symbol = p_symbol;
43 | m_secType = p_secType;
44 | m_exchange = p_exchange;
45 | m_side = p_side;
46 | }
47 |
48 | public boolean equals(Object p_other) {
49 | boolean l_bRetVal = false;
50 |
51 | if ( p_other == null ) {
52 | l_bRetVal = false;
53 | }
54 | else if ( this == p_other ) {
55 | l_bRetVal = true;
56 | }
57 | else {
58 | ExecutionFilter l_theOther = (ExecutionFilter)p_other;
59 | l_bRetVal = (m_clientId == l_theOther.m_clientId &&
60 | m_acctCode.equalsIgnoreCase( l_theOther.m_acctCode) &&
61 | m_time.equalsIgnoreCase( l_theOther.m_time) &&
62 | m_symbol.equalsIgnoreCase( l_theOther.m_symbol) &&
63 | m_secType.equalsIgnoreCase( l_theOther.m_secType) &&
64 | m_exchange.equalsIgnoreCase( l_theOther.m_exchange) &&
65 | m_side.equalsIgnoreCase( l_theOther.m_side) );
66 | }
67 | return l_bRetVal;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/ScanCode.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | public enum ScanCode {
7 | TOP_PERC_GAIN,
8 | TOP_PERC_LOSE,
9 | MOST_ACTIVE,
10 | ALL_SYMBOLS_ASC,
11 | ALL_SYMBOLS_DESC,
12 | BOND_CUSIP_AZ,
13 | BOND_CUSIP_ZA,
14 | FAR_MATURITY_DATE,
15 | HALTED,
16 | HIGH_BOND_ASK_CURRENT_YIELD_ALL,
17 | HIGH_BOND_ASK_YIELD_ALL,
18 | HIGH_BOND_DEBT_2_BOOK_RATIO,
19 | HIGH_BOND_DEBT_2_EQUITY_RATIO,
20 | HIGH_BOND_DEBT_2_TAN_BOOK_RATIO,
21 | HIGH_BOND_EQUITY_2_BOOK_RATIO,
22 | HIGH_BOND_EQUITY_2_TAN_BOOK_RATIO,
23 | HIGH_BOND_NET_ASK_CURRENT_YIELD_ALL,
24 | HIGH_BOND_NET_ASK_YIELD_ALL,
25 | HIGH_BOND_NET_SPREAD_ALL,
26 | HIGH_BOND_SPREAD_ALL,
27 | HIGH_COUPON_RATE,
28 | HIGH_DIVIDEND_YIELD,
29 | HIGH_DIVIDEND_YIELD_IB,
30 | HIGHEST_SLB_BID,
31 | HIGH_GROWTH_RATE,
32 | HIGH_MOODY_RATING_ALL,
33 | HIGH_OPEN_GAP,
34 | HIGH_OPT_IMP_VOLAT,
35 | HIGH_OPT_IMP_VOLAT_OVER_HIST,
36 | HIGH_OPT_OPEN_INTEREST_PUT_CALL_RATIO,
37 | HIGH_OPT_VOLUME_PUT_CALL_RATIO,
38 | HIGH_PE_RATIO,
39 | HIGH_PRICE_2_BOOK_RATIO,
40 | HIGH_PRICE_2_TAN_BOOK_RATIO,
41 | HIGH_QUICK_RATIO,
42 | HIGH_RETURN_ON_EQUITY,
43 | HIGH_SYNTH_BID_REV_NAT_YIELD,
44 | HIGH_VS_13W_HL,
45 | HIGH_VS_26W_HL,
46 | HIGH_VS_52W_HL,
47 | HOT_BY_OPT_VOLUME,
48 | HOT_BY_PRICE,
49 | HOT_BY_PRICE_RANGE,
50 | HOT_BY_VOLUME,
51 | LIMIT_UP_DOWN,
52 | LOW_BOND_BID_CURRENT_YIELD_ALL,
53 | LOW_BOND_BID_YIELD_ALL,
54 | LOW_BOND_DEBT_2_BOOK_RATIO,
55 | LOW_BOND_DEBT_2_EQUITY_RATIO,
56 | LOW_BOND_DEBT_2_TAN_BOOK_RATIO,
57 | LOW_BOND_EQUITY_2_BOOK_RATIO,
58 | LOW_BOND_EQUITY_2_TAN_BOOK_RATIO,
59 | LOW_BOND_NET_BID_CURRENT_YIELD_ALL,
60 | LOW_BOND_NET_BID_YIELD_ALL,
61 | LOW_BOND_NET_SPREAD_ALL,
62 | LOW_BOND_SPREAD_ALL,
63 | LOW_COUPON_RATE,
64 | LOWEST_SLB_ASK,
65 | LOW_GROWTH_RATE,
66 | LOW_MOODY_RATING_ALL,
67 | LOW_OPEN_GAP,
68 | LOW_OPT_IMP_VOLAT,
69 | LOW_OPT_IMP_VOLAT_OVER_HIST,
70 | LOW_OPT_OPEN_INTEREST_PUT_CALL_RATIO,
71 | LOW_OPT_VOLUME_PUT_CALL_RATIO,
72 | LOW_PE_RATIO,
73 | LOW_PRICE_2_BOOK_RATIO,
74 | LOW_PRICE_2_TAN_BOOK_RATIO,
75 | LOW_QUICK_RATIO,
76 | LOW_RETURN_ON_EQUITY,
77 | LOW_SYNTH_ASK_REV_NAT_YIELD,
78 | LOW_VS_13W_HL,
79 | LOW_VS_26W_HL,
80 | LOW_VS_52W_HL,
81 | LOW_WAR_REL_IMP_VOLAT,
82 | MARKET_CAP_USD_ASC,
83 | MARKET_CAP_USD_DESC,
84 | MOST_ACTIVE_AVG_USD,
85 | MOST_ACTIVE_USD,
86 | NEAR_MATURITY_DATE,
87 | NOT_OPEN,
88 | OPT_OPEN_INTEREST_MOST_ACTIVE,
89 | OPT_VOLUME_MOST_ACTIVE,
90 | PMONITOR_AVAIL_CONTRACTS,
91 | PMONITOR_CTT,
92 | PMONITOR_IBOND,
93 | PMONITOR_RFQ,
94 | TOP_OPEN_PERC_GAIN,
95 | TOP_OPEN_PERC_LOSE,
96 | TOP_OPT_IMP_VOLAT_GAIN,
97 | TOP_OPT_IMP_VOLAT_LOSE,
98 | TOP_PRICE_RANGE,
99 | TOP_STOCK_BUY_IMBALANCE_ADV_RATIO,
100 | TOP_STOCK_SELL_IMBALANCE_ADV_RATIO,
101 | TOP_TRADE_COUNT,
102 | TOP_TRADE_RATE,
103 | TOP_VOLUME_RATE,
104 | WSH_NEXT_ANALYST_MEETING,
105 | WSH_NEXT_EARNINGS,
106 | WSH_NEXT_EVENT,
107 | WSH_NEXT_MAJOR_EVENT,
108 | WSH_PREV_ANALYST_MEETING,
109 | WSH_PREV_EARNINGS,
110 | WSH_PREV_EVENT,
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/TickType.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public enum TickType {
8 | BID_SIZE( 0, "bidSize" ),
9 | BID( 1, "bidPrice" ),
10 | ASK( 2, "askPrice" ),
11 | ASK_SIZE( 3, "askSize" ),
12 | LAST( 4, "lastPrice" ),
13 | LAST_SIZE( 5, "lastSize" ),
14 | HIGH( 6, "high" ),
15 | LOW( 7, "low" ),
16 | VOLUME( 8, "volume" ),
17 | CLOSE( 9, "close" ),
18 | BID_OPTION( 10, "bidOptComp" ),
19 | ASK_OPTION( 11, "askOptComp" ),
20 | LAST_OPTION( 12, "lastOptComp" ),
21 | MODEL_OPTION( 13, "modelOptComp" ),
22 | OPEN( 14, "open" ),
23 | LOW_13_WEEK( 15, "13WeekLow" ),
24 | HIGH_13_WEEK( 16, "13WeekHigh" ),
25 | LOW_26_WEEK( 17, "26WeekLow" ),
26 | HIGH_26_WEEK( 18, "26WeekHigh" ),
27 | LOW_52_WEEK( 19, "52WeekLow" ),
28 | HIGH_52_WEEK( 20, "52WeekHigh" ),
29 | AVG_VOLUME( 21, "AvgVolume" ),
30 | OPEN_INTEREST( 22, "OpenInterest" ),
31 | OPTION_HISTORICAL_VOL( 23, "OptionHistoricalVolatility" ),
32 | OPTION_IMPLIED_VOL( 24, "OptionImpliedVolatility" ),
33 | OPTION_BID_EXCH( 25, "OptionBidExchStr" ),
34 | OPTION_ASK_EXCH( 26, "OptionAskExchStr" ),
35 | OPTION_CALL_OPEN_INTEREST( 27, "OptionCallOpenInterest" ),
36 | OPTION_PUT_OPEN_INTEREST( 28, "OptionPutOpenInterest" ),
37 | OPTION_CALL_VOLUME( 29, "OptionCallVolume" ),
38 | OPTION_PUT_VOLUME( 30, "OptionPutVolume" ),
39 | INDEX_FUTURE_PREMIUM( 31, "IndexFuturePremium" ),
40 | BID_EXCH( 32, "bidExch" ), // string
41 | ASK_EXCH( 33, "askExch" ), // string
42 | AUCTION_VOLUME( 34, "auctionVolume" ),
43 | AUCTION_PRICE( 35, "auctionPrice" ),
44 | AUCTION_IMBALANCE( 36, "auctionImbalance" ),
45 | MARK_PRICE( 37, "markPrice" ),
46 | BID_EFP_COMPUTATION( 38, "bidEFP" ),
47 | ASK_EFP_COMPUTATION( 39, "askEFP" ),
48 | LAST_EFP_COMPUTATION( 40, "lastEFP" ),
49 | OPEN_EFP_COMPUTATION( 41, "openEFP" ),
50 | HIGH_EFP_COMPUTATION( 42, "highEFP" ),
51 | LOW_EFP_COMPUTATION( 43, "lowEFP" ),
52 | CLOSE_EFP_COMPUTATION( 44, "closeEFP" ),
53 | LAST_TIMESTAMP( 45, "lastTimestamp" ), // string
54 | SHORTABLE( 46, "shortable" ),
55 | FUNDAMENTAL_RATIOS( 47, "fundamentals" ), // string
56 | RT_VOLUME( 48, "RTVolume" ), // string
57 | HALTED( 49, "halted" ),
58 | BID_YIELD( 50, "bidYield" ),
59 | ASK_YIELD( 51, "askYield" ),
60 | LAST_YIELD( 52, "lastYield" ),
61 | CUST_OPTION_COMPUTATION( 53, "custOptComp" ),
62 | TRADE_COUNT( 54, "trades" ),
63 | TRADE_RATE( 55, "trades/min" ),
64 | VOLUME_RATE( 56, "volume/min" ),
65 | LAST_RTH_TRADE( 57, "lastRTHTrade" ),
66 | RT_HISTORICAL_VOL( 58, "RTHistoricalVol" ),
67 | REGULATORY_IMBALANCE( 61, "regulatoryImbalance" ),
68 | UNKNOWN( Integer.MAX_VALUE , "unknown" );
69 |
70 | private int m_ndx;
71 | private String m_field;
72 |
73 | // Get
74 | public int index() { return m_ndx; }
75 | public String field() { return m_field; }
76 |
77 | private TickType(int ndx, String field) {
78 | m_ndx = ndx;
79 | m_field = field;
80 | }
81 |
82 | public static TickType get(int ndx) {
83 | for( TickType tt : values() ) {
84 | if( tt.m_ndx == ndx) {
85 | return tt;
86 | }
87 | }
88 | return UNKNOWN;
89 | }
90 |
91 | public static String getField(int tickType) {
92 | return get(tickType).field();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/OrderState.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public class OrderState {
8 | private String m_status;
9 | private String m_initMargin;
10 | private String m_maintMargin;
11 | private String m_equityWithLoan;
12 | private double m_commission;
13 | private double m_minCommission;
14 | private double m_maxCommission;
15 | private String m_commissionCurrency;
16 | private String m_warningText;
17 |
18 | // Get
19 | public double commission() { return m_commission; }
20 | public double maxCommission() { return m_maxCommission; }
21 | public double minCommission() { return m_minCommission; }
22 | public OrderStatus status() { return OrderStatus.valueOf(m_status); }
23 | public String getStatus() { return m_status; }
24 | public String commissionCurrency() { return m_commissionCurrency; }
25 | public String equityWithLoan() { return m_equityWithLoan; }
26 | public String initMargin() { return m_initMargin; }
27 | public String maintMargin() { return m_maintMargin; }
28 | public String warningText() { return m_warningText; }
29 |
30 | // Set
31 | public void commission(double v) { m_commission = v; }
32 | public void commissionCurrency(String v) { m_commissionCurrency = v; }
33 | public void equityWithLoan(String v) { m_equityWithLoan = v; }
34 | public void initMargin(String v) { m_initMargin = v; }
35 | public void maintMargin(String v) { m_maintMargin = v; }
36 | public void maxCommission(double v) { m_maxCommission = v; }
37 | public void minCommission(double v) { m_minCommission = v; }
38 | public void status(OrderStatus v) { m_status = ( v == null ) ? null : v.name(); }
39 | public void status(String v) { m_status = v; }
40 | public void warningText(String v) { m_warningText = v; }
41 |
42 | OrderState() {
43 | this (null, null, null, null, 0.0, 0.0, 0.0, null, null);
44 | }
45 |
46 | OrderState(String status, String initMargin, String maintMargin,
47 | String equityWithLoan, double commission, double minCommission,
48 | double maxCommission, String commissionCurrency, String warningText) {
49 | m_status = status;
50 | m_initMargin = initMargin;
51 | m_maintMargin = maintMargin;
52 | m_equityWithLoan = equityWithLoan;
53 | m_commission = commission;
54 | m_minCommission = minCommission;
55 | m_maxCommission = maxCommission;
56 | m_commissionCurrency = commissionCurrency;
57 | m_warningText = warningText;
58 | }
59 |
60 | public boolean equals(Object other) {
61 | if (this == other)
62 | return true;
63 |
64 | if (other == null)
65 | return false;
66 |
67 | OrderState state = (OrderState)other;
68 |
69 | if (m_commission != state.m_commission ||
70 | m_minCommission != state.m_minCommission ||
71 | m_maxCommission != state.m_maxCommission) {
72 | return false;
73 | }
74 |
75 | if (Util.StringCompare(m_status, state.m_status) != 0 ||
76 | Util.StringCompare(m_initMargin, state.m_initMargin) != 0 ||
77 | Util.StringCompare(m_maintMargin, state.m_maintMargin) != 0 ||
78 | Util.StringCompare(m_equityWithLoan, state.m_equityWithLoan) != 0 ||
79 | Util.StringCompare(m_commissionCurrency, state.m_commissionCurrency) != 0) {
80 | return false;
81 | }
82 | return true;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/Builder.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.DataOutputStream;
8 | import java.io.IOException;
9 |
10 | /** This class is used to build messages so the entire message can be
11 | * sent to the socket in a single write. */
12 | public class Builder {
13 | private static final char SEP = 0;
14 | private static final int PADDING_SIZE = 1; // 1 disables padding, 4 is normal if padding is used
15 | private static final byte[] EMPTY_LENGTH_HEADER = new byte[ 4 ];
16 |
17 | private final ByteBuffer m_sb;
18 | private boolean m_useSendMax;
19 |
20 | public Builder( int size ) {
21 | m_sb = new ByteBuffer( size );
22 | }
23 |
24 | /** If a numeric value is set to the maxvalue (appropriate for the data type),
25 | * then when it is serialized using send it is automatically replaced by a null field. */
26 | public void setUseSendMax() {
27 | m_useSendMax = true;
28 | }
29 |
30 | public void send(int a) {
31 | if ( m_useSendMax ) {
32 | sendMax( a );
33 | }
34 | else {
35 | send( String.valueOf(a) );
36 | }
37 | }
38 |
39 | public void sendMax(int a) {
40 | send( a == Integer.MAX_VALUE ? "" : String.valueOf( a) );
41 | }
42 |
43 | public void send(double a) {
44 | if ( m_useSendMax ) {
45 | sendMax( a );
46 | }
47 | else {
48 | send( String.valueOf( a) );
49 | }
50 | }
51 |
52 | public void sendMax(double a) {
53 | send( a == Double.MAX_VALUE ? "" : String.valueOf( a) );
54 | }
55 |
56 | public void send( boolean a) {
57 | send( a ? 1 : 0);
58 | }
59 |
60 | public void send( IApiEnum a) {
61 | send( a == null ? (String)null : a.getApiString() );
62 | }
63 |
64 | public void send( String a) {
65 | if (a != null) {
66 | byte[] buffer = a.getBytes();
67 | m_sb.write( buffer, 0, buffer.length );
68 | }
69 | m_sb.write( SEP);
70 | }
71 |
72 | public void send( byte[] bytes ) {
73 | if ( bytes != null ) {
74 | m_sb.write( bytes, 0, bytes.length );
75 | }
76 | }
77 |
78 | public int allocateLengthHeader() {
79 | int lengthHeaderPosition = m_sb.size();
80 | m_sb.write( EMPTY_LENGTH_HEADER, 0, EMPTY_LENGTH_HEADER.length );
81 | return lengthHeaderPosition;
82 | }
83 |
84 | public void updateLength( int lengthHeaderPosition ) {
85 | m_sb.updateLength( lengthHeaderPosition );
86 | }
87 |
88 | public void writeTo( DataOutputStream dos ) throws IOException {
89 | m_sb.writeTo( dos );
90 | }
91 |
92 | // b[] must be at least b[position+4]
93 | public static void intToBytes(int val, byte b[], int position) {
94 | b[position+0] = (byte)(0xff & (val >> 24));
95 | b[position+1] = (byte)(0xff & (val >> 16));
96 | b[position+2] = (byte)(0xff & (val >> 8));
97 | b[position+3] = (byte)(0xff & val);
98 | return;
99 | }
100 |
101 | /** inner class: ByteBuffer - storage for bytes and direct access to buffer. */
102 | private static class ByteBuffer extends ByteArrayOutputStream {
103 | public ByteBuffer( int capacity ) {
104 | super( capacity );
105 | }
106 |
107 | public void updateLength( int lengthHeaderPosition ) {
108 | int len = this.count - EMPTY_LENGTH_HEADER.length - lengthHeaderPosition;
109 | if ( PADDING_SIZE > 1 ) {
110 | int padding = PADDING_SIZE - len%PADDING_SIZE;
111 | if ( padding < PADDING_SIZE ) {
112 | this.write( EMPTY_LENGTH_HEADER, 0, PADDING_SIZE ); // extra padding at the end
113 | len = this.count - EMPTY_LENGTH_HEADER.length - lengthHeaderPosition;
114 | }
115 | }
116 | intToBytes(len, this.buf, lengthHeaderPosition);
117 | }
118 |
119 | public void writeTo( DataOutputStream out ) throws IOException {
120 | out.write( this.buf, 0, this.count );
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/EWrapper.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public interface EWrapper {
8 | ///////////////////////////////////////////////////////////////////////
9 | // Interface methods
10 | ///////////////////////////////////////////////////////////////////////
11 | void tickPrice( int tickerId, int field, double price, int canAutoExecute);
12 | void tickSize( int tickerId, int field, int size);
13 | void tickOptionComputation( int tickerId, int field, double impliedVol,
14 | double delta, double optPrice, double pvDividend,
15 | double gamma, double vega, double theta, double undPrice);
16 | void tickGeneric(int tickerId, int tickType, double value);
17 | void tickString(int tickerId, int tickType, String value);
18 | void tickEFP(int tickerId, int tickType, double basisPoints,
19 | String formattedBasisPoints, double impliedFuture, int holdDays,
20 | String futureExpiry, double dividendImpact, double dividendsToExpiry);
21 | void orderStatus( int orderId, String status, int filled, int remaining,
22 | double avgFillPrice, int permId, int parentId, double lastFillPrice,
23 | int clientId, String whyHeld);
24 | void openOrder( int orderId, Contract contract, Order order, OrderState orderState);
25 | void openOrderEnd();
26 | void updateAccountValue(String key, String value, String currency, String accountName);
27 | void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue,
28 | double averageCost, double unrealizedPNL, double realizedPNL, String accountName);
29 | void updateAccountTime(String timeStamp);
30 | void accountDownloadEnd(String accountName);
31 | void nextValidId( int orderId);
32 | void contractDetails(int reqId, ContractDetails contractDetails);
33 | void bondContractDetails(int reqId, ContractDetails contractDetails);
34 | void contractDetailsEnd(int reqId);
35 | void execDetails( int reqId, Contract contract, Execution execution);
36 | void execDetailsEnd( int reqId);
37 | void updateMktDepth( int tickerId, int position, int operation, int side, double price, int size);
38 | void updateMktDepthL2( int tickerId, int position, String marketMaker, int operation,
39 | int side, double price, int size);
40 | void updateNewsBulletin( int msgId, int msgType, String message, String origExchange);
41 | void managedAccounts( String accountsList);
42 | void receiveFA(int faDataType, String xml);
43 | void historicalData(int reqId, String date, double open, double high, double low,
44 | double close, int volume, int count, double WAP, boolean hasGaps);
45 | void scannerParameters(String xml);
46 | void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance,
47 | String benchmark, String projection, String legsStr);
48 | void scannerDataEnd(int reqId);
49 | void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count);
50 | void currentTime(long time);
51 | void fundamentalData(int reqId, String data);
52 | void deltaNeutralValidation(int reqId, DeltaNeutralContract underComp);
53 | void tickSnapshotEnd(int reqId);
54 | void marketDataType(int reqId, int marketDataType);
55 | void commissionReport(CommissionReport commissionReport);
56 | void position(String account, Contract contract, int pos, double avgCost);
57 | void positionEnd();
58 | void accountSummary(int reqId, String account, String tag, String value, String currency);
59 | void accountSummaryEnd(int reqId);
60 | void verifyMessageAPI( String apiData);
61 | void verifyCompleted( boolean isSuccessful, String errorText);
62 | void verifyAndAuthMessageAPI( String apiData, String xyzChallange);
63 | void verifyAndAuthCompleted( boolean isSuccessful, String errorText);
64 | void displayGroupList( int reqId, String groups);
65 | void displayGroupUpdated( int reqId, String contractInfo);
66 | void error( Exception e);
67 | void error( String str);
68 | void error(int id, int errorCode, String errorMsg);
69 | void connectionClosed();
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/ScannerSubscription.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class ScannerSubscription {
7 | public final static int NO_ROW_NUMBER_SPECIFIED = -1;
8 |
9 | private int m_numberOfRows = NO_ROW_NUMBER_SPECIFIED;
10 | private String m_instrument;
11 | private String m_locationCode;
12 | private String m_scanCode;
13 | private double m_abovePrice = Double.MAX_VALUE;
14 | private double m_belowPrice = Double.MAX_VALUE;
15 | private int m_aboveVolume = Integer.MAX_VALUE;
16 | private int m_averageOptionVolumeAbove = Integer.MAX_VALUE;
17 | private double m_marketCapAbove = Double.MAX_VALUE;
18 | private double m_marketCapBelow = Double.MAX_VALUE;
19 | private String m_moodyRatingAbove;
20 | private String m_moodyRatingBelow;
21 | private String m_spRatingAbove;
22 | private String m_spRatingBelow;
23 | private String m_maturityDateAbove;
24 | private String m_maturityDateBelow;
25 | private double m_couponRateAbove = Double.MAX_VALUE;
26 | private double m_couponRateBelow = Double.MAX_VALUE;
27 | private String m_excludeConvertible;
28 | private String m_scannerSettingPairs;
29 | private String m_stockTypeFilter;
30 |
31 | // Get
32 | public int numberOfRows() { return m_numberOfRows; }
33 | public String instrument() { return m_instrument; }
34 | public String locationCode() { return m_locationCode; }
35 | public String scanCode() { return m_scanCode; }
36 | public double abovePrice() { return m_abovePrice; }
37 | public double belowPrice() { return m_belowPrice; }
38 | public int aboveVolume() { return m_aboveVolume; }
39 | public int averageOptionVolumeAbove() { return m_averageOptionVolumeAbove; }
40 | public double marketCapAbove() { return m_marketCapAbove; }
41 | public double marketCapBelow() { return m_marketCapBelow; }
42 | public String moodyRatingAbove() { return m_moodyRatingAbove; }
43 | public String moodyRatingBelow() { return m_moodyRatingBelow; }
44 | public String spRatingAbove() { return m_spRatingAbove; }
45 | public String spRatingBelow() { return m_spRatingBelow; }
46 | public String maturityDateAbove() { return m_maturityDateAbove; }
47 | public String maturityDateBelow() { return m_maturityDateBelow; }
48 | public double couponRateAbove() { return m_couponRateAbove; }
49 | public double couponRateBelow() { return m_couponRateBelow; }
50 | public String excludeConvertible() { return m_excludeConvertible; }
51 | public String scannerSettingPairs() { return m_scannerSettingPairs; }
52 | public String stockTypeFilter() { return m_stockTypeFilter; }
53 |
54 | // Set
55 | public void numberOfRows(int num) { m_numberOfRows = num; }
56 | public void instrument(String txt) { m_instrument = txt; }
57 | public void locationCode(String txt) { m_locationCode = txt; }
58 | public void scanCode(String txt) { m_scanCode = txt; }
59 | public void abovePrice(double price) { m_abovePrice = price; }
60 | public void belowPrice(double price) { m_belowPrice = price; }
61 | public void aboveVolume(int volume) { m_aboveVolume = volume; }
62 | public void averageOptionVolumeAbove(int volume) { m_averageOptionVolumeAbove = volume; }
63 | public void marketCapAbove(double cap) { m_marketCapAbove = cap; }
64 | public void marketCapBelow(double cap) { m_marketCapBelow = cap; }
65 | public void moodyRatingAbove(String r) { m_moodyRatingAbove = r; }
66 | public void moodyRatingBelow(String r) { m_moodyRatingBelow = r; }
67 | public void spRatingAbove(String r) { m_spRatingAbove = r; }
68 | public void spRatingBelow(String r) { m_spRatingBelow = r; }
69 | public void maturityDateAbove(String d) { m_maturityDateAbove = d; }
70 | public void maturityDateBelow(String d) { m_maturityDateBelow = d; }
71 | public void couponRateAbove(double r) { m_couponRateAbove = r; }
72 | public void couponRateBelow(double r) { m_couponRateBelow = r; }
73 | public void excludeConvertible(String c) { m_excludeConvertible = c; }
74 | public void scannerSettingPairs(String val) { m_scannerSettingPairs = val; }
75 | public void stockTypeFilter(String val) { m_stockTypeFilter = val; }
76 | }
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scala IBClient
2 |
3 | Interactive brokers Scala client.
4 |
5 | This code wraps the java IBClient in asynchronous (futures) and reactive (RxScala) API.
6 |
7 | All the wrapped functions from the java client return Futures, making the scala IBClient a
8 | standalone class that plays nicely with concurrent code.
9 |
10 |
11 | The Java IBClient is copyright interactive brokers and subject to it's license, see https://www.interactivebrokers.com/en/index.php?f=5041 and http://interactivebrokers.github.io/
12 |
13 | ## Build standalone jar
14 |
15 | You can use a fat jar to run the commandline client by doing
16 |
17 | ```
18 | sbt assembly
19 | ```
20 |
21 | Which can be run with:
22 |
23 | ```
24 | java -jar target/scala-2.12/ibclient-assembly-0.2.2-SNAPSHOT.jar
25 | ```
26 |
27 | Or a binary executable with `sbt pack`, and run with `target/pack/bin/main`
28 |
29 |
30 | ## Connect to IB
31 |
32 | To connect to ib you can use IBC, I would recommend my docker version of IBC:
33 |
34 | check out here how to use it:
35 |
36 | https://github.com/larroy/IBC/blob/docker/docker/README.md
37 |
38 |
39 | ## Examples
40 |
41 | The client connects asynchronously so to wait until it's connected we can do the following:
42 | For ilustration purposes we will block on the futures to make the API syncrhonous:
43 |
44 | ```
45 | import scala.concurrent.{Await, Future}
46 | import scala.concurrent.duration.Duration
47 | def block[A](f: Future[A]): A = {
48 | Await.result(f, Duration.Inf)
49 | }
50 |
51 | import com.larroy.ibclient.{IBClient}
52 | val ibc = new IBClient("localhost", 7496, 3)
53 | block(ibc.connect())
54 | ibc.isConnected
55 | ```
56 |
57 |
58 | Using the IBClient class is simple, historical data works out of the box and does throttling
59 | automatically:
60 |
61 | ```
62 |
63 |
64 | import com.larroy.ibclient.contract.{CashContract, FutureContract, GenericContract, StockContract}
65 | val subscription = ibc.marketData(new StockContract("SPY"))
66 | observableBar.subscribe({bar=>println(s"got bar ${bar}")}
67 | ```
68 |
69 | ```
70 | import scala.concurrent.duration._
71 | import org.joda.time._
72 | import com.ib.client.Types.DurationUnit
73 | import com.ib.client.Types.WhatToShow
74 |
75 | val startDate = new DateTime(2015, 4, 10, 15, 0).toDate
76 |
77 | val endDate = new DateTime(2015, 4, 13, 15, 0).toDate
78 | val h = block(ibc.historicalData(new StockContract("SPY"), endDate, 20, DurationUnit.DAY, BarSize._15_mins, WhatToShow.MIDPOINT, false))
79 |
80 | val h = block(ibclient.historicalData(new StockContract("SPY"), endDate, 20,
81 | DurationUnit.DAY, BarSize._15_mins, WhatToShow.MIDPOINT, false))
82 |
83 | ```
84 |
85 | Or with the 'easy historical data' facility:
86 |
87 |
88 | ```
89 | val eh = block(ibc.easyHistoricalData(new StockContract("SPY"),startDate,endDate,BarSize._1_day, WhatToShow.TRADES))
90 | ```
91 |
92 |
93 | ```
94 | val res2 = Await.result(ibclient.historicalData(new CashContract("EUR","EUR.USD"), endDate2, 120, DurationUnit.SECOND, BarSize._1_min, WhatToShow.MIDPOINT, false), scala.concurrent.duration.Duration.Inf)
95 | import java.text.SimpleDateFormat
96 | import java.util.Date
97 | import com.larroy.ibclient.contract.{CashContract, StockContract}
98 | import com.ib.client.Types.{BarSize, DurationUnit, WhatToShow, SecType}
99 | import scala.concurrent.duration._
100 | import scala.concurrent.Await
101 | import com.larroy.ibclient._
102 | import com.typesafe.config.ConfigFactory
103 | val cfg = ConfigFactory.load().getConfig("ibclient.test")
104 | Await.result(ibclient.connect(), Duration.Inf)
105 | ```
106 |
107 | Subscribe to market data:
108 |
109 | ```
110 | val subscription = ibclient.realtimeBars(new CashContract("EUR","EUR.USD"), WhatToShow.MIDPOINT)
111 | subscription.observableBar.subscribe({bar=>println(s"got bar ${bar}")}, {error ⇒ throw (error)})
112 | ```
113 |
114 |
115 | Get contract details
116 |
117 | ```
118 | import scala.concurrent.Await
119 | import scala.concurrent.duration.Duration
120 | import com.larroy.ibclient.contract.{CashContract, FutureContract, GenericContract, StockContract}
121 | import com.larroy.ibclient.{IBClient}
122 | val ibc = new IBClient("localhost", 7496, 3)
123 | Await.result(ibc.connect(), Duration.Inf)
124 | val c = new FutureContract("CL", "", "NYMEX")
125 | val cd = Await.result(ibc.contractDetails(c), Duration.Inf)
126 | ```
127 |
128 |
129 | Or with the commandline client:
130 |
131 | ```
132 | history -c CL -t FUT -e NYMEX -x USD -y 20150421 -a "20150128 15:00:00" -z "20150415 15:00:00" -b
133 | _1_min -o cl.csv
134 | ```
135 |
136 |
137 | ```
138 | history -c VIX -e CBOE -t IND -a "20161001 00:00:00" -o vix.csv -b _1_day
139 | ```
140 |
141 | ```
142 | java -jar target/scala-2.11/ibclient-assembly-0.2.2-SNAPSHOT.jar history -c VIX -e CBOE -t IND -a
143 | "20161115 00:00:00" -z "201118 00:00:00" -o vix.csv -b _1_day
144 | ```
145 |
146 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/Execution.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | public class Execution {
7 | private int m_orderId;
8 | private int m_clientId;
9 | private String m_execId;
10 | private String m_time;
11 | private String m_acctNumber;
12 | private String m_exchange;
13 | private String m_side;
14 | private int m_shares;
15 | private double m_price;
16 | private int m_permId;
17 | private int m_liquidation;
18 | private int m_cumQty;
19 | private double m_avgPrice;
20 | private String m_orderRef;
21 | private String m_evRule;
22 | private double m_evMultiplier;
23 |
24 | // Get
25 | public int orderId() { return m_orderId; }
26 | public int clientId() { return m_clientId; }
27 | public String execId() { return m_execId; }
28 | public String time() { return m_time; }
29 | public String acctNumber() { return m_acctNumber; }
30 | public String exchange() { return m_exchange; }
31 | public String side() { return m_side; }
32 | public int shares() { return m_shares; }
33 | public double price() { return m_price; }
34 | public int permId() { return m_permId; }
35 | public int liquidation() { return m_liquidation; }
36 | public int cumQty() { return m_cumQty; }
37 | public double avgPrice() { return m_avgPrice; }
38 | public String orderRef() { return m_orderRef; }
39 | public String evRule() { return m_evRule; }
40 | public double evMultiplier() { return m_evMultiplier; }
41 |
42 | // Set
43 | public void orderId(int orderId) { m_orderId = orderId; }
44 | public void clientId(int clientId) { m_clientId = clientId; }
45 | public void execId(String execId) { m_execId = execId; }
46 | public void time(String time) { m_time = time; }
47 | public void acctNumber(String acctNumber) { m_acctNumber = acctNumber; }
48 | public void exchange(String exchange) { m_exchange = exchange; }
49 | public void side(String side) { m_side = side; }
50 | public void shares(int shares) { m_shares = shares; }
51 | public void price(double price) { m_price = price; }
52 | public void permId(int permId) { m_permId = permId; }
53 | public void liquidation(int liquidation) { m_liquidation = liquidation; }
54 | public void cumQty(int cumQty) { m_cumQty = cumQty; }
55 | public void avgPrice(double avgPrice) { m_avgPrice = avgPrice; }
56 | public void orderRef(String orderRef) { m_orderRef = orderRef; }
57 | public void evRule(String evRule) { m_evRule = evRule; }
58 | public void evMultiplier(double evMultiplier) { m_evMultiplier = evMultiplier; }
59 |
60 | public Execution() {
61 | m_orderId = 0;
62 | m_clientId = 0;
63 | m_shares = 0;
64 | m_price = 0;
65 | m_permId = 0;
66 | m_liquidation = 0;
67 | m_cumQty = 0;
68 | m_avgPrice = 0;
69 | m_evMultiplier = 0;
70 | }
71 |
72 | public Execution( int p_orderId, int p_clientId, String p_execId, String p_time,
73 | String p_acctNumber, String p_exchange, String p_side, int p_shares,
74 | double p_price, int p_permId, int p_liquidation, int p_cumQty,
75 | double p_avgPrice, String p_orderRef, String p_evRule, double p_evMultiplier) {
76 | m_orderId = p_orderId;
77 | m_clientId = p_clientId;
78 | m_execId = p_execId;
79 | m_time = p_time;
80 | m_acctNumber = p_acctNumber;
81 | m_exchange = p_exchange;
82 | m_side = p_side;
83 | m_shares = p_shares;
84 | m_price = p_price;
85 | m_permId = p_permId;
86 | m_liquidation = p_liquidation;
87 | m_cumQty = p_cumQty;
88 | m_avgPrice = p_avgPrice;
89 | m_orderRef = p_orderRef;
90 | m_evRule = p_evRule;
91 | m_evMultiplier = p_evMultiplier;
92 | }
93 |
94 | public boolean equals(Object p_other) {
95 | boolean l_bRetVal = false;
96 |
97 | if ( p_other == null ) {
98 | l_bRetVal = false;
99 | }
100 | else if ( this == p_other ) {
101 | l_bRetVal = true;
102 | }
103 | else {
104 | Execution l_theOther = (Execution)p_other;
105 | l_bRetVal = m_execId.equals( l_theOther.m_execId);
106 | }
107 | return l_bRetVal;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/ComboLeg.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import com.ib.client.Types.Action;
7 |
8 | public class ComboLeg {
9 | public enum OpenClose implements IApiEnum {
10 | Same, Open, Close, Unknown;
11 |
12 | static OpenClose get( int i) {
13 | return Types.getEnum( i, values() );
14 | }
15 |
16 | public String getApiString() {
17 | return "" + ordinal();
18 | }
19 | }
20 |
21 | private int m_conid;
22 | private int m_ratio;
23 | private String m_action = "BUY"; // BUY/SELL/SSHORT/SSHORTX
24 | private String m_exchange;
25 | private int m_openClose = 0; // Same
26 | // for stock legs when doing short sale
27 | private int m_shortSaleSlot; // 1 = clearing broker, 2 = third party
28 | private String m_designatedLocation;
29 | private int m_exemptCode;
30 |
31 | // Get
32 | public Action action() { return Action.valueOf(m_action); }
33 | public String getAction() { return m_action; }
34 | public int conid() { return m_conid; }
35 | public int exemptCode() { return m_exemptCode; }
36 | public int ratio() { return m_ratio; }
37 | public int shortSaleSlot() { return m_shortSaleSlot; }
38 | public OpenClose openClose() { return OpenClose.get(m_openClose); }
39 | public int getOpenClose() { return m_openClose; }
40 | public String designatedLocation() { return m_designatedLocation; }
41 | public String exchange() { return m_exchange; }
42 |
43 | // Set
44 | public void action(Action v) { m_action = ( v == null ) ? null : v.getApiString(); }
45 | public void action(String v) { m_action = v; }
46 | public void conid(int v) { m_conid = v; }
47 | public void designatedLocation(String v) { m_designatedLocation = v; }
48 | public void exchange(String v) { m_exchange = v; }
49 | public void exemptCode(int v) { m_exemptCode = v; }
50 | public void openClose(OpenClose v) { m_openClose = ( v == null ) ? null : v.ordinal(); }
51 | public void openClose(int v) { m_openClose = v; }
52 | public void ratio(int v) { m_ratio = v; }
53 | public void shortSaleSlot(int v) { m_shortSaleSlot = v; }
54 |
55 | public ComboLeg() {
56 | this(/* conId */ 0, /* ratio */ 0, /* action */ null,
57 | /* exchange */ null, /* openClose */ 0,
58 | /* shortSaleSlot */ 0, /* designatedLocation*/ null, /* exemptCode */ -1);
59 | }
60 |
61 | public ComboLeg(int p_conId, int p_ratio, String p_action, String p_exchange, int p_openClose) {
62 | this(p_conId, p_ratio, p_action, p_exchange, p_openClose,
63 | /* shortSaleSlot */ 0, /* designatedLocation*/ null, /* exemptCode */ -1);
64 |
65 | }
66 |
67 | public ComboLeg(int p_conId, int p_ratio, String p_action, String p_exchange,
68 | int p_openClose, int p_shortSaleSlot, String p_designatedLocation) {
69 | this(p_conId, p_ratio, p_action, p_exchange, p_openClose, p_shortSaleSlot, p_designatedLocation,
70 | /* exemptCode */ -1);
71 |
72 | }
73 |
74 | public ComboLeg(int p_conId, int p_ratio, String p_action, String p_exchange,
75 | int p_openClose, int p_shortSaleSlot, String p_designatedLocation, int p_exemptCode) {
76 | m_conid = p_conId;
77 | m_ratio = p_ratio;
78 | m_action = p_action;
79 | m_exchange = p_exchange;
80 | m_openClose = p_openClose;
81 | m_shortSaleSlot = p_shortSaleSlot;
82 | m_designatedLocation = p_designatedLocation;
83 | m_exemptCode = p_exemptCode;
84 | }
85 |
86 | public boolean equals(Object p_other) {
87 | if ( this == p_other ) {
88 | return true;
89 | }
90 | else if ( p_other == null ) {
91 | return false;
92 | }
93 |
94 | ComboLeg l_theOther = (ComboLeg)p_other;
95 |
96 | if (m_conid != l_theOther.m_conid ||
97 | m_ratio != l_theOther.m_ratio ||
98 | m_openClose != l_theOther.m_openClose ||
99 | m_shortSaleSlot != l_theOther.m_shortSaleSlot ||
100 | m_exemptCode != l_theOther.m_exemptCode) {
101 | return false;
102 | }
103 |
104 | if (Util.StringCompareIgnCase(m_action, l_theOther.m_action) != 0 ||
105 | Util.StringCompareIgnCase(m_exchange, l_theOther.m_exchange) != 0 ||
106 | Util.StringCompareIgnCase(m_designatedLocation, l_theOther.m_designatedLocation) != 0) {
107 | return false;
108 | }
109 |
110 | return true;
111 | }
112 |
113 | @Override public String toString() {
114 | return String.format( "%s %s %s", m_action, m_ratio, m_conid);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/HistoricalRateLimiter.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import java.util.Calendar
4 |
5 | import com.google.common.collect.TreeMultimap
6 | import scala.collection.JavaConversions._
7 | import org.slf4j.{Logger, LoggerFactory}
8 |
9 |
10 | /**
11 | * Stateful utility to account for historical requests in order no to violate historical data limitations as specified in:
12 | * https://www.interactivebrokers.com/en/software/api/apiguide/tables/historical_data_limitations.htm
13 | *
14 | * The basic usage is done through the following calls:
15 | *
16 | * Requests should be accounted for with the "requested" method
17 | * nextRequestAfter_ms gives us the wait time until the next request can be made
18 | * cleanup will free the accounting of old requests, should be called periodically
19 | *
20 | * @author piotr 01.03.15
21 | * Limitations:
22 | * 1. Making identical historical data requests within 15 seconds;
23 | * 2. Making six or more historical data requests for the same Contract, Exchange and Tick Type within two seconds.
24 | * 3. Do not make more than 60 historical data requests in any ten-minute period.
25 | */
26 | class HistoricalRateLimiter {
27 | private[this] val log: Logger = LoggerFactory.getLogger(this.getClass)
28 | // latest times are first
29 | // map time of request in millis to request
30 | private[this] val requests = TreeMultimap.create[Long, HistoricalRequest](
31 | implicitly[Ordering[Long]].reverse,
32 | implicitly[Ordering[HistoricalRequest]]
33 | )
34 |
35 | def now_ms: Long = Calendar.getInstance().getTimeInMillis()
36 |
37 | /**
38 | * Account for an historical request in this rate limiter
39 | * @param request
40 | * @param reftime_ms
41 | */
42 | def requested(request: HistoricalRequest, reftime_ms: Option[Long] = None): Unit = synchronized {
43 | var curTime = reftime_ms.getOrElse(now_ms)
44 | while (requests.put(curTime, request) == false) {
45 | log.warn(s"Incrememtomg reftime of request ${request}, another request with the same time ${curTime}")
46 | curTime += 1
47 | }
48 | }
49 |
50 | protected def latestInLast(timeframe_ms: Long, reftime_ms: Long = now_ms): Iterator[java.util.Map.Entry[Long, HistoricalRequest]] = {
51 | requests.entries.iterator.takeWhile { x ⇒ x.getKey > reftime_ms - timeframe_ms}
52 | }
53 |
54 | /**
55 | * @param timeframe_ms timeframe to consider
56 | * @param numRequests the allowed number of requests in this timeframe
57 | * @param filter a comparison functor to filter only the given requests if nonEmpty
58 | * @param reftime_ms the reference time (now)
59 | * @return number of ms after we are able to have numRequests requests or less in the given timeframe with the optional filter
60 | */
61 | def nextSlot_ms(timeframe_ms: Long, numRequests: Int, filter: Option[(HistoricalRequest) ⇒ Boolean] = None, reftime_ms: Long = now_ms): Long = {
62 | val latest = if (filter.isEmpty)
63 | latestInLast(timeframe_ms, reftime_ms).toVector
64 | else
65 | latestInLast(timeframe_ms, reftime_ms).toVector.filter(x ⇒ filter.get(x.getValue))
66 |
67 | if (latest.size >= numRequests) {
68 | val nextSlotIn_ms = timeframe_ms - (reftime_ms - latest.take(numRequests).last.getKey)
69 | if (nextSlotIn_ms > 0)
70 | return nextSlotIn_ms
71 | }
72 | 0L
73 | }
74 |
75 | /**
76 | * @param request type of request that we want to make
77 | * @param reftime_ms the reference time, what is considered "now", defaults to the current time
78 | * @return minimum milliseconds to wait after we can make the next request without violating the limits
79 | */
80 | def nextRequestAfter_ms(request: HistoricalRequest, reftime_ms: Long = now_ms): Long = synchronized {
81 | var after_ms = 0L
82 | // Rate limit on restriction 1
83 | // this is conservative as some arguments might make a different request, but it's tricky (BID_ASK for example counts as two)
84 | val identical = { req: HistoricalRequest ⇒
85 | req == request
86 | }
87 | after_ms = Math.max(after_ms, nextSlot_ms(15L * 1000, 1, Some(identical), reftime_ms))
88 | log.debug(s"delayed ${after_ms} (identical requests within 15 s)")
89 |
90 | // restriction 2
91 | val sameContract = { req: HistoricalRequest ⇒
92 | (req.contract, req.exchange, req.barSize) == (request.contract, request.exchange, req.barSize)
93 | }
94 | after_ms = Math.max(after_ms, nextSlot_ms(2L * 1000, 5, Some(sameContract), reftime_ms))
95 | log.debug(s"delayed ${after_ms} 6 or more same contract within 2 s")
96 |
97 | // restriction 3
98 | after_ms = Math.max(after_ms, nextSlot_ms(10L * 60 * 1000, 60, None, reftime_ms))
99 | log.debug(s"delayed ${after_ms} no more than 60 in 10 min")
100 | if (after_ms > 0)
101 | log.info(s"HistoricalRateLimiter, delaying request for ${after_ms/1000} s.")
102 | after_ms
103 | }
104 |
105 | def registerAndGetWait_ms(request: HistoricalRequest): Long = synchronized {
106 | val wait_ms = nextRequestAfter_ms(request)
107 | requested(request, Some(now_ms + wait_ms))
108 | wait_ms
109 | }
110 |
111 | def cleanupAfter(time_ms: Long): Unit = synchronized {
112 | val expired = requests.keys.filter { x ⇒ x < time_ms }
113 | expired.foreach { key ⇒
114 | requests.removeAll(key)
115 | }
116 | }
117 |
118 | def cleanup(reftime_ms: Long = now_ms): Unit = {
119 | cleanupAfter(reftime_ms + 10L * 60 * 1000)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/test/scala/com/larroy/ibclient/IBClientSpec.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.Date
5 | import java.util.concurrent.{ArrayBlockingQueue, TimeUnit}
6 |
7 | import com.ib.client.Types.{BarSize, DurationUnit, WhatToShow, SecType}
8 | import com.larroy.ibclient.contract.{CashContract, StockContract}
9 | import com.typesafe.config.ConfigFactory
10 | import org.slf4j.{Logger, LoggerFactory}
11 |
12 | import scala.collection.mutable.ArrayBuffer
13 | import scala.concurrent.Await
14 | import scala.concurrent.duration._
15 |
16 | /**
17 | * @author piotr 11.02.15
18 | */
19 |
20 | import org.specs2.mutable._
21 |
22 | class IBClientSpec extends Specification {
23 | private val log: Logger = LoggerFactory.getLogger(this.getClass)
24 | private val cfg = ConfigFactory.load()
25 | .withFallback(ConfigFactory.parseString(defaultConfig))
26 | .getConfig("ibclient")
27 |
28 | var (host, port, clientId) = (cfg.getString("tws.host"), cfg.getInt("tws.port"), cfg.getInt("tws.clientId"))
29 |
30 | val ibclient: IBClient = {
31 | new IBClient(host, port, clientId).connectBlocking(cfg.getInt("tws.timeout_s"))
32 | }
33 |
34 | def testWaitDuration = Duration(cfg.getInt("tws.timeout_s"), SECONDS)
35 |
36 | def testStockContract: StockContract = {
37 | new StockContract(
38 | cfg.getString("params.stock.contract"),
39 | cfg.getString("params.stock.exchange"),
40 | cfg.getString("params.stock.currency")
41 | )
42 | }
43 |
44 | "IBClientSpec" should {
45 | "contract details" in {
46 | val stockContract = testStockContract
47 | val futureContractDetails = ibclient.contractDetails(stockContract)
48 | val contractDetails = Await.result(futureContractDetails, testWaitDuration)
49 | contractDetails must not be empty
50 | }
51 |
52 | "historical data" in {
53 | val stockContract = testStockContract
54 | val res = ibclient.historicalData(stockContract, new Date(), 10,
55 | DurationUnit.DAY, BarSize._1_hour, WhatToShow.MIDPOINT, false
56 | )
57 | val hist = Await.result(res, testWaitDuration)
58 | hist must not be empty
59 | }
60 |
61 | "easy historical data" in {
62 | import org.joda.time._
63 | val stockContract = testStockContract
64 | val startDate = new DateTime(2015, 3, 1, 15, 0).toDate
65 | val endDate = new DateTime(2015, 3, 3, 15, 0).toDate
66 | val res = ibclient.easyHistoricalData(stockContract, startDate, endDate, BarSize._1_min, WhatToShow.TRADES)
67 | val hist = Await.result(res, testWaitDuration)
68 | hist must not be empty
69 | }
70 |
71 | "get easy historical data in order" in {
72 | import org.joda.time._
73 | val stockContract = testStockContract
74 | val startDate = new DateTime(2008, 3, 3, 15, 0).toDate
75 | val endDate = new DateTime(2015, 12, 31, 15, 0).toDate
76 | val res = ibclient.easyHistoricalData(stockContract, startDate, endDate, BarSize._1_day, WhatToShow.TRADES)
77 | val hist = Await.result(res, testWaitDuration)
78 | hist must not be empty
79 | hist mustEqual hist.sorted(Ordering[Bar].reverse)
80 | }
81 |
82 | "market data" in {
83 | val result = ArrayBuffer.empty[Tick]
84 | val subscription = ibclient.marketData(new CashContract("EUR", "EUR.USD"))
85 | val currThread = Thread.currentThread()
86 | subscription.observableTick.subscribe(
87 | { tick ⇒
88 | log.debug(s"Got tick ${tick}")
89 | result += tick
90 | if (result.length >= 3) {
91 | log.debug(s"Closing subscription: ${subscription.id}")
92 | subscription.close()
93 | }
94 | },
95 | {error ⇒ throw (error)},
96 | {() ⇒
97 | println("Closed")
98 | currThread.interrupt()
99 | }
100 | )
101 | try {
102 | Thread.sleep(testWaitDuration.toMillis)
103 | log.error("Timeout waiting for market data")
104 | } catch {
105 | case e: InterruptedException ⇒
106 | }
107 | ((result.length >= 1) must beTrue).setMessage("We didn't receive market data")
108 | }
109 |
110 | "positions" in {
111 | val pos = Await.result(ibclient.positions(), testWaitDuration)
112 | pos must not be empty
113 | }
114 | "realtime bars" in {
115 | val subscription = ibclient.realtimeBars(new CashContract("EUR", "EUR.USD"))
116 | // The functions passed to subscribe are executed in the EReader thread
117 | // This can be changed by using observeOn
118 | // subscription.observableBar.observeOn(ComputationScheduler()).subscribe({bar=>log.debug(s"got bar ${bar}")},{error ⇒ throw (error)})
119 | val bars = new ArrayBlockingQueue[Bar](64)
120 | subscription.observableBar.subscribe(
121 | { bar =>
122 | log.debug(s"got bar ${bar}")
123 | val succ = bars.offer(bar)
124 | if (! succ)
125 | log.debug("Bar queue full")
126 | },
127 | { error ⇒
128 | throw error
129 | }
130 | )
131 | val bar = Option(bars.poll(3, TimeUnit.SECONDS))
132 | subscription.close
133 | if (bar.isEmpty)
134 | log.warn("We didn't receive market data, are we outside RTH?")
135 | //(bar must not be empty).setMessage("We didn't receive market data")
136 | success
137 | }
138 | "return a failed future when it can't connect" in {
139 | val futureIBclient = new IBClient("host.invalid", port, clientId).connect()
140 | Await.result(futureIBclient, testWaitDuration) must throwAn[Exception]
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/scalastyle-config.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 | Scalastyle standard configuration
8 |
9 |
10 |
11 |
12 |
13 |
14 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | !
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/util/HistoryLimits.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient.util
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.Date
5 |
6 | import com.ib.client.Types.{DurationUnit, BarSize, WhatToShow}
7 | import com.larroy.ibclient
8 | import com.larroy.ibclient.{IBApiError, IBClient}
9 | import com.larroy.ibclient.contract.StockContract
10 | import com.typesafe.config.ConfigFactory
11 | import org.slf4j.{Logger, LoggerFactory}
12 |
13 | import scala.concurrent.Await
14 | import scala.concurrent.duration._
15 | import scala.util.{Success, Failure}
16 |
17 | import net.ceedubs.ficus.Ficus._
18 |
19 | object HistoryLimits {
20 | private[this] val cfg = ConfigFactory.load()
21 | .withFallback(ConfigFactory.parseString(ibclient.defaultConfig))
22 | .getConfig("ibclient.historyLimits")
23 |
24 | /**
25 | * @param durationUnit
26 | * @param barSize
27 | * @return the maximum number of bar (duration) that can be requested with the
28 | * given parameters or None if the combination is not valid.
29 | */
30 | def apply(durationUnit: DurationUnit, barSize: BarSize): Option[Int] = {
31 | val path = s"${durationUnit.name}.${barSize.name}"
32 | cfg.as[Option[Int]](path)
33 | }
34 |
35 | def bestDuration(startDate: Date, endDate: Date, barSize: BarSize): HistoryDuration = {
36 | import org.joda.time._
37 | val endDateTime = new DateTime(endDate)
38 | val startDateTime = new DateTime(startDate)
39 | val seconds = Math.abs(Seconds.secondsBetween(startDateTime, endDateTime).getSeconds)
40 |
41 | val secondsInDay = 24 * 60 * 60
42 | val daysRem = if (seconds/secondsInDay > 0 && seconds % secondsInDay != 0) 1 else 0
43 | val days = seconds / secondsInDay + daysRem
44 |
45 | val secondsInWeek = 7 * secondsInDay
46 | val weeksRem = if (seconds / secondsInWeek > 0 && seconds % secondsInWeek != 0) 1 else 0
47 | val weeks = seconds / secondsInWeek + weeksRem
48 |
49 | val secondsInMonth = 30 * secondsInDay
50 | val monthsRem = if (seconds / secondsInMonth > 0 && seconds % secondsInMonth != 0) 1 else 0
51 | val months = seconds / secondsInMonth + monthsRem
52 |
53 | val secondsInYear = 365 * secondsInDay
54 | val yearsRem = if (seconds / secondsInYear > 0 && seconds % secondsInYear != 0) 1 else 0
55 | val years = seconds / secondsInYear
56 |
57 | if (years == 0 && months == 0 && weeks == 0 && days == 0 && barSize.ordinal > BarSize._1_hour.ordinal)
58 | new HistoryDuration(Math.max(days, 1), this(DurationUnit.DAY, barSize).getOrElse(1), DurationUnit.DAY)
59 |
60 | else if (years == 0 && months == 0 && weeks == 0 && days <= 1 && barSize.ordinal < BarSize._1_day.ordinal)
61 | new HistoryDuration(Math.max(seconds, 1), this(DurationUnit.SECOND, barSize).getOrElse(1), DurationUnit.SECOND)
62 |
63 | else if (years == 0 && months == 0 && weeks == 0 || barSize.ordinal <= BarSize._5_mins.ordinal)
64 | new HistoryDuration(Math.max(days, 1), this(DurationUnit.DAY, barSize).getOrElse(1), DurationUnit.DAY)
65 |
66 | else if (years == 0 && months == 0 || barSize.ordinal <= BarSize._15_mins.ordinal)
67 | new HistoryDuration(Math.max(weeks, 1), this(DurationUnit.WEEK, barSize).getOrElse(1), DurationUnit.WEEK)
68 |
69 | else if (years == 0 || barSize.ordinal <= BarSize._1_hour.ordinal)
70 | new HistoryDuration(Math.max(months, 1), this(DurationUnit.MONTH, barSize).getOrElse(1), DurationUnit.MONTH)
71 |
72 | else
73 | new HistoryDuration(Math.max(years, 1), this(DurationUnit.YEAR, barSize).getOrElse(1), DurationUnit.YEAR)
74 | }
75 | }
76 |
77 | /**
78 | * @author piotr 01.03.15
79 | * History limits are not clearly documented, this class can automatically find the limits via calibrate
80 | */
81 | class HistoryLimits {
82 | import com.larroy.ibclient.defaultConfig
83 | private[this] val log: Logger = LoggerFactory.getLogger(this.getClass)
84 | private[this] val cfg = ConfigFactory.load()
85 | .withFallback(ConfigFactory.parseString(defaultConfig))
86 | .getConfig("ibclient")
87 |
88 | private[this] val ibclient = connectedClient
89 | private[this] val endDate = new Date()
90 | private[this] val contract = testStockContract
91 |
92 | def testWaitDuration = scala.concurrent.duration.Duration(cfg.getInt("tws.timeout_s"), SECONDS)
93 |
94 | def testStockContract: StockContract = {
95 | new StockContract(
96 | cfg.getString("params.stock.contract"),
97 | cfg.getString("params.stock.exchange"),
98 | cfg.getString("params.stock.currency")
99 | )
100 | }
101 |
102 | def connectedClient: IBClient = {
103 | val ibclient = new IBClient(cfg.getString("tws.host"), cfg.getInt("tws.port"), cfg.getInt("tws.clientId"))
104 | Await.result(ibclient.connect(), testWaitDuration)
105 | ibclient
106 | }
107 |
108 | def validDuration(barSize: BarSize, durationUnit: DurationUnit, duration: Int): Boolean = {
109 | log.debug(s"validDuration ${barSize} ${durationUnit} ${duration}")
110 | val futureBars = ibclient.historicalData(contract, endDate, duration,
111 | durationUnit, barSize, WhatToShow.TRADES, false
112 | )
113 | Thread.sleep(16000)
114 | Await.ready(futureBars, scala.concurrent.duration.Duration.Inf).value match {
115 | case Some(Failure(e)) ⇒ {
116 | log.debug(s"fail")
117 | false
118 | }
119 | case Some(Success(bars)) ⇒ {
120 | log.debug(s"success, ${bars.size} bars")
121 | true
122 | }
123 | case _ ⇒ {
124 | assert(false)
125 | false
126 | }
127 | }
128 | }
129 |
130 | def findLimits (barsizes: Array[BarSize], durations: Array[DurationUnit]): Seq[ValidDurations] =
131 | {
132 | implicit class Crossable[X](xs: Traversable[X]) {
133 | def cross[Y](ys: Traversable[Y]) = for {x <- xs; y <- ys} yield (x, y)
134 | }
135 | val candidates = barsizes.toVector cross durations.toVector
136 | def from(start: Int): Stream[Int] = Stream.cons(start, from(start + 1))
137 | val validDurations = candidates.map { x ⇒
138 | val barSize = x._1
139 | val durationUnit = x._2
140 | new ValidDurations(barSize, durationUnit, from(1).takeWhile(validDuration(barSize, durationUnit, _)).toArray)
141 | }.toSeq
142 | validDurations
143 | }
144 |
145 | /**
146 | * Finds the maximum set of [[ValidDurations]] for history requests
147 | * @return
148 | */
149 | def calibrate(): Seq[ValidDurations] = {
150 | val barsizes = Array[BarSize](BarSize._15_mins, BarSize._1_hour, BarSize._4_hours, BarSize._1_day, BarSize._1_week)
151 | val durations = DurationUnit.values().drop(1)
152 | val limits = findLimits(barsizes, durations)
153 | limits
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/EClientErrors.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 |
7 | public class EClientErrors {
8 | public static final int NO_VALID_ID = -1;
9 | static final CodeMsgPair ALREADY_CONNECTED = new CodeMsgPair(501, "Already connected.");
10 | static final CodeMsgPair CONNECT_FAIL = new CodeMsgPair(502, "Couldn't connect to TWS. Confirm that \"Enable ActiveX and Socket Clients\" is enabled on the TWS \"Configure->API\" menu.");
11 | public static final CodeMsgPair UPDATE_TWS = new CodeMsgPair(503, "The TWS is out of date and must be upgraded.");
12 | public static final CodeMsgPair NOT_CONNECTED = new CodeMsgPair(504, "Not connected");
13 | static final CodeMsgPair UNKNOWN_ID = new CodeMsgPair(505, "Fatal Error: Unknown message id.");
14 | static final CodeMsgPair UNSUPPORTED_VERSION = new CodeMsgPair(506, "Unsupported Version");
15 | static final CodeMsgPair BAD_LENGTH = new CodeMsgPair(507, "Bad Message Length");
16 | static final CodeMsgPair BAD_MESSAGE = new CodeMsgPair(508, "Bad Message");
17 | static final CodeMsgPair FAIL_SEND = new CodeMsgPair(509, "Failed to send message - "); // generic message; all future messages should use this
18 | static final CodeMsgPair FAIL_SEND_REQMKT = new CodeMsgPair(510, "Request Market Data Sending Error - ");
19 | static final CodeMsgPair FAIL_SEND_CANMKT = new CodeMsgPair(511, "Cancel Market Data Sending Error - ");
20 | static final CodeMsgPair FAIL_SEND_ORDER = new CodeMsgPair(512, "Order Sending Error - ");
21 | static final CodeMsgPair FAIL_SEND_ACCT = new CodeMsgPair(513, "Account Update Request Sending Error -");
22 | static final CodeMsgPair FAIL_SEND_EXEC = new CodeMsgPair(514, "Request For Executions Sending Error -");
23 | static final CodeMsgPair FAIL_SEND_CORDER = new CodeMsgPair(515, "Cancel Order Sending Error -");
24 | static final CodeMsgPair FAIL_SEND_OORDER = new CodeMsgPair(516, "Request Open Order Sending Error -");
25 | static final CodeMsgPair UNKNOWN_CONTRACT = new CodeMsgPair(517, "Unknown contract. Verify the contract details supplied.");
26 | static final CodeMsgPair FAIL_SEND_REQCONTRACT = new CodeMsgPair(518, "Request Contract Data Sending Error - ");
27 | static final CodeMsgPair FAIL_SEND_REQMKTDEPTH = new CodeMsgPair(519, "Request Market Depth Sending Error - ");
28 | static final CodeMsgPair FAIL_SEND_CANMKTDEPTH = new CodeMsgPair(520, "Cancel Market Depth Sending Error - ");
29 | static final CodeMsgPair FAIL_SEND_SERVER_LOG_LEVEL = new CodeMsgPair(521, "Set Server Log Level Sending Error - ");
30 | static final CodeMsgPair FAIL_SEND_FA_REQUEST = new CodeMsgPair(522, "FA Information Request Sending Error - ");
31 | static final CodeMsgPair FAIL_SEND_FA_REPLACE = new CodeMsgPair(523, "FA Information Replace Sending Error - ");
32 | static final CodeMsgPair FAIL_SEND_REQSCANNER = new CodeMsgPair(524, "Request Scanner Subscription Sending Error - ");
33 | static final CodeMsgPair FAIL_SEND_CANSCANNER = new CodeMsgPair(525, "Cancel Scanner Subscription Sending Error - ");
34 | static final CodeMsgPair FAIL_SEND_REQSCANNERPARAMETERS = new CodeMsgPair(526, "Request Scanner Parameter Sending Error - ");
35 | static final CodeMsgPair FAIL_SEND_REQHISTDATA = new CodeMsgPair(527, "Request Historical Data Sending Error - ");
36 | static final CodeMsgPair FAIL_SEND_CANHISTDATA = new CodeMsgPair(528, "Request Historical Data Sending Error - ");
37 | static final CodeMsgPair FAIL_SEND_REQRTBARS = new CodeMsgPair(529, "Request Real-time Bar Data Sending Error - ");
38 | static final CodeMsgPair FAIL_SEND_CANRTBARS = new CodeMsgPair(530, "Cancel Real-time Bar Data Sending Error - ");
39 | static final CodeMsgPair FAIL_SEND_REQCURRTIME = new CodeMsgPair(531, "Request Current Time Sending Error - ");
40 | static final CodeMsgPair FAIL_SEND_REQFUNDDATA = new CodeMsgPair(532, "Request Fundamental Data Sending Error - ");
41 | static final CodeMsgPair FAIL_SEND_CANFUNDDATA = new CodeMsgPair(533, "Cancel Fundamental Data Sending Error - ");
42 | static final CodeMsgPair FAIL_SEND_REQCALCIMPLIEDVOLAT = new CodeMsgPair(534, "Request Calculate Implied Volatility Sending Error - ");
43 | static final CodeMsgPair FAIL_SEND_REQCALCOPTIONPRICE = new CodeMsgPair(535, "Request Calculate Option Price Sending Error - ");
44 | static final CodeMsgPair FAIL_SEND_CANCALCIMPLIEDVOLAT = new CodeMsgPair(536, "Cancel Calculate Implied Volatility Sending Error - ");
45 | static final CodeMsgPair FAIL_SEND_CANCALCOPTIONPRICE = new CodeMsgPair(537, "Cancel Calculate Option Price Sending Error - ");
46 | static final CodeMsgPair FAIL_SEND_REQGLOBALCANCEL = new CodeMsgPair(538, "Request Global Cancel Sending Error - ");
47 | static final CodeMsgPair FAIL_SEND_REQMARKETDATATYPE = new CodeMsgPair(539, "Request Market Data Type Sending Error - ");
48 | static final CodeMsgPair FAIL_SEND_REQPOSITIONS = new CodeMsgPair(540, "Request Positions Sending Error - ");
49 | static final CodeMsgPair FAIL_SEND_CANPOSITIONS = new CodeMsgPair(541, "Cancel Positions Sending Error - ");
50 | static final CodeMsgPair FAIL_SEND_REQACCOUNTDATA = new CodeMsgPair(542, "Request Account Data Sending Error - ");
51 | static final CodeMsgPair FAIL_SEND_CANACCOUNTDATA = new CodeMsgPair(543, "Cancel Account Data Sending Error - ");
52 | static final CodeMsgPair FAIL_SEND_VERIFYREQUEST = new CodeMsgPair(544, "Verify Request Sending Error - ");
53 | static final CodeMsgPair FAIL_SEND_VERIFYMESSAGE = new CodeMsgPair(545, "Verify Message Sending Error - ");
54 | static final CodeMsgPair FAIL_SEND_QUERYDISPLAYGROUPS = new CodeMsgPair(546, "Query Display Groups Sending Error - ");
55 | static final CodeMsgPair FAIL_SEND_SUBSCRIBETOGROUPEVENTS = new CodeMsgPair(547, "Subscribe To Group Events Sending Error - ");
56 | static final CodeMsgPair FAIL_SEND_UPDATEDISPLAYGROUP = new CodeMsgPair(548, "Update Display Group Sending Error - ");
57 | static final CodeMsgPair FAIL_SEND_UNSUBSCRIBEFROMGROUPEVENTS = new CodeMsgPair(549, "Unsubscribe From Group Events Sending Error - ");
58 | static final CodeMsgPair FAIL_SEND_STARTAPI = new CodeMsgPair(550, "Start API Sending Error - ");
59 | static final CodeMsgPair FAIL_SEND_VERIFYANDAUTHREQUEST = new CodeMsgPair(551, "Verify And Auth Request Sending Error - ");
60 | static final CodeMsgPair FAIL_SEND_VERIFYANDAUTHMESSAGE = new CodeMsgPair(552, "Verify And Auth Message Sending Error - ");
61 |
62 | public EClientErrors() {
63 | }
64 |
65 | static public class CodeMsgPair {
66 |
67 | // members vars
68 | int m_errorCode;
69 | String m_errorMsg;
70 |
71 | // Get/Set methods
72 | public int code() { return m_errorCode; }
73 | public String msg() { return m_errorMsg; }
74 |
75 | /** Constructor */
76 | public CodeMsgPair(int i, String errString) {
77 | m_errorCode = i;
78 | m_errorMsg = errString;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/Tick.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import com.ib.client.TickType
4 |
5 | /**
6 | * @author piotr 14.02.15
7 | */
8 |
9 | object Tick {
10 | def apply(numericTickType: Int, value: Double): Tick = {
11 | val tickType = TickType.get(numericTickType)
12 | numericTickType match {
13 | case 0 => new TickBidSize(tickType, value)
14 | case 1 => new TickBid(tickType, value)
15 | case 2 => new TickAsk(tickType, value)
16 | case 3 => new TickAskSize(tickType, value)
17 | case 4 => new TickLast(tickType, value)
18 | case 5 => new TickLastSize(tickType, value)
19 | case 6 => new TickHigh(tickType, value)
20 | case 7 => new TickLow(tickType, value)
21 | case 8 => new TickVolume(tickType, value)
22 | case 9 => new TickClose(tickType, value)
23 | case 10 => new TickBidOption(tickType, value)
24 | case 11 => new TickAskOption(tickType, value)
25 | case 12 => new TickLastOption(tickType, value)
26 | case 13 => new TickModelOption(tickType, value)
27 | case 14 => new TickOpen(tickType, value)
28 | case 21 => new TickAvgVolume(tickType, value)
29 | case 22 => new TickOpenInterest(tickType, value)
30 | case 23 => new TickOptionHistoricalVol(tickType, value)
31 | case 24 => new TickOptionImpliedVol(tickType, value)
32 | case 25 => new TickOptionBidExch(tickType, value)
33 | case 26 => new TickOptionAskExch(tickType, value)
34 | case 27 => new TickOptionCallOpenInterest(tickType, value)
35 | case 28 => new TickOptionPutOpenInterest(tickType, value)
36 | case 29 => new TickOptionCallVolume(tickType, value)
37 | case 30 => new TickOptionPutVolume(tickType, value)
38 | case 31 => new TickIndexFuturePremium(tickType, value)
39 | case 32 => new TickBidExch(tickType, value)
40 | case 33 => new TickAskExch(tickType, value)
41 | case 34 => new TickAuctionVolume(tickType, value)
42 | case 35 => new TickAuctionPrice(tickType, value)
43 | case 36 => new TickAuctionImbalance(tickType, value)
44 | case 37 => new TickMarkPrice(tickType, value)
45 | case 38 => new TickBidEfpComputation(tickType, value)
46 | case 39 => new TickAskEfpComputation(tickType, value)
47 | case 40 => new TickLastEfpComputation(tickType, value)
48 | case 41 => new TickOpenEfpComputation(tickType, value)
49 | case 42 => new TickHighEfpComputation(tickType, value)
50 | case 43 => new TickLowEfpComputation(tickType, value)
51 | case 44 => new TickCloseEfpComputation(tickType, value)
52 | case 45 => new TickLastTimestamp(tickType, value)
53 | case 46 => new TickShortable(tickType, value)
54 | case 47 => new TickFundamentalRatios(tickType, value)
55 | case 48 => new TickRtVolume(tickType, value)
56 | case 49 => new TickHalted(tickType, value)
57 | case 50 => new TickBidYield(tickType, value)
58 | case 51 => new TickAskYield(tickType, value)
59 | case 52 => new TickLastYield(tickType, value)
60 | case 53 => new TickCustOptionComputation(tickType, value)
61 | case 54 => new TickTradeCount(tickType, value)
62 | case 57 => new TickLastRthTrade(tickType, value)
63 | case 58 => new TickRtHistoricalVol(tickType, value)
64 | case 61 => new TickRegulatoryImbalance(tickType, value)
65 | case _ => new TickUnknown(tickType, value)
66 | }
67 | }
68 | }
69 |
70 | class Tick(tickType: TickType, value: Double)
71 |
72 | case class TickBidSize(tickType: TickType, value: Double) extends Tick(tickType, value)
73 |
74 | case class TickBid(tickType: TickType, value: Double) extends Tick(tickType, value)
75 |
76 | case class TickAsk(tickType: TickType, value: Double) extends Tick(tickType, value)
77 |
78 | case class TickAskSize(tickType: TickType, value: Double) extends Tick(tickType, value)
79 |
80 | case class TickLast(tickType: TickType, value: Double) extends Tick(tickType, value)
81 |
82 | case class TickLastSize(tickType: TickType, value: Double) extends Tick(tickType, value)
83 |
84 | case class TickHigh(tickType: TickType, value: Double) extends Tick(tickType, value)
85 |
86 | case class TickLow(tickType: TickType, value: Double) extends Tick(tickType, value)
87 |
88 | case class TickVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
89 |
90 | case class TickClose(tickType: TickType, value: Double) extends Tick(tickType, value)
91 |
92 | case class TickBidOption(tickType: TickType, value: Double) extends Tick(tickType, value)
93 |
94 | case class TickAskOption(tickType: TickType, value: Double) extends Tick(tickType, value)
95 |
96 | case class TickLastOption(tickType: TickType, value: Double) extends Tick(tickType, value)
97 |
98 | case class TickModelOption(tickType: TickType, value: Double) extends Tick(tickType, value)
99 |
100 | case class TickOpen(tickType: TickType, value: Double) extends Tick(tickType, value)
101 |
102 | case class TickAvgVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
103 |
104 | case class TickOpenInterest(tickType: TickType, value: Double) extends Tick(tickType, value)
105 |
106 | case class TickOptionHistoricalVol(tickType: TickType, value: Double) extends Tick(tickType, value)
107 |
108 | case class TickOptionImpliedVol(tickType: TickType, value: Double) extends Tick(tickType, value)
109 |
110 | case class TickOptionBidExch(tickType: TickType, value: Double) extends Tick(tickType, value)
111 |
112 | case class TickOptionAskExch(tickType: TickType, value: Double) extends Tick(tickType, value)
113 |
114 | case class TickOptionCallOpenInterest(tickType: TickType, value: Double) extends Tick(tickType, value)
115 |
116 | case class TickOptionPutOpenInterest(tickType: TickType, value: Double) extends Tick(tickType, value)
117 |
118 | case class TickOptionCallVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
119 |
120 | case class TickOptionPutVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
121 |
122 | case class TickIndexFuturePremium(tickType: TickType, value: Double) extends Tick(tickType, value)
123 |
124 | case class TickBidExch(tickType: TickType, value: Double) extends Tick(tickType, value)
125 |
126 | case class TickAskExch(tickType: TickType, value: Double) extends Tick(tickType, value)
127 |
128 | case class TickAuctionVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
129 |
130 | case class TickAuctionPrice(tickType: TickType, value: Double) extends Tick(tickType, value)
131 |
132 | case class TickAuctionImbalance(tickType: TickType, value: Double) extends Tick(tickType, value)
133 |
134 | case class TickMarkPrice(tickType: TickType, value: Double) extends Tick(tickType, value)
135 |
136 | case class TickBidEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
137 |
138 | case class TickAskEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
139 |
140 | case class TickLastEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
141 |
142 | case class TickOpenEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
143 |
144 | case class TickHighEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
145 |
146 | case class TickLowEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
147 |
148 | case class TickCloseEfpComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
149 |
150 | case class TickLastTimestamp(tickType: TickType, value: Double) extends Tick(tickType, value)
151 |
152 | case class TickShortable(tickType: TickType, value: Double) extends Tick(tickType, value)
153 |
154 | case class TickFundamentalRatios(tickType: TickType, value: Double) extends Tick(tickType, value)
155 |
156 | case class TickRtVolume(tickType: TickType, value: Double) extends Tick(tickType, value)
157 |
158 | case class TickHalted(tickType: TickType, value: Double) extends Tick(tickType, value)
159 |
160 | case class TickBidYield(tickType: TickType, value: Double) extends Tick(tickType, value)
161 |
162 | case class TickAskYield(tickType: TickType, value: Double) extends Tick(tickType, value)
163 |
164 | case class TickLastYield(tickType: TickType, value: Double) extends Tick(tickType, value)
165 |
166 | case class TickCustOptionComputation(tickType: TickType, value: Double) extends Tick(tickType, value)
167 |
168 | case class TickTradeCount(tickType: TickType, value: Double) extends Tick(tickType, value)
169 |
170 | case class TickLastRthTrade(tickType: TickType, value: Double) extends Tick(tickType, value)
171 |
172 | case class TickRtHistoricalVol(tickType: TickType, value: Double) extends Tick(tickType, value)
173 |
174 | case class TickRegulatoryImbalance(tickType: TickType, value: Double) extends Tick(tickType, value)
175 |
176 | case class TickUnknown(tickType: TickType, value: Double) extends Tick(tickType, value)
177 |
--------------------------------------------------------------------------------
/src/main/scala/com/larroy/ibclient/Main.scala:
--------------------------------------------------------------------------------
1 | package com.larroy.ibclient
2 |
3 | import java.io.File
4 | import java.text.SimpleDateFormat
5 | import java.util.Date
6 |
7 | import com.github.tototoshi.csv.CSVWriter
8 | import com.ib.client.Contract
9 | import com.ib.client.Types.{BarSize, DurationUnit, SecType, WhatToShow}
10 | import com.larroy.ibclient.contract.{CashContract, FutureContract, GenericContract, StockContract}
11 | import org.joda.time.{DateTime, DateTimeZone}
12 | import org.joda.time.format.DateTimeFormat
13 | import org.slf4j.{Logger, LoggerFactory}
14 |
15 | //import rx.schedulers.Schedulers
16 |
17 | import scala.concurrent.Await
18 | import scala.concurrent.duration.Duration
19 |
20 |
21 | object Mode extends Enumeration {
22 | type Mode = Value
23 | val Invalid, History = Value
24 | }
25 |
26 | import Mode._
27 |
28 | sealed case class Options(
29 | host: String = "localhost",
30 | port: Int = 7496,
31 | clientId: Int = 1,
32 | mode: Mode = Mode.Invalid,
33 | quiet: Boolean = false,
34 | contract: String = "",
35 | localSymbol: Option[String] = None,
36 | contractType: SecType = SecType.valueOf("STK"),
37 | contractExchange: String = "SMART",
38 | contractCurrency: String = "USD",
39 | contractExpiry: String = "",
40 | historyBarSize: BarSize = BarSize._1_min,
41 | historyStartDate: Date = new DateTime(DateTimeZone.UTC).minusDays(1).toDate,
42 | historyEndDate: Date = new DateTime(DateTimeZone.UTC).minusMinutes(1).toDate,
43 | historyOutFile: Option[String] = None
44 | )
45 |
46 | //historyEndDate: Option[String] = Some(DateTimeFormat.forPattern("yyyyMMdd HH:mm:ss z").print(new DateTime(DateTimeZone.UTC)))
47 | /**
48 | * @author piotr 19.10.14
49 | */
50 | object Main {
51 | private val log: Logger = LoggerFactory.getLogger(this.getClass)
52 | private val version = "0.1"
53 | val dateTimeFormat = DateTimeFormat.forPattern("yyyyMMdd HH:mm:ss")
54 |
55 | def getOptionParser: scopt.OptionParser[Options] = {
56 | val contractTypes = SecType.values().map(_.name)
57 | val durationUnits = DurationUnit.values().map(_.name)
58 | val barSizes = BarSize.values().map(_.name)
59 | val validDateRe = """(\d{8}) (\d{2}:\d{2}:\d{2}) ?(\w*)?""".r
60 | new scopt.OptionParser[Options]("ibclient") {
61 | head("ibclient", Main.version)
62 |
63 | override def showUsageOnError: Option[Boolean] = Some(true)
64 |
65 | help("help") text ("print help")
66 | /*
67 | * Common arguments
68 | */
69 | opt[Boolean]('q', "quiet") text ("suppress progress on stdout") action {
70 | (arg, dest) => dest.copy(quiet = arg)
71 | }
72 | opt[String]('h', "host") text ("host") action {
73 | (arg, dest) => dest.copy(host = arg)
74 | }
75 | opt[Int]('p', "port") text ("port") action {
76 | (arg, dest) => dest.copy(port = arg)
77 | }
78 | opt[Int]('i', "clientid") text ("client id") action {
79 | (arg, dest) => dest.copy(clientId = arg)
80 | }
81 | cmd("history") text ("history") action {
82 | (_, dest) => dest.copy(mode = Mode.History)
83 | } children(
84 | opt[String]('c', "contract") text ("contract") minOccurs (1) action {
85 | (arg, dest) => dest.copy(contract = arg)
86 | },
87 | opt[String]('s', "localsymbol") text ("localsymbol eg EUR.USD") action {
88 | (arg, dest) => dest.copy(localSymbol = Some(arg))
89 | },
90 | opt[String]('t', "type") text ("contract type") action {
91 | (arg, dest) => dest.copy(contractType = SecType.valueOf(arg))
92 | } validate (x => if (contractTypes.contains(x)) success else failure("unknown contract type")),
93 | note(s"contract type is one of: '${contractTypes.mkString(" ")}'"),
94 |
95 | opt[String]('e', "exchange") text ("exchange") action {
96 | (arg, dest) => dest.copy(contractExchange = arg)
97 | },
98 | opt[String]('x', "currency") text ("currency") action {
99 | (arg, dest) => dest.copy(contractCurrency = arg)
100 | },
101 | opt[String]('y', "expiry") text ("expiry") action {
102 | (arg, dest) => dest.copy(contractExpiry = arg)
103 | },
104 | opt[String]('a', "startdate") text ("startdate") action {
105 | (arg, dest) => dest.copy(historyStartDate = dateTimeFormat.parseDateTime(arg).toDate)
106 | } validate { x ⇒
107 | x match {
108 | case validDateRe(_*) ⇒ success
109 | case _ ⇒ failure(s"argument doesn't match ${validDateRe.toString}")
110 | }
111 | },
112 | note(s"date has format yyyyMMdd HH:mm:ss z"),
113 |
114 | opt[String]('z', "enddate") text ("enddate") action {
115 | (arg, dest) => dest.copy(historyEndDate = dateTimeFormat.parseDateTime(arg).toDate)
116 | } validate { x ⇒
117 | x match {
118 | case validDateRe(_*) ⇒ success
119 | case _ ⇒ failure(s"argument doesn't match ${validDateRe.toString}")
120 | }
121 | },
122 |
123 | opt[String]('b', "barsize") text ("bar size") action {
124 | (arg, dest) => dest.copy(historyBarSize = BarSize.valueOf(arg))
125 | } validate (x => if (barSizes.contains(x)) success else failure("unknown bar size")),
126 | note(s"duration unit is one of: '${barSizes.mkString(" ")}'"),
127 |
128 | opt[String]('o', "out") text ("output file") action {
129 | (arg, dest) => dest.copy(historyOutFile = Some(arg))
130 | },
131 | note(s"")
132 | )
133 | }
134 | }
135 |
136 | def main(args: Array[String]) {
137 | val optionParser = getOptionParser
138 | val options: Options = optionParser.parse(args, Options()).getOrElse {
139 | log.error("Option syntax incorrect")
140 | log.error(s"Arguments given ${args.mkString("'", "' '", "'")}")
141 | log.error("Failure.")
142 | sys.exit(1)
143 | }
144 |
145 | val success: Boolean = try options.mode match {
146 | case Mode.Invalid => {
147 | optionParser.reportError("Please specify a valid command")
148 | println(optionParser.usage)
149 | false
150 | }
151 | case Mode.History => {
152 | history(options)
153 | true
154 | }
155 | } catch {
156 | case e: Exception => {
157 | log.error(s"Exception thrown ${e.getMessage}")
158 | e.printStackTrace()
159 | false
160 | }
161 | }
162 |
163 | if (success) {
164 | log.info("Success.")
165 | log.info("=========== finished successfully ================")
166 | sys.exit(0)
167 | } else {
168 | log.error("Failure.")
169 | log.info("=========== finished with errors =================")
170 | sys.exit(-1)
171 | }
172 | }
173 |
174 | def history(options: Options): Unit = {
175 |
176 | val ibclient = new IBClient(options.host, options.port, options.clientId)
177 | log.info(s"Connecting to ${options.host}:${options.port} with client id: ${options.clientId}")
178 | Await.result(ibclient.connect(), Duration.Inf)
179 | val contract: Contract = options.contractType match {
180 | case SecType.STK => new StockContract(options.contract, options.contractExchange, options.contractCurrency)
181 | case SecType.FUT => new FutureContract(options.contract, options.contractExpiry, options.contractExchange,
182 | options.contractCurrency
183 | )
184 | case SecType.CASH ⇒ new CashContract(options.contract, options.localSymbol.get, options.contractExchange, options.contractCurrency)
185 | case other@_ => new GenericContract(other, options.contract, options.contractExchange, options.contractCurrency)
186 | }
187 |
188 | def fileName(): String = {
189 | if (contract.expiry() != null)
190 | s"${contract.symbol}${contract.expiry}-${contract.exchange}-${options.historyBarSize.toString}.csv"
191 | else
192 | s"${contract.symbol}-${contract.exchange}-${options.historyBarSize.toString}.csv"
193 | }
194 |
195 | val outFile = new File(options.historyOutFile.getOrElse(fileName()))
196 | require(!outFile.exists(), s"Output file ${outFile.getAbsolutePath} already exists")
197 | /*
198 | val futureContractDetails = ibclient.contractDetails(contract)
199 | val cd = Await.result(futureContractDetails, Duration.Inf)
200 | println(cd)
201 | */
202 | import scala.concurrent.ExecutionContext.Implicits.global
203 | val res = ibclient.easyHistoricalData(contract, options.historyStartDate, options.historyEndDate, options.historyBarSize,
204 | WhatToShow.TRADES, false
205 | )
206 | val hist = Await.result(res, Duration.Inf)
207 | val csvWriter = CSVWriter.open(outFile)
208 | csvWriter.writeRow(Vector("time", "high", "low", "open", "close", "volume", "trades", "gaps"))
209 | hist.foreach { bar ⇒
210 | csvWriter.writeRow(List(bar.time, bar.high, bar.low, bar.open, bar.close, bar.volume, bar.count, bar.hasGaps))
211 | }
212 | log.info(s"wrote ${hist.size} rows to ${outFile}")
213 | ibclient.disconnect()
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/ContractDetails.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import java.util.ArrayList;
7 |
8 | public class ContractDetails {
9 | private Contract m_contract;
10 | private String m_marketName;
11 | private double m_minTick;
12 | private int m_priceMagnifier;
13 | private String m_orderTypes;
14 | private String m_validExchanges;
15 | private int m_underConid;
16 | private String m_longName;
17 | private String m_contractMonth;
18 | private String m_industry;
19 | private String m_category;
20 | private String m_subcategory;
21 | private String m_timeZoneId;
22 | private String m_tradingHours;
23 | private String m_liquidHours;
24 | private String m_evRule;
25 | private double m_evMultiplier;
26 | private ArrayList m_secIdList; // CUSIP/ISIN/etc.
27 |
28 | // BOND values
29 | private String m_cusip;
30 | private String m_ratings;
31 | private String m_descAppend;
32 | private String m_bondType;
33 | private String m_couponType;
34 | private boolean m_callable = false;
35 | private boolean m_putable = false;
36 | private double m_coupon = 0;
37 | private boolean m_convertible = false;
38 | private String m_maturity;
39 | private String m_issueDate;
40 | private String m_nextOptionDate;
41 | private String m_nextOptionType;
42 | private boolean m_nextOptionPartial = false;
43 | private String m_notes;
44 |
45 | // Get
46 | public int conid() { return m_contract.conid(); }
47 | public Contract contract() { return m_contract; }
48 | public String marketName() { return m_marketName; }
49 | public double minTick() { return m_minTick; }
50 | public int priceMagnifier() { return m_priceMagnifier; }
51 | public String orderTypes() { return m_orderTypes; }
52 | public String validExchanges() { return m_validExchanges; }
53 | public int underConid() { return m_underConid; }
54 | public String longName() { return m_longName; }
55 | public String contractMonth() { return m_contractMonth; }
56 | public String industry() { return m_industry; }
57 | public String category() { return m_category; }
58 | public String subcategory() { return m_subcategory; }
59 | public String timeZoneId() { return m_timeZoneId; }
60 | public String tradingHours() { return m_tradingHours; }
61 | public String liquidHours() { return m_liquidHours; }
62 | public String evRule() { return m_evRule; }
63 | public double evMultiplier() { return m_evMultiplier; }
64 | public ArrayList secIdList() { return m_secIdList; }
65 |
66 | public String cusip() { return m_cusip; }
67 | public String ratings() { return m_ratings; }
68 | public String descAppend() { return m_descAppend; }
69 | public String bondType() { return m_bondType; }
70 | public String couponType() { return m_couponType; }
71 | public boolean callable() { return m_callable; }
72 | public boolean putable() { return m_putable; }
73 | public double coupon() { return m_coupon; }
74 | public boolean convertible() { return m_convertible; }
75 | public String maturity() { return m_maturity; }
76 | public String issueDate() { return m_issueDate; }
77 | public String nextOptionDate() { return m_nextOptionDate; }
78 | public String nextOptionType() { return m_nextOptionType; }
79 | public boolean nextOptionPartial() { return m_nextOptionPartial; }
80 | public String notes() { return m_notes; }
81 |
82 | // Set
83 | public void contract(Contract contract) { m_contract = contract; }
84 | public void marketName(String marketName) { m_marketName = marketName; }
85 | public void minTick(double minTick) { m_minTick = minTick; }
86 | public void priceMagnifier(int priceMagnifier) { m_priceMagnifier = priceMagnifier; }
87 | public void orderTypes(String orderTypes) { m_orderTypes = orderTypes; }
88 | public void validExchanges(String validExchanges) { m_validExchanges = validExchanges; }
89 | public void underConid(int underConid) { m_underConid = underConid; }
90 | public void longName(String longName) { m_longName = longName; }
91 | public void contractMonth(String contractMonth) { m_contractMonth = contractMonth; }
92 | public void industry(String industry) { m_industry = industry; }
93 | public void category(String category) { m_category = category; }
94 | public void subcategory(String subcategory) { m_subcategory = subcategory; }
95 | public void timeZoneId(String timeZoneId) { m_timeZoneId = timeZoneId; }
96 | public void tradingHours(String tradingHours) { m_tradingHours = tradingHours; }
97 | public void liquidHours(String liquidHours) { m_liquidHours = liquidHours; }
98 | public void evRule(String evRule) { m_evRule = evRule; }
99 | public void evMultiplier(double evMultiplier) { m_evMultiplier = evMultiplier; }
100 | public void secIdList(ArrayList secIdList) { m_secIdList = secIdList; }
101 |
102 | public void cusip(String cusip) { m_cusip = cusip; }
103 | public void ratings(String ratings) { m_ratings = ratings; }
104 | public void descAppend(String descAppend) { m_descAppend = descAppend; }
105 | public void bondType(String bondType) { m_bondType = bondType; }
106 | public void couponType(String couponType) { m_couponType = couponType; }
107 | public void callable(boolean callable) { m_callable = callable; }
108 | public void putable(boolean putable) { m_putable = putable; }
109 | public void coupon(double coupon) { m_coupon = coupon; }
110 | public void convertible(boolean convertible) { m_convertible = convertible; }
111 | public void maturity(String maturity) { m_maturity = maturity; }
112 | public void issueDate(String issueDate) { m_issueDate = issueDate; }
113 | public void nextOptionDate(String nextOptionDate) { m_nextOptionDate = nextOptionDate; }
114 | public void nextOptionType(String nextOptionType) { m_nextOptionType = nextOptionType; }
115 | public void nextOptionPartial(boolean nextOptionPartial) { m_nextOptionPartial = nextOptionPartial; }
116 | public void notes(String notes) { m_notes = notes; }
117 |
118 | public ContractDetails() {
119 | m_contract = new Contract();
120 | m_minTick = 0;
121 | m_underConid = 0;
122 | m_evMultiplier = 0;
123 | }
124 |
125 | public ContractDetails( Contract p_contract, String p_marketName,
126 | double p_minTick, String p_orderTypes, String p_validExchanges, int p_underConId, String p_longName,
127 | String p_contractMonth, String p_industry, String p_category, String p_subcategory,
128 | String p_timeZoneId, String p_tradingHours, String p_liquidHours,
129 | String p_evRule, double p_evMultiplier) {
130 | m_contract = p_contract;
131 | m_marketName = p_marketName;
132 | m_minTick = p_minTick;
133 | m_orderTypes = p_orderTypes;
134 | m_validExchanges = p_validExchanges;
135 | m_underConid = p_underConId;
136 | m_longName = p_longName;
137 | m_contractMonth = p_contractMonth;
138 | m_industry = p_industry;
139 | m_category = p_category;
140 | m_subcategory = p_subcategory;
141 | m_timeZoneId = p_timeZoneId;
142 | m_tradingHours = p_tradingHours;
143 | m_liquidHours = p_liquidHours;
144 | m_evRule = p_evRule;
145 | m_evMultiplier = p_evMultiplier;
146 | }
147 |
148 | @Override public String toString() {
149 | StringBuilder sb = new StringBuilder( m_contract.toString() );
150 |
151 | add( sb, "marketName", m_marketName);
152 | add( sb, "minTick", m_minTick);
153 | add( sb, "priceMagnifier", m_priceMagnifier);
154 | add( sb, "orderTypes", m_orderTypes);
155 | add( sb, "validExchanges", m_validExchanges);
156 | add( sb, "underConId", m_underConid);
157 | add( sb, "longName", m_longName);
158 | add( sb, "contractMonth", m_contractMonth);
159 | add( sb, "industry", m_industry);
160 | add( sb, "category", m_category);
161 | add( sb, "subcategory", m_subcategory);
162 | add( sb, "timeZoneId", m_timeZoneId);
163 | add( sb, "tradingHours", m_tradingHours);
164 | add( sb, "liquidHours", m_liquidHours);
165 | add( sb, "evRule", m_evRule);
166 | add( sb, "evMultiplier", m_evMultiplier);
167 |
168 | add( sb, "cusip", m_cusip);
169 | add( sb, "ratings", m_ratings);
170 | add( sb, "descAppend", m_descAppend);
171 | add( sb, "bondType", m_bondType);
172 | add( sb, "couponType", m_couponType);
173 | add( sb, "callable", m_callable);
174 | add( sb, "putable", m_putable);
175 | add( sb, "coupon", m_coupon);
176 | add( sb, "convertible", m_convertible);
177 | add( sb, "maturity", m_maturity);
178 | add( sb, "issueDate", m_issueDate);
179 | add( sb, "nextOptionDate", m_nextOptionDate);
180 | add( sb, "nextOptionType", m_nextOptionType);
181 | add( sb, "nextOptionPartial", m_nextOptionPartial);
182 | add( sb, "notes", m_notes);
183 |
184 | return sb.toString();
185 | }
186 |
187 | public static void add(StringBuilder sb, String tag, Object val) {
188 | if (val == null || val instanceof String && ((String)val).length() == 0) {
189 | return;
190 | }
191 | sb.append( tag);
192 | sb.append( '\t');
193 | sb.append( val);
194 | sb.append( '\n');
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/Types.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import static com.ib.client.Types.AlgoParam.allowPastEndTime;
7 | import static com.ib.client.Types.AlgoParam.catchUp;
8 | import static com.ib.client.Types.AlgoParam.componentSize;
9 | import static com.ib.client.Types.AlgoParam.displaySize;
10 | import static com.ib.client.Types.AlgoParam.endTime;
11 | import static com.ib.client.Types.AlgoParam.forceCompletion;
12 | import static com.ib.client.Types.AlgoParam.getDone;
13 | import static com.ib.client.Types.AlgoParam.giveUp;
14 | import static com.ib.client.Types.AlgoParam.maxPctVol;
15 | import static com.ib.client.Types.AlgoParam.noTakeLiq;
16 | import static com.ib.client.Types.AlgoParam.noTradeAhead;
17 | import static com.ib.client.Types.AlgoParam.pctVol;
18 | import static com.ib.client.Types.AlgoParam.randomizeSize55;
19 | import static com.ib.client.Types.AlgoParam.randomizeTime20;
20 | import static com.ib.client.Types.AlgoParam.riskAversion;
21 | import static com.ib.client.Types.AlgoParam.startTime;
22 | import static com.ib.client.Types.AlgoParam.strategyType;
23 | import static com.ib.client.Types.AlgoParam.timeBetweenOrders;
24 | import static com.ib.client.Types.AlgoParam.useOddLots;
25 | import static com.ib.client.Types.AlgoParam.waitForFill;
26 |
27 | public class Types {
28 | public static enum ComboParam {
29 | NonGuaranteed, PriceCondConid, CondPriceMax, CondPriceMin, ChangeToMktTime1, ChangeToMktTime2, DiscretionaryPct, DontLeginNext, LeginPrio, MaxSegSize,
30 | }
31 |
32 | public static enum AlgoParam {
33 | startTime, endTime, allowPastEndTime, maxPctVol, pctVol, strategyType, noTakeLiq, riskAversion, forceCompletion, displaySize, getDone, noTradeAhead, useOddLots,
34 | componentSize, timeBetweenOrders, randomizeTime20, randomizeSize55, giveUp, catchUp, waitForFill
35 | }
36 |
37 | public static enum AlgoStrategy implements IApiEnum {
38 | None(),
39 | Vwap( startTime, endTime, maxPctVol, noTakeLiq, getDone, noTradeAhead, useOddLots),
40 | Twap( startTime, endTime, allowPastEndTime, strategyType),
41 | ArrivalPx( startTime, endTime, allowPastEndTime, maxPctVol, riskAversion, forceCompletion),
42 | DarkIce( startTime, endTime, allowPastEndTime, displaySize),
43 | PctVol( startTime, endTime, pctVol, noTakeLiq),
44 | AD( startTime, endTime, componentSize, timeBetweenOrders, randomizeTime20, randomizeSize55, giveUp, catchUp, waitForFill);
45 |
46 | private AlgoParam[] m_params;
47 |
48 | public AlgoParam[] params() { return m_params; }
49 |
50 | private AlgoStrategy( AlgoParam... params) {
51 | m_params = params;
52 | }
53 |
54 | public static AlgoStrategy get( String apiString) {
55 | return apiString != null && apiString.length() > 0 ? valueOf( apiString) : None;
56 | }
57 |
58 | @Override public String getApiString() {
59 | return this == None ? "" : super.toString();
60 | }
61 | }
62 |
63 | public static enum HedgeType implements IApiEnum {
64 | None, Delta, Beta, Fx, Pair;
65 |
66 | public static HedgeType get( String apiString) {
67 | for (HedgeType type : values() ) {
68 | if (type.getApiString().equals( apiString) ) {
69 | return type;
70 | }
71 | }
72 | return None;
73 | }
74 |
75 | @Override public String getApiString() {
76 | return this == None ? "" : String.valueOf( super.toString().charAt( 0) );
77 | }
78 | }
79 |
80 | public static enum Right implements IApiEnum {
81 | None, Put, Call;
82 |
83 | public static Right get( String apiString) {
84 | if (apiString != null && apiString.length() > 0) {
85 | switch( apiString.charAt( 0) ) {
86 | case 'P' : return Put;
87 | case 'C' : return Call;
88 | }
89 | }
90 | return None;
91 | }
92 |
93 | @Override public String getApiString() {
94 | return this == None ? "" : String.valueOf( toString().charAt( 0) );
95 | }
96 | }
97 |
98 | public static enum VolatilityType implements IApiEnum {
99 | None, Daily, Annual;
100 |
101 | public static VolatilityType get( int ordinal) {
102 | return ordinal == Integer.MAX_VALUE ? None : getEnum( ordinal, values() );
103 | }
104 |
105 | @Override public String getApiString() {
106 | return "" + ordinal();
107 | }
108 | }
109 |
110 | public static enum ReferencePriceType implements IApiEnum {
111 | None, Midpoint, BidOrAsk;
112 |
113 | public static ReferencePriceType get( int ordinal) {
114 | return getEnum( ordinal, values() );
115 | }
116 |
117 | @Override public String getApiString() {
118 | return "" + ordinal();
119 | }
120 | }
121 |
122 | public static enum TriggerMethod implements IApiEnum {
123 | Default( 0), DoubleBidAsk( 1), Last( 2), DoubleLast( 3), BidAsk( 4), LastOrBidAsk( 7), Midpoint( 8);
124 |
125 | int m_val;
126 |
127 | public int val() { return m_val; }
128 |
129 | private TriggerMethod( int val) {
130 | m_val = val;
131 | }
132 |
133 | public static TriggerMethod get( int val) {
134 | for (TriggerMethod m : values() ) {
135 | if (m.m_val == val) {
136 | return m;
137 | }
138 | }
139 | return null;
140 | }
141 |
142 | @Override public String getApiString() {
143 | return "" + m_val;
144 | }
145 | }
146 |
147 | public static enum Action implements IApiEnum {
148 | BUY, SELL, SSHORT;
149 |
150 | @Override public String getApiString() {
151 | return toString();
152 | }
153 | }
154 |
155 | public static enum Rule80A implements IApiEnum {
156 | None(""), IndivArb("J"), IndivBigNonArb("K"), IndivSmallNonArb("I"), INST_ARB("U"), InstBigNonArb("Y"), InstSmallNonArb("A");
157 |
158 | private String m_apiString;
159 |
160 | private Rule80A( String apiString) {
161 | m_apiString = apiString;
162 | }
163 |
164 | public static Rule80A get( String apiString) {
165 | for (Rule80A val : values() ) {
166 | if (val.m_apiString.equals( apiString) ) {
167 | return val;
168 | }
169 | }
170 | return None;
171 | }
172 |
173 | public String getApiString() {
174 | return m_apiString;
175 | }
176 | }
177 |
178 | public static enum OcaType implements IApiEnum {
179 | None, CancelWithBlocking, ReduceWithBlocking, ReduceWithoutBlocking;
180 |
181 | public static OcaType get( int ordinal) {
182 | return getEnum( ordinal, values() );
183 | }
184 |
185 | @Override public String getApiString() {
186 | return "" + ordinal();
187 | }
188 | }
189 |
190 | public static enum TimeInForce implements IApiEnum {
191 | DAY, GTC, OPG, IOC, GTD, GTT, AUC, FOK, GTX, DTC;
192 |
193 | @Override public String getApiString() {
194 | return toString();
195 | }
196 | }
197 |
198 | public static enum ExerciseType {
199 | None, Exercise, Lapse;
200 | }
201 |
202 | public static enum FundamentalType {
203 | ReportSnapshot, ReportsFinSummary, ReportRatios, ReportsFinStatements, RESC, CalendarReport;
204 |
205 | public String getApiString() {
206 | return super.toString();
207 | }
208 |
209 | @Override public String toString() {
210 | switch( this) {
211 | case ReportSnapshot: return "Company overview";
212 | case ReportsFinSummary: return "Financial summary";
213 | case ReportRatios: return "Financial ratios";
214 | case ReportsFinStatements: return "Financial statements";
215 | case RESC: return "Analyst estimates";
216 | case CalendarReport: return "Company calendar";
217 | default: return null;
218 | }
219 | }
220 | }
221 |
222 | public static enum WhatToShow {
223 | TRADES, MIDPOINT, BID, ASK, // << only these are valid for real-time bars
224 | BID_ASK, HISTORICAL_VOLATILITY, OPTION_IMPLIED_VOLATILITY, YIELD_ASK, YIELD_BID, YIELD_BID_ASK, YIELD_LAST
225 | }
226 |
227 | public static enum BarSize {
228 | _1_secs, _5_secs, _10_secs, _15_secs, _30_secs, _1_min, _2_mins, _3_mins, _5_mins, _10_mins, _15_mins, _20_mins, _30_mins, _1_hour, _4_hours, _1_day, _1_week;
229 |
230 | public String toString() {
231 | return super.toString().substring( 1).replace( '_', ' ');
232 | }
233 | }
234 |
235 | public static enum DurationUnit {
236 | SECOND, DAY, WEEK, MONTH, YEAR;
237 | }
238 |
239 | public static enum DeepType {
240 | INSERT, UPDATE, DELETE;
241 |
242 | public static DeepType get( int ordinal) {
243 | return getEnum( ordinal, values() );
244 | }
245 | }
246 |
247 | public static enum DeepSide {
248 | SELL, BUY;
249 |
250 | public static DeepSide get( int ordinal) {
251 | return getEnum( ordinal, values() );
252 | }
253 | }
254 |
255 | public enum NewsType {
256 | UNKNOWN, BBS, LIVE_EXCH, DEAD_EXCH, HTML, POPUP_TEXT, POPUP_HTML;
257 |
258 | public static NewsType get( int ordinal) {
259 | return getEnum( ordinal, values() );
260 | }
261 | }
262 |
263 | public enum FADataType {
264 | UNUSED, GROUPS, PROFILES, ALIASES;
265 |
266 | public static FADataType get( int ordinal) {
267 | return getEnum( ordinal, values() );
268 | }
269 | }
270 |
271 | public enum SecIdType implements IApiEnum {
272 | None, CUSIP, SEDOL, ISIN, RIC;
273 |
274 | public static SecIdType get(String str) {
275 | return str == null || str.length() == 0 ? None : valueOf( str);
276 | }
277 |
278 | @Override public String getApiString() {
279 | return this == None ? "" : super.toString();
280 | }
281 | }
282 |
283 | public enum SecType implements IApiEnum {
284 | None, STK, OPT, FUT, CASH, BOND, CFD, FOP, WAR, IOPT, FWD, BAG, IND, BILL, FUND, FIXED, SLB, NEWS, CMDTY, BSK, ICU, ICS;
285 |
286 | public static SecType get(String str) {
287 | return str == null || str.length() == 0 ? None : valueOf( str);
288 | }
289 |
290 | @Override public String getApiString() {
291 | return this == None ? "" : super.toString();
292 | }
293 | }
294 |
295 | public enum MktDataType {
296 | Unknown, Realtime, Frozen;
297 |
298 | public static MktDataType get( int ordinal) {
299 | return getEnum( ordinal, values() );
300 | }
301 | }
302 |
303 | public enum Method implements IApiEnum {
304 | None, EqualQuantity, AvailableEquity, NetLiq, PctChange;
305 |
306 | public static Method get( String str) {
307 | return str == null || str.length() == 0 ? None : valueOf( str);
308 | }
309 |
310 | @Override public String getApiString() {
311 | return this == None ? "" : super.toString();
312 | }
313 | }
314 |
315 | /** Lookup enum by ordinal. Use Enum.valueOf() to lookup by string. */
316 | public static > T getEnum(int ordinal, T[] values) {
317 | if (ordinal == Integer.MAX_VALUE) {
318 | return null;
319 | }
320 |
321 | for (T val : values) {
322 | if (val.ordinal() == ordinal) {
323 | return val;
324 | }
325 | }
326 | String str = String.format( "Error: %s is not a valid value for enum %s", ordinal, values[0].getClass().getName() );
327 | throw new IllegalArgumentException( str);
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/client/Contract.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.client;
5 |
6 | import java.util.ArrayList;
7 |
8 | import com.ib.client.ComboLeg;
9 | import com.ib.client.Contract;
10 | import com.ib.client.Types.Right;
11 | import com.ib.client.Types.SecIdType;
12 | import com.ib.client.Types.SecType;
13 |
14 | public class Contract implements Cloneable {
15 | private int m_conid;
16 | private String m_symbol;
17 | private String m_secType;
18 | private String m_expiry;
19 | private double m_strike;
20 | private String m_right;
21 | private String m_multiplier; // should be double
22 | private String m_exchange;
23 | private String m_primaryExch; // pick a non-aggregate (ie not the SMART exchange) exchange that the contract trades on. DO NOT SET TO SMART.
24 | private String m_currency;
25 | private String m_localSymbol;
26 | private String m_tradingClass;
27 | private String m_secIdType; // CUSIP;SEDOL;ISIN;RIC
28 | private String m_secId;
29 |
30 | private DeltaNeutralContract m_underComp;
31 | private boolean m_includeExpired; // can not be set to true for orders
32 | // COMBOS
33 | private String m_comboLegsDescrip; // received in open order version 14 and up for all combos
34 | private ArrayList m_comboLegs = new ArrayList(); // would be final except for clone
35 |
36 | // Get
37 | public double strike() { return m_strike; }
38 | public int conid() { return m_conid; }
39 | public SecIdType secIdType() { return SecIdType.get(m_secIdType); }
40 | public String getSecIdType() { return m_secIdType; }
41 | public SecType secType() { return m_secType == null ? SecType.None : SecType.valueOf(m_secType); }
42 | public String getSecType() { return m_secType; }
43 | public String currency() { return m_currency; }
44 | public String exchange() { return m_exchange; }
45 | public String primaryExch() { return m_primaryExch; }
46 | public String expiry() { return m_expiry; }
47 | public String localSymbol() { return m_localSymbol; }
48 | public String tradingClass() { return m_tradingClass; }
49 | public String multiplier() { return m_multiplier; }
50 | public Right right() { return Right.get(m_right); }
51 | public String getRight() { return m_right; }
52 | public String secId() { return m_secId; }
53 | public String symbol() { return m_symbol; }
54 | public boolean includeExpired() { return m_includeExpired; }
55 | public DeltaNeutralContract underComp() { return m_underComp; }
56 | public ArrayList comboLegs() { return m_comboLegs; }
57 | public String comboLegsDescrip() { return m_comboLegsDescrip; }
58 |
59 | // Set
60 | public void conid(int v) { m_conid = v; }
61 | public void currency(String v) { m_currency = v; }
62 | public void exchange(String v) { m_exchange = v; }
63 | public void expiry(String v) { m_expiry = v; }
64 | public void localSymbol(String v) { m_localSymbol = v; }
65 | public void tradingClass(String v) { m_tradingClass = v; }
66 | public void multiplier(String v) { m_multiplier = v; }
67 | public void primaryExch(String v) { m_primaryExch = v; }
68 | public void right(Right v) { m_right = ( v == null ) ? null : v.getApiString(); }
69 | public void right(String v) { m_right = v; }
70 | public void secId(String v) { m_secId = v; }
71 | public void secIdType(SecIdType v) { m_secIdType = ( v == null ) ? null : v.getApiString(); }
72 | public void secIdType(String v) { m_secIdType = v; }
73 | public void secType(SecType v) { m_secType = ( v == null ) ? null : v.getApiString(); }
74 | public void secType(String v) { m_secType = v; }
75 | public void strike(double v) { m_strike = v; }
76 | public void symbol(String v) { m_symbol = v; }
77 | public void underComp(DeltaNeutralContract v) { m_underComp = v; }
78 | public void includeExpired(boolean v) { m_includeExpired = v; }
79 | public void comboLegs(ArrayList v) { m_comboLegs = v; }
80 | public void comboLegsDescrip(String v) { m_comboLegsDescrip = v; }
81 |
82 | public Contract() {
83 | m_conid = 0;
84 | m_strike = 0;
85 | m_includeExpired = false;
86 | }
87 |
88 | @Override public Contract clone() {
89 | try {
90 | Contract copy = (Contract)super.clone();
91 | if ( copy.m_comboLegs != null ) {
92 | copy.m_comboLegs = new ArrayList( copy.m_comboLegs);
93 | }
94 | else {
95 | copy.m_comboLegs = new ArrayList();
96 | }
97 | return copy;
98 | }
99 | catch (CloneNotSupportedException e) {
100 | e.printStackTrace();
101 | return null;
102 | }
103 | }
104 |
105 | public Contract(int p_conId, String p_symbol, String p_secType, String p_expiry,
106 | double p_strike, String p_right, String p_multiplier,
107 | String p_exchange, String p_currency, String p_localSymbol, String p_tradingClass,
108 | ArrayList p_comboLegs, String p_primaryExch, boolean p_includeExpired,
109 | String p_secIdType, String p_secId) {
110 | m_conid = p_conId;
111 | m_symbol = p_symbol;
112 | m_secType = p_secType;
113 | m_expiry = p_expiry;
114 | m_strike = p_strike;
115 | m_right = p_right;
116 | m_multiplier = p_multiplier;
117 | m_exchange = p_exchange;
118 | m_currency = p_currency;
119 | m_includeExpired = p_includeExpired;
120 | m_localSymbol = p_localSymbol;
121 | m_tradingClass = p_tradingClass;
122 | m_comboLegs = p_comboLegs;
123 | m_primaryExch = p_primaryExch;
124 | m_secIdType = p_secIdType;
125 | m_secId = p_secId ;
126 | }
127 |
128 | public boolean equals(Object p_other) {
129 | if (this == p_other) {
130 | return true;
131 | }
132 |
133 | if (p_other == null || !(p_other instanceof Contract)) {
134 | return false;
135 | }
136 |
137 | Contract l_theOther = (Contract)p_other;
138 |
139 | if (m_conid != l_theOther.m_conid) {
140 | return false;
141 | }
142 |
143 | if (Util.StringCompare(m_secType, l_theOther.m_secType) != 0) {
144 | return false;
145 | }
146 |
147 | if (Util.StringCompare(m_symbol, l_theOther.m_symbol) != 0 ||
148 | Util.StringCompare(m_exchange, l_theOther.m_exchange) != 0 ||
149 | Util.StringCompare(m_primaryExch, l_theOther.m_primaryExch) != 0 ||
150 | Util.StringCompare(m_currency, l_theOther.m_currency) != 0) {
151 | return false;
152 | }
153 |
154 | if (!Util.NormalizeString(m_secType).equals("BOND")) {
155 |
156 | if (m_strike != l_theOther.m_strike) {
157 | return false;
158 | }
159 |
160 | if (Util.StringCompare(m_expiry, l_theOther.m_expiry) != 0 ||
161 | Util.StringCompare(m_right, l_theOther.m_right) != 0 ||
162 | Util.StringCompare(m_multiplier, l_theOther.m_multiplier) != 0 ||
163 | Util.StringCompare(m_localSymbol, l_theOther.m_localSymbol) != 0 ||
164 | Util.StringCompare(m_tradingClass, l_theOther.m_tradingClass) != 0) {
165 | return false;
166 | }
167 | }
168 |
169 | if (Util.StringCompare(m_secIdType, l_theOther.m_secIdType) != 0) {
170 | return false;
171 | }
172 |
173 | if (Util.StringCompare(m_secId, l_theOther.m_secId) != 0) {
174 | return false;
175 | }
176 |
177 | // compare combo legs
178 | if (!Util.ArrayEqualsUnordered(m_comboLegs, l_theOther.m_comboLegs)) {
179 | return false;
180 | }
181 |
182 | if (m_underComp != l_theOther.m_underComp) {
183 | if (m_underComp == null || l_theOther.m_underComp == null) {
184 | return false;
185 | }
186 | if (!m_underComp.equals(l_theOther.m_underComp)) {
187 | return false;
188 | }
189 | }
190 | return true;
191 | }
192 |
193 | /** Returns a text description that can be used for display. */
194 | public String description() {
195 | StringBuilder sb = new StringBuilder();
196 |
197 | if (isCombo() ) {
198 | int i = 0;
199 | for (ComboLeg leg : m_comboLegs) {
200 | if (i++ > 0) {
201 | sb.append( "/");
202 | }
203 | sb.append( leg.toString() );
204 | }
205 | }
206 | else {
207 | sb.append( m_symbol);
208 | app( sb, m_secType);
209 | app( sb, m_exchange);
210 |
211 | if (m_exchange != null && m_exchange.equals( "SMART") && m_primaryExch != null) {
212 | app( sb, m_primaryExch);
213 | }
214 |
215 | app( sb, m_expiry);
216 |
217 | if (m_strike != 0) {
218 | app( sb, m_strike);
219 | }
220 |
221 | if( !Util.StringIsEmpty(m_right) ) {
222 | app( sb, m_right);
223 | }
224 | }
225 | return sb.toString();
226 | }
227 |
228 | private static void app(StringBuilder buf, Object obj) {
229 | if (obj != null) {
230 | buf.append( " ");
231 | buf.append( obj);
232 | }
233 | }
234 |
235 | public boolean isCombo() {
236 | return m_comboLegs.size() > 0;
237 | }
238 |
239 | @Override public String toString() {
240 | StringBuilder sb = new StringBuilder();
241 |
242 | add( sb, "conid", m_conid);
243 | add( sb, "symbol", m_symbol);
244 | add( sb, "secType", m_secType);
245 | add( sb, "expiry", m_expiry);
246 | add( sb, "strike", m_strike);
247 | add( sb, "right", m_right);
248 | add( sb, "multiplier", m_multiplier);
249 | add( sb, "exchange", m_exchange);
250 | add( sb, "currency", m_currency);
251 | add( sb, "localSymbol", m_localSymbol);
252 | add( sb, "tradingClass", m_tradingClass);
253 | add( sb, "primaryExch", m_primaryExch);
254 | add( sb, "secIdType", m_secIdType);
255 | add( sb, "secId", m_secId);
256 |
257 | return sb.toString();
258 | }
259 |
260 | public static void add(StringBuilder sb, String tag, Object val) {
261 | if (val == null || val instanceof String && ((String)val).length() == 0) {
262 | return;
263 | }
264 |
265 | sb.append( tag);
266 | sb.append( '\t');
267 | sb.append( val);
268 | sb.append( '\n');
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/AdvisorUtil.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.StringReader;
9 | import java.util.ArrayList;
10 |
11 | import com.ib.client.Types.Method;
12 | import com.ib.controller.Profile.Allocation;
13 | import com.ib.controller.Profile.Type;
14 |
15 |
16 |
17 | public class AdvisorUtil {
18 | static ArrayList getGroups( String xml) {
19 | try {
20 | return getGroups_( xml);
21 | } catch (IOException e) {
22 | e.printStackTrace();
23 | return null;
24 | }
25 | }
26 |
27 | static ArrayList getGroups_( String xml) throws IOException {
28 | ArrayList list = new ArrayList();
29 |
30 | Group group = null;
31 |
32 | BufferedReader reader = new BufferedReader( new StringReader( xml) );
33 | String line;
34 | int state = 0; // 0=none; 1=list of groups; 2=reading group 3=listOfAccts
35 | while ( (line=reader.readLine()) != null) {
36 | line = line.trim();
37 |
38 | switch( state) {
39 | // top of file
40 | case 0:
41 | if (line.equals( "")) {
42 | state = 1;
43 | }
44 | break;
45 |
46 | // reading groups
47 | case 1:
48 | if (line.equals( "")) {
49 | group = new Group();
50 | state = 2;
51 | }
52 | else if (line.equals( "")) {
53 | state = 0;
54 | }
55 | else {
56 | err( line);
57 | }
58 | break;
59 |
60 | // reading group
61 | case 2:
62 | if (line.startsWith( "") ) {
63 | group.name( getVal( line) );
64 | }
65 | else if (line.startsWith( "")) {
66 | group.defaultMethod( Method.valueOf( getVal( line) ) );
67 | }
68 | else if (line.startsWith( "")) {
72 | list.add( group);
73 | state = 1;
74 | }
75 | else {
76 | err( line);
77 | }
78 | break;
79 |
80 | // reading list of accts
81 | case 3:
82 | if (line.equals( "")) {
83 | state = 2;
84 | }
85 | else {
86 | group.addAccount( getVal( line) );
87 | }
88 | break;
89 | }
90 | }
91 |
92 | return list;
93 | }
94 |
95 | static ArrayList getProfiles( String xml) {
96 | try {
97 | return getProfiles_( xml);
98 | } catch (IOException e) {
99 | e.printStackTrace();
100 | return null;
101 | }
102 | }
103 |
104 | static ArrayList getProfiles_( String xml) throws IOException {
105 | ArrayList list = new ArrayList();
106 |
107 | Profile profile = null;
108 | Allocation alloc = null;
109 |
110 | BufferedReader reader = new BufferedReader( new StringReader( xml) );
111 | String line;
112 | int state = 0; // 0=none; 1=list of groups; 2=reading group 3=listOfAllocations 4=allocation
113 | while ( (line=reader.readLine() ) != null) {
114 | line = line.trim();
115 |
116 | switch( state) {
117 | // top of file
118 | case 0:
119 | if (line.equals( "")) {
120 | state = 1;
121 | }
122 | break;
123 |
124 | // reading profiles
125 | case 1:
126 | if (line.equals( "")) {
127 | profile = new Profile();
128 | state = 2;
129 | }
130 | else if (line.equals( "")) {
131 | state = 0;
132 | }
133 | else {
134 | err( line);
135 | }
136 | break;
137 |
138 | // reading Profile
139 | case 2:
140 | if (line.startsWith( "") ) {
141 | profile.name( getVal( line) );
142 | }
143 | else if (line.startsWith( "")) {
144 | int i = Integer.parseInt( getVal( line) );
145 | profile.type( Type.get( i) );
146 | }
147 | else if (line.startsWith( "")) {
151 | list.add( profile);
152 | state = 1;
153 | }
154 | else {
155 | err( line);
156 | }
157 | break;
158 |
159 | // reading list of allocations
160 | case 3:
161 | if (line.equals( "")) {
162 | alloc = new Allocation();
163 | state = 4;
164 | }
165 | else if (line.equals( "")) {
166 | state = 2;
167 | }
168 | else {
169 | err( line);
170 | }
171 | break;
172 |
173 | // reading Allocation
174 | case 4:
175 | if (line.startsWith( "") ) {
176 | alloc.account( getVal( line) );
177 | }
178 | else if (line.startsWith( "") ) {
179 | alloc.amount( getVal( line) );
180 | }
181 | else if (line.startsWith( "") ) {
182 | // skip this
183 | }
184 | else if (line.equals( "") ) {
185 | profile.add( alloc);
186 | state = 3;
187 | }
188 | else {
189 | err( line);
190 | }
191 | break;
192 | }
193 | }
194 |
195 | return list;
196 | }
197 |
198 | static ArrayList getAliases( String xml) {
199 | try {
200 | return getAliases_( xml);
201 | } catch (IOException e) {
202 | e.printStackTrace();
203 | return null;
204 | }
205 | }
206 |
207 | static ArrayList getAliases_( String xml) throws IOException {
208 | ArrayList list = new ArrayList();
209 |
210 | Alias alias = null;
211 |
212 | BufferedReader reader = new BufferedReader( new StringReader( xml) );
213 | String line;
214 | int state = 0; // 0=none; 1=list of aliases; 2=reading alias
215 | while ( (line=reader.readLine() ) != null) {
216 | line = line.trim();
217 |
218 | switch( state) {
219 | // top of file
220 | case 0:
221 | if (line.equals( "")) {
222 | state = 1;
223 | }
224 | break;
225 |
226 | // reading aliases
227 | case 1:
228 | if (line.equals( "")) {
229 | alias = new Alias();
230 | state = 2;
231 | }
232 | else if (line.equals( "")) {
233 | state = 0;
234 | }
235 | else {
236 | err( line);
237 | }
238 | break;
239 |
240 | // reading Alias
241 | case 2:
242 | if (line.startsWith( "") ) {
243 | alias.account( getVal( line) );
244 | }
245 | else if (line.startsWith( "")) {
246 | alias.alias( getVal( line) );
247 | }
248 | else if (line.equals( "")) {
249 | list.add( alias);
250 | state = 1;
251 | }
252 | else {
253 | err( line);
254 | }
255 | break;
256 | }
257 | }
258 |
259 | return list;
260 | }
261 |
262 | private static String getVal(String line) {
263 | int i1 = line.indexOf( '>');
264 | int i2 = line.indexOf( '<', 1);
265 | return line.substring( i1 + 1, i2);
266 | }
267 |
268 | private static void err(String line) {
269 | System.out.println( "error " + line);
270 | }
271 |
272 | /*
273 | public static void main(String[] args) {
274 | String str1 = "\n\n \n Group 1\n \n DU109949\n DU109950\n DU110a156\n DU110157\n DU110158\n \n AvailableEquity\n \n \n Group 2\n \n DU109950\n DU110156\n DU110157\n \n AvailableEquity\n \n\n";
275 | ArrayList groups = getGroups( str1);
276 |
277 | String str2 = "\n\n \n High Risk\n 1\n \n \n DU110157\n 90.0\n O\n \n \n DU110158\n 10.0\n O\n \n \n \n \n Profile\n 2\n \n \n DU109949\n 1.0\n O\n \n \n \n\n";
278 | ArrayList profiles = getProfiles( str2);
279 |
280 | String str3 = "\n\n \n DF109948\n DF109948\n \n \n DU109949\n DU109949\n \n \n DU109950\n DU109950\n \n \n DU110156\n DU110156\n \n \n DU110157\n DU110157\n \n \n DU110158\n DU110158\n \n\n\n";
281 | ArrayList aliases = getAliases( str3);
282 |
283 | AdvisorUtil.err( aliases.toString() );
284 | }
285 | */
286 |
287 | public static String getGroupsXml(ArrayList groups) {
288 | StringBuilder buf = new StringBuilder();
289 | buf.append( "\n");
290 | buf.append( "\n");
291 | for( Group group : groups) {
292 | buf.append( "\n");
293 | buf.append( String.format( "%s\n", group.name() ) );
294 | buf.append( String.format( "%s\n", group.defaultMethod() ) );
295 | buf.append( "");
296 | for( String acct : group.accounts() ) {
297 | buf.append( String.format( "%s\n", acct) );
298 | }
299 | buf.append( "\n");
300 | buf.append( "\n");
301 | }
302 | buf.append( "\n");
303 | return buf.toString();
304 | }
305 |
306 | public static String getProfilesXml(ArrayList profiles) {
307 | StringBuilder buf = new StringBuilder();
308 | buf.append( "\n");
309 | buf.append( "\n");
310 | for( Profile profile : profiles) {
311 | buf.append( "\n");
312 | buf.append( String.format( "%s\n", profile.name() ) );
313 | buf.append( String.format( "%s\n", profile.type().ordinal() ) );
314 | buf.append( "\n");
315 | for( Allocation alloc : profile.allocations() ) {
316 | buf.append( "\n");
317 | buf.append( String.format( "%s\n", alloc.account() ) );
318 | buf.append( String.format( "%s\n", alloc.amount() ) );
319 | buf.append( "\n");
320 | }
321 | buf.append( "\n");
322 | buf.append( "\n");
323 | }
324 | buf.append( "\n");
325 | return buf.toString();
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/src/main/java/com/ib/controller/ApiConnection.java:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2013 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
2 | * and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable. */
3 |
4 | package com.ib.controller;
5 |
6 |
7 | import java.io.DataInputStream;
8 | import java.io.FilterInputStream;
9 | import java.io.FilterOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.OutputStream;
13 | import java.lang.reflect.Field;
14 | import java.net.Socket;
15 |
16 | import com.ib.client.ComboLeg;
17 | import com.ib.client.Contract;
18 | import com.ib.client.EWrapper;
19 | import com.ib.client.Builder;
20 | import com.ib.client.EClientErrors;
21 | import com.ib.client.EClientSocket;
22 | import com.ib.client.EReader;
23 | import com.ib.client.Order;
24 | import com.ib.client.OrderComboLeg;
25 | import com.ib.client.OrderType;
26 | import com.ib.client.TagValue;
27 | import com.ib.client.Types.AlgoStrategy;
28 | import com.ib.client.Types.HedgeType;
29 | import com.ib.client.Types.SecType;
30 |
31 | // NOTE: TWS 936 SERVER_VERSION is 67.
32 |
33 | public class ApiConnection extends EClientSocket {
34 | public interface ILogger {
35 | void log(String valueOf);
36 | }
37 |
38 | public static final char EOL = 0;
39 | public static final char LOG_EOL = '_';
40 |
41 | private final ILogger m_inLogger;
42 | private final ILogger m_outLogger;
43 |
44 | public ApiConnection(EWrapper wrapper, ILogger inLogger, ILogger outLogger) {
45 | super( wrapper);
46 | m_inLogger = inLogger;
47 | m_outLogger = outLogger;
48 | }
49 |
50 | @Override public synchronized void eConnect(Socket socket, int clientId) throws IOException {
51 | super.eConnect(socket, clientId);
52 |
53 | // replace the output stream with one that logs all data to m_outLogger
54 | if (isConnected()) {
55 | try {
56 | Field realOsField = FilterOutputStream.class.getDeclaredField( "out");
57 | realOsField.setAccessible( true);
58 | OutputStream realOs = (OutputStream)realOsField.get( m_dos);
59 | realOsField.set( m_dos, new MyOS( realOs) );
60 | }
61 | catch( Exception e) {
62 | e.printStackTrace();
63 | }
64 | }
65 | }
66 |
67 | /** Replace the input stream with one that logs all data to m_inLogger. */
68 | @Override public EReader createReader(EClientSocket socket, DataInputStream dis) {
69 | try {
70 | Field realIsField = FilterInputStream.class.getDeclaredField( "in");
71 | realIsField.setAccessible( true);
72 | InputStream realIs = (InputStream)realIsField.get( dis);
73 | realIsField.set( dis, new MyIS( realIs) );
74 | }
75 | catch( Exception e) {
76 | e.printStackTrace();
77 | }
78 | return super.createReader(socket, dis);
79 | }
80 |
81 | public synchronized void placeOrder(Contract contract, Order order) {
82 | // not connected?
83 | if( !isConnected() ) {
84 | notConnected();
85 | return;
86 | }
87 |
88 | // ApiController requires TWS 932 or higher; this limitation could be removed if needed
89 | if (serverVersion() < 66) {
90 | error( EClientErrors.NO_VALID_ID, EClientErrors.UPDATE_TWS, "ApiController requires TWS build 932 or higher to place orders.");
91 | return;
92 | }
93 |
94 | Builder b = prepareBuffer();
95 | b.setUseSendMax(); // Order placement relies on null valued numeric fields
96 |
97 | int VERSION = 43;
98 |
99 | // send place order msg
100 | try {
101 | b.send( PLACE_ORDER);
102 | b.send( VERSION);
103 | b.send( order.orderId() );
104 | b.send( contract.conid() );
105 | b.send( contract.symbol());
106 | b.send( contract.secType() );
107 | b.send( contract.expiry());
108 | b.send( contract.strike());
109 | b.send( contract.right().getApiString() ); // TODO: Here and below: do not need to call getApiString() - NPE is possible
110 | b.send( contract.multiplier() );
111 | b.send( contract.exchange() );
112 | b.send( contract.primaryExch() );
113 | b.send( contract.currency() );
114 | b.send( contract.localSymbol() );
115 | if (m_serverVersion >= MIN_SERVER_VER_TRADING_CLASS) {
116 | b.send(contract.tradingClass() );
117 | }
118 | b.send( contract.secIdType() );
119 | b.send( contract.secId() );
120 | b.send( order.action() );
121 | b.send( order.totalQuantity() );
122 | b.send( order.orderType() );
123 | b.send( order.lmtPrice() );
124 | b.send( order.auxPrice() );
125 | b.send( order.tif() );
126 | b.send( order.ocaGroup() );
127 | b.send( order.account() );
128 | b.send( ""); // open/close
129 | b.send( ""); // origin
130 | b.send( order.orderRef() );
131 | b.send( order.transmit() );
132 | b.send( order.parentId() );
133 | b.send( order.blockOrder() );
134 | b.send( order.sweepToFill() );
135 | b.send( order.displaySize() );
136 | b.send( order.triggerMethod() );
137 | b.send( order.outsideRth() );
138 | b.send( order.hidden() );
139 |
140 | // send combo legs for BAG orders
141 | if(contract.secType() == SecType.BAG) {
142 | b.send( contract.comboLegs().size());
143 |
144 | for (ComboLeg leg : contract.comboLegs() ) {
145 | b.send( leg.conid() );
146 | b.send( leg.ratio() );
147 | b.send( leg.action().getApiString() );
148 | b.send( leg.exchange() );
149 | b.send( leg.openClose().getApiString() );
150 | b.send( leg.shortSaleSlot() );
151 | b.send( leg.designatedLocation() );
152 | b.send( leg.exemptCode() );
153 | }
154 |
155 | b.send( order.orderComboLegs().size());
156 | for (OrderComboLeg orderComboLeg : order.orderComboLegs() ) {
157 | b.send( orderComboLeg.price());
158 | }
159 |
160 | b.send( order.smartComboRoutingParams().size() );
161 | for (TagValue tagValue : order.smartComboRoutingParams() ) {
162 | b.send( tagValue.m_tag);
163 | b.send( tagValue.m_value);
164 | }
165 | }
166 |
167 | b.send( ""); // obsolete field
168 | b.send( order.discretionaryAmt() );
169 | b.send( order.goodAfterTime() );
170 | b.send( order.goodTillDate() );
171 | b.send( order.faGroup());
172 | b.send( order.faMethod() );
173 | b.send( order.faPercentage() );
174 | b.send( order.faProfile());
175 | b.send( 0); // short sale slot
176 | b.send( ""); // designatedLocation
177 | b.send( ""); // exemptCode
178 | b.send( order.ocaType() );
179 | b.send( order.rule80A() );
180 | b.send( ""); // settlingFirm
181 | b.send( order.allOrNone() );
182 | b.send( order.minQty() );
183 | b.send( order.percentOffset() );
184 | b.send( order.eTradeOnly() );
185 | b.send( order.firmQuoteOnly() );
186 | b.send( order.nbboPriceCap() );
187 | b.send( order.auctionStrategy() );
188 | b.send( order.startingPrice() );
189 | b.send( order.stockRefPrice() );
190 | b.send( order.delta() );
191 | b.send( order.stockRangeLower() );
192 | b.send( order.stockRangeUpper() );
193 | b.send( order.overridePercentageConstraints() );
194 | b.send( order.volatility() );
195 | b.send( order.volatilityType() );
196 | b.send( order.deltaNeutralOrderType() );
197 | b.send( order.deltaNeutralAuxPrice() );
198 |
199 | if (order.deltaNeutralOrderType() != OrderType.None) {
200 | b.send( order.deltaNeutralConId() );
201 | b.send( ""); //deltaNeutralSettlingFirm
202 | b.send( ""); //deltaNeutralClearingAccount
203 | b.send( ""); //deltaNeutralClearingIntent
204 | b.send( ""); //deltaNeutralOpenClose
205 | b.send( ""); //deltaNeutralShortSale
206 | b.send( ""); //deltaNeutralShortSaleSlot
207 | b.send( ""); //deltaNeutralDesignatedLocation
208 | }
209 |
210 | b.send( order.continuousUpdate() );
211 | b.send( order.referencePriceType() );
212 | b.send( order.trailStopPrice() );
213 | b.send( order.trailingPercent() );
214 | b.send( order.scaleInitLevelSize() );
215 | b.send( order.scaleSubsLevelSize() );
216 | b.send( order.scalePriceIncrement() );
217 |
218 | if (order.scalePriceIncrement() != 0 && order.scalePriceIncrement() != Double.MAX_VALUE) {
219 | b.send( order.scalePriceAdjustValue() );
220 | b.send( order.scalePriceAdjustInterval() );
221 | b.send( order.scaleProfitOffset() );
222 | b.send( order.scaleAutoReset() );
223 | b.send( order.scaleInitPosition() );
224 | b.send( order.scaleInitFillQty() );
225 | b.send( order.scaleRandomPercent() );
226 | }
227 |
228 | if (m_serverVersion >= MIN_SERVER_VER_SCALE_TABLE) {
229 | b.send( order.scaleTable() );
230 | b.send( ""); // active start time
231 | b.send( ""); // active stop time
232 | }
233 |
234 | b.send( order.hedgeType() );
235 | if (order.hedgeType() != HedgeType.None) {
236 | b.send( order.hedgeParam() );
237 | }
238 |
239 | b.send( order.optOutSmartRouting() );
240 | b.send( "");//clearingAccount
241 | b.send( "");//clearingIntent
242 | b.send( order.notHeld() );
243 |
244 | b.send( contract.underComp() != null);
245 | if (contract.underComp() != null) {
246 | b.send( contract.underComp().conid() );
247 | b.send( contract.underComp().delta() );
248 | b.send( contract.underComp().price() );
249 | }
250 |
251 | b.send( order.algoStrategy() );
252 | if( order.algoStrategy() != AlgoStrategy.None) {
253 | b.send( order.algoParams().size() );
254 | for( TagValue tagValue : order.algoParams() ) {
255 | b.send( tagValue.m_tag);
256 | b.send( tagValue.m_value);
257 | }
258 | }
259 |
260 | if (m_serverVersion >= MIN_SERVER_VER_ALGO_ID) {
261 | b.send( order.algoId() );
262 | }
263 |
264 | b.send( order.whatIf() );
265 |
266 | // send orderMiscOptions stub
267 | if(m_serverVersion >= MIN_SERVER_VER_LINKING) {
268 | b.send( "" );
269 | }
270 |
271 | closeAndSend( b );
272 | }
273 | catch( Exception e) {
274 | e.printStackTrace();
275 | error( order.orderId(), 512, "Order sending error - " + e);
276 | close();
277 | }
278 | }
279 |
280 | /** An output stream that forks all writes to the output logger. */
281 | private class MyOS extends OutputStream {
282 | final OutputStream m_os;
283 |
284 | MyOS( OutputStream os) {
285 | m_os = os;
286 | }
287 |
288 | @Override public void write(byte[] b) throws IOException {
289 | m_os.write( b);
290 | log( new String( b) );
291 | }
292 |
293 | @Override public synchronized void write(byte[] b, int off, int len) throws IOException {
294 | m_os.write(b, off, len);
295 | log( new String( b, off, len) );
296 | }
297 |
298 | @Override public synchronized void write(int b) throws IOException {
299 | m_os.write(b);
300 | log( String.valueOf( (char)b) );
301 | }
302 |
303 | @Override public void flush() throws IOException {
304 | m_os.flush();
305 | }
306 |
307 | @Override public void close() throws IOException {
308 | m_os.close();
309 | m_outLogger.log( "