lib/ib/gateway.rb in ib-extensions-1.0 vs lib/ib/gateway.rb in ib-extensions-1.1
- old
+ new
@@ -73,71 +73,83 @@
=end
- class Gateway
+ class Gateway
- include LogDev # provides default_logger
- include AccountInfos # provides Handling of Account-Data provided by the tws
- include OrderHandling
+ include Support::Logging # provides default_logger
+ include AccountInfos # provides Handling of Account-Data provided by the tws
+ include OrderHandling
- # include GWSupport # introduces update_or_create, first_or_create and intercept to the Array-Class
+ # include GWSupport # introduces update_or_create, first_or_create and intercept to the Array-Class
- # from active-support. Add Logging at Class + Instance-Level
- mattr_accessor :logger
- # similar to the Connection-Class: current represents the active instance of Gateway
- mattr_accessor :current
- mattr_accessor :tws
+ # from active-support. Add Logging at Class + Instance-Level
+ # similar to the Connection-Class: current represents the active instance of Gateway
+ mattr_accessor :current
+ mattr_accessor :tws
- def initialize port: 4002, # 7497,
+ def initialize port: 4002, # 7497,
host: '127.0.0.1', # 'localhost:4001' is also accepted
client_id: random_id,
- subscribe_managed_accounts: true,
- subscribe_alerts: true,
- subscribe_order_messages: true,
- connect: true,
+ subscribe_managed_accounts: true,
+ subscribe_alerts: true,
+ subscribe_order_messages: true,
+ connect: true,
get_account_data: false,
- serial_array: false,
- logger: default_logger,
+ serial_array: false,
+ logger: nil,
watchlists: [] , # array of watchlists (IB::Symbols::{watchlist}) containing descriptions for complex positions
**other_agruments_which_are_ignored,
&b
- host, port = (host+':'+port.to_s).split(':')
+ host, port = (host+':'+port.to_s).split(':')
- self.logger = logger
- logger.info { '-' * 20 +' initialize ' + '-' * 20 }
- logger.tap{|l| l.progname = 'Gateway#Initialize' }
+ self.class.configure_logger logger
+ self.logger.info { '-' * 20 +' initialize ' + '-' * 20 }
+
@connection_parameter = { received: serial_array, port: port, host: host, connect: false, logger: logger, client_id: client_id }
@account_lock = Mutex.new
@watchlists = watchlists
- @gateway_parameter = { s_m_a: subscribe_managed_accounts,
+ @gateway_parameter = { s_m_a: subscribe_managed_accounts,
s_a: subscribe_alerts,
s_o_m: subscribe_order_messages,
g_a_d: get_account_data }
Thread.report_on_exception = true
# https://blog.bigbinary.com/2018/04/18/ruby-2-5-enables-thread-report_on_exception-by-default.html
Gateway.current = self
- # establish Alert-framework
- IB::Alert.logger = logger
# initialise Connection without connecting
prepare_connection &b
# finally connect to the tws
connect = true if get_account_data
- if connect
- if connect(100) # tries to connect for about 2h
- get_account_data(watchlists: watchlists.map{|b| IB::Symbols.allocate_collection b}) if get_account_data
- # request_open_orders() if request_open_orders || get_account_data
- else
- @accounts = [] # definitivley reset @accounts
+
+ if connect
+ i = 0
+ begin
+ i+=1
+ if connect(100) # tries to connect for about 2h
+ get_account_data(watchlists: watchlists.map{|b| IB::Symbols.allocate_collection b}) if get_account_data
+ # request_open_orders() if request_open_orders || get_account_data
+ else
+ @accounts = [] # definitivley reset @accounts
+ end
+ rescue IB::Error => e
+ disconnect
+ logger.fatal e.message
+ if e.message =~ /NextLocalId is not initialized/
+ Kernel.exit
+ elsif i < 5
+ retry
+ else
+ raise "could not get account data"
+ end
end
end
end
@@ -155,21 +167,20 @@
end
## ------------------------------------- connect ---------------------------------------------##
=begin
-Zentrale Methode
+Zentrale Methode
Es wird ein Connection-Objekt (IB::Connection.current) angelegt.
-Sollte keine TWS vorhanden sein, wird eine entsprechende Meldung ausgegeben und der Verbindungsversuch
+Sollte keine TWS vorhanden sein, wird ein entsprechende Meldung ausgegeben und der Verbindungsversuch
wiederholt.
Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
=end
def connect maximal_count_of_retry=100
i= -1
- logger.progname = 'Gateway#connect'
begin
tws.connect
rescue Errno::ECONNREFUSED => e
i+=1
if i < maximal_count_of_retry
@@ -201,34 +212,34 @@
if r == 30
error "Connected, NextLocalId is not initialized. Repeat with another client_id"
end
end
# initialize @accounts (incl. aliases)
- tws.send_message :RequestFA, fa_data_type: 3
+ tws.send_message( :RequestFA, fa_data_type: 3) if fa?
logger.debug { "Communications successfully established" }
+ # update open orders
+ request_open_orders if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
end # def
def reconnect
- logger.progname = 'Gateway#reconnect'
if tws.present?
disconnect
sleep 1
end
logger.info "trying to reconnect ..."
connect
end
def disconnect
- logger.progname = 'Gateway#disconnect'
tws.disconnect if tws.present?
@accounts = [] # each{|y| y.update_attribute :connected, false }
- logger.info "Connection closed"
+ logger.info "Connection closed"
end
=begin
Proxy for Connection#SendMessage
@@ -237,11 +248,10 @@
checks the connection before sending a message.
=end
def send_message what, *args
- logger.tap{|l| l.progname = 'Gateway#SendMessage' }
begin
if check_connection
tws.send_message what, *args
else
error( "Connection lost. Could not send message #{what}" )
@@ -254,13 +264,12 @@
Argument is either an order-object or a local_id
=end
- def cancel_order *orders
+ def cancel_order *orders
- logger.tap{|l| l.progname = 'Gateway#CancelOrder' }
orders.compact.each do |o|
local_id = if o.is_a? (IB::Order)
logger.info{ "Cancelling #{o.to_human}" }
o.local_id
@@ -271,18 +280,25 @@
end
end
=begin
-clients returns a list of Account-Objects
+clients returns a list of Account-Objects
If only one Account is present, Client and Advisor are identical.
=end
def clients
- @accounts.find_all &:user?
+ @accounts.find_all &:user?
end
+
+# is the account a financial advisor
+ def fa?
+ !(advisor == clients.first)
+ end
+
+
=begin
The Advisor is always the first account
=end
def advisor
@accounts.first
@@ -309,48 +325,42 @@
if block_given?
if account_or_id.present?
sa = account_or_id.is_a?(IB::Account) ? account_or_id : @accounts.detect{|x| x.account == account_or_id }
safe[sa] if sa.is_a? IB::Account
else
- clients.map{|sa| safe[sa]}
+ clients.map{|s| safe[s]}
end
end
end
- private
- def random_id
- rand 99999
- end
-
-
def prepare_connection &b
tws.disconnect if tws.is_a? IB::Connection
- self.tws = IB::Connection.new @connection_parameter
+ self.tws = IB::Connection.new **@connection_parameter.merge( logger: self.logger )
@accounts = @local_orders = Array.new
# prepare Advisor-User hierachy
initialize_managed_accounts if @gateway_parameter[:s_m_a]
initialize_alerts if @gateway_parameter[:s_a]
- initialize_order_handling if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
+ initialize_order_handling if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
## apply other initialisations which should apper before the connection as block
## i.e. after connection order-state events are fired if an open-order is pending
## a possible response is best defined before the connect-attempt is done
# ## Attention
# ## @accounts are not initialized yet (empty array)
- if block_given?
+ if block_given?
yield self
end
end
=begin
-InitializeManagedAccounts
+InitializeManagedAccounts
defines the Message-Handler for :ManagedAccounts
-Its always active.
+Its always active.
=end
def initialize_managed_accounts
rec_id = tws.subscribe( :ReceiveFA ) do |msg|
msg.accounts.each do |a|
@@ -358,11 +368,10 @@
end
logger.info { "Accounts initialized \n #{@accounts.map( &:to_human ).join " \n " }" }
end
man_id = tws.subscribe( :ManagedAccounts ) do |msg|
- logger.progname = 'Gateway#InitializeManagedAccounts'
if @accounts.empty?
# just validate the message and put all together into an array
@accounts = msg.accounts_list.split(',').map do |a|
account = IB::Account.new( account: a.upcase , connected: true )
end
@@ -375,31 +384,30 @@
def initialize_alerts
tws.subscribe( :AccountUpdateTime ){| msg | logger.debug{ msg.to_human }}
- tws.subscribe(:Alert) do |msg|
- logger.progname = 'Gateway#Alerts'
+ tws.subscribe(:Alert) do |msg|
logger.debug " ----------------#{msg.code}-----"
# delegate anything to IB::Alert
IB::Alert.send("alert_#{msg.code}", msg )
end
end
# Handy method to ensure that a connection is established and active.
#
- # The connection is reset on the IB-side at least once a day. Then the
- # IB-Ruby-Connection has to be reestablished, too.
- #
- # check_connection reconnects if necessary and returns false if the connection is lost.
- #
+ # The connection is reset on the IB-side at least once a day. Then the
+ # IB-Ruby-Connection has to be reestablished, too.
+ #
+ # check_connection reconnects if necessary and returns false if the connection is lost.
+ #
# It delays the process by 6 ms (150 MBit Cable connection)
#
# a = Time.now; G.check_connection; b= Time.now ;b-a
# => 0.00066005
- #
+ #
def check_connection
answer = nil; count=0
z= tws.subscribe( :CurrentTime ) { answer = true }
while (answer.nil?)
begin
@@ -417,9 +425,15 @@
break if count > 5
end
tws.unsubscribe z
count < 5 && answer # return value
end
+ private
+
+ def random_id
+ rand 99999
+ end
+
end # class
end # module