IB::Gateway is part of ib-extensions. Include
 
require 'ib-api' 
require 'ib/gateway' 
in your scripts.

public interface

connect disconnect reconnect   check_connection
tws        
get_account_data update_orders all_contracts    
advisor clients      

Typical setup

require 'bundler/setup'
require 'ib-api'
require 'ib-gateway'

TWS= 'localhost:7496'  
GATEWAY= '127.0.0.1:4002'  

IB::Gateway.new host: TWS 

if  IB::Gateway.current.present?
    (...)
  else
   puts 'No TWS'
  end

If called with get_account_data: true it detects accounts and reads available data from the TWS.

Later, get_account_data and update_orders synchonize the internal database with TWS-Data.

## use /bin/gateway to reproduce
 gw = IB::Gateway.new host: TWS, get_account_data: true
 puts gw.clients.portfolio_values.to_human
 <PortfolioValue: Pos=-8 @ 0.0;Value=0.0;PNL=<Option: BOSS 20210416 put 33.0 DTB EUR>
 <PortfolioValue: Pos=-4 @ 0.0;Value=0.0;PNL=<Option: STM 20210416 put 30.0 DTB EUR>

IB::Gateway provides an intuitive interface for order-placement and -management.

Accounts

In a financial advisor environment, after initialization IB::Gateway attached accounts are present through

> gw =  IB::Gateway.current
> gw.advisor.to_human
 => "<demo_advisor DF167347>" 
> puts gw.clients.to_human
<demo_user DU167348>
<demo_user DU167349>

In single user mode Advisor and Client are identical:

> gw =  IB::Gateway.current
> gw.advisor.to_human
 => "<user U1274612>" 
> puts gw.clients.to_human
<user U1274612>
  • IB::Gateway.tws points to the active and instantiated IB::Connection and its methods.

  • IB::Gateway.current links to the instantiated IB::Gateway and its account-database.

IB::Gateway.current.clients contain Account-Positions and Portfolio-Values. They are synchronized with the TWS by IB::Gateway.current.get_account_data {the account} and beeing accessed via

g = IB::Gateway.current
# thread safe access
g.account_data &:portfolio_values

g.account_data &:account_values

# primitive access
g.clients.map &:portfolio_values
g.clients.map &:account_values

Visit Account Overview for further details.

Order Management

A simplified order management process is included.

The centerpiece is an IB::Order-Object. It is reused and updated through the process and keeps the history and current status.

The process is initiated by calling IB::Account.preview or IB::Account.place with IB::Contract and IB::Order_Prototype as arguments.
Other steps ( modify, reverse, close or cancel ) just require the IB-Order object as argument.

> g =  IB::Gateway.current
> the_contract =  IB::Symbols::Stocks.msft  # Microsoft
> the_order = Limit.order size: 100, 
                          price: the_contract.market_price - 1 ,
                          action: :buy
> the_order.to_human
 => "<Order: LMT GTC buy 100  @ 201.33 New #/ from >" 

> the_account  =  g.clients.first

> the_account.preview order: the_order, 
                      contract: the_contract
  # returns an array with projected what-if values											
  => {:init_margin=>0.1344327e5, :maint_margin=>0.1168288e5, 
      :equity_with_loan=>0.118936284e7, :commission=>nil, 
      :commission_currency=>"USD", :warning=>""} 

> the_account.place order: the_order

> g.cancel_order the_order              # general order cancelling facility
> the_account.cancel order: the_order   # identical

The general procedure for order-tasks:

> the_account.{ preview | place | modify | reverse | close  } order: the_order, 
                                                              contract: the_contract

Locate Order

After initialisation, open orders are retrieved from the TWS. They are associated to the Clients.

locate_order offers a versatile option to monitor the status an order.

> g =  IB::Gateway.current
> one= g.clients.first
> one.orders                        => []  #  No open orders

> dbk =  Stock.new( symbol: :dbk, currency: :eur).verify.first
> dbk.market_price                  => 10.428  
> one.place contract: dbk, order: Limit.order( action: :buy, price: 10, size: 100  )
=> 4 
 # Now lets fetch the order
> one.locate_order( contract: dbk  ).to_human
 => "<Order: LMT GTC buy 100.0  @ 10.0 Submitted #4/743282540 from 2000/DU167348>" 
 #  cancel it and retrieve the acknowledge from the TWS
> open_order = one.locate_order( contract: dbk  )
> one.cancel order: open_order
	11:57:40 Cancelling <Order: LMT GTC buy 100.0  @ 10.0 Submitted #4/743282540 from 2000/DU167348>  # log-output
> one.locate_order( contract: dbk , status: nil  ).to_human
 => "<Order: LMT GTC buy 100.0  @ 10.0 Cancelled #4/743282540 from 2000/DU167348>" 
> one.locate_order contract: dbk  => nil

locate_order returns only one order-object (or nil). If the steps described above (preview, place, modify) are performed, each time another IB::Order is added to the Client.order array. But only the last one is active. locate_orderreturns this order-object.`

To obtain any submitted order, just search manually


> one= G.clients.first
> one.orders.find_all{|o|  o.order_state.status ~= /ubmitted/ }     # finds Presubmited and Submitted

Interactive Shell

Analogous to the console script that enables experiments with IB::Connection, running ./gateway.rb« in the »bin« directory of ib-extensions opens an interactive console with IB::Gateway preloaded.

-------------------- initialize --------------------
Connected to server, version: 137,
connection time: 2018-04-07 17:10:44 +0000 local, 2018-04-07T17:10:44+00:00 remote.
new demo_advisor detected => DF167347
new demo_user detected => DU167348
new demo_user detected => DU167349
Got next valid order id: 1.
DU167348 => Count of AccountValues: 218 
DU167349 => Count of AccountValues: 218 
Connection established on Port  4002, client_id 3000 used
		 
----> G    points to the Gateway-Instance
----> C    points to the Connection-Instance
		 
 ---------------------------------------------

> G.clients.to_human
  => ["<demo_user DU167348>", "<demo_user DU167349>"]

Contracts, Account- and PortfolioValues are always present


> G.clients.first.contracts.to_human
    => ["<Stock: BLUE EUR>", "<Stock: CBA AUD>", "<Stock: CIEN USD>", 
        "<Stock: CSCO USD>", "<Stock: DBA USD>", "<Stock: DBB USD>", 
        "<Stock: GE USD>", "<Option: GOOG 20190118 call 1130.0 SMART USD>", 
        "<Option: GOOG 20190118 call 1150.0 SMART USD>", 
        "<Option: GOOG 20190118 call 1170.0 SMART USD>", 
        "<Stock: LHA EUR>", "<Stock: WFC USD>", "<Stock: WMT USD>"] 
				
> puts G.clients.first.account_data_scan /PnL/
  <UnrealizedPnL=0.00 AUD>
  <RealizedPnL=0.00 AUD>
  <UnrealizedPnL=4391.77 BASE>
  <RealizedPnL=0.00 BASE>
  <UnrealizedPnL=6284.20 EUR>
  <RealizedPnL=0.00 EUR>
  <UnrealizedPnL=0.00 HKD>
  <RealizedPnL=0.00 HKD>
  <UnrealizedPnL=0.00 JPY>
  <RealizedPnL=0.00 JPY>
  <UnrealizedPnL=-2205.57 USD>
  <RealizedPnL=0.00 USD>
 
> puts G.clients.portfolio_values.to_human
  <PortfolioValue: DU167348 Pos=720 @ 16.365;Value=11782.87;PNL=-4520.41 unrealized;<Stock: BLUE EUR SBF>
  <PortfolioValue: DU167348 Pos=812 @ 31.31;Value=25423.64;PNL=6199.54 unrealized;<Stock: CIEN USD NYSE>
  <PortfolioValue: DU167348 Pos=44 @ 47.03;Value=2069.32;PNL=1125.52 unrealized;<Stock: CSCO USD NASDAQ>
  <PortfolioValue: DU167348 Pos=1366 @ 17.059;Value=23302.32;PNL=-11281.23 unrealized;<Stock: DBA USD ARCA>
  <PortfolioValue: DU167348 Pos=-1 @ 15.662;Value=-15.66;PNL=1.97 unrealized;<Stock: DBB USD ARCA>
  <PortfolioValue: DU167348 Pos=100 @ 60.174;Value=6017.4;PNL=-437.76 unrealized;<Stock: J36 USD SGX>
  <PortfolioValue: DU167348 Pos=100 @ 57.35;Value=5735.0;PNL=439.0 unrealized;<Stock: WFC USD NYSE>
  <PortfolioValue: DU167349 Pos=48 @ 98.83;Value=4743.84;PNL=1816.32 unrealized;<Stock: ALB USD NYSE>
  <PortfolioValue: DU167349 Pos=740 @ 16.365;Value=12110.17;PNL=-4637.96 unrealized;<Stock: BLUE EUR SBF>
	(...)
Tags: