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
IB::Gateway
keeps Account- and Portfolio-Data, Contracts, Orders in an inline database
and provides account-based trading tools.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 instantiatedIB::Connection
and its methods. -
IB::Gateway.current
links to the instantiatedIB::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
OrderState
-Objects. But never use the same order object for different users.
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_order
returns 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>
(...)