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