Class: IB::Gateway

Inherits:
Object
  • Object
show all
Includes:
AccountInfos, LogDev, OrderHandling
Defined in:
lib/ib/gateway.rb

Overview

The Gateway-Class defines anything which has to be done before a connection can be established. The Default Skeleton can easily be substituted by customized actions

The IB::Gateway can be used in three modes (1) IB::Gateway.new( connect:true, –other arguments– ) do | gateway | ** subscribe to Messages and define the response ** # This block is executed before a connect-attempt is made end (2) gw = IB:Gateway.new ** subscribe to Messages ** gw.connect (3) IB::Gateway.new connect:true, host: ‘localhost’ .…

Independently IB::Alert.alert_#nnn should be defined for a proper response to warnings, error- and system-messages.

The Connection to the TWS is realized throught IB::Connection. Additional to IB::Connection.current IB::Gateway.tws points to the active Connection.

To support asynchronic access, the :recieved-Array of the Connection-Class is not active. The Array is easily confused, if used in production mode with a FA-Account and has limits. Thus IB::Conncetion.wait_for(message) is not available until the programm is called with IB::Gateway.new serial_array: true (, …)

Instance Method Summary collapse

Methods included from OrderHandling

#initialize_order_handling, #request_open_orders, #update_order_dependent_object

Methods included from AccountInfos

#all_contracts, #get_account_data

Constructor Details

#initialize(port: 4002, host: '127.0.0.1', client_id: random_id, subscribe_managed_accounts: true, subscribe_alerts: true, subscribe_order_messages: true, connect: true, get_account_data: false, serial_array: false, logger: default_logger, watchlists: [], **other_agruments_which_are_ignored, &b) ⇒ Gateway

7497,



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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/ib/gateway.rb', line 94

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, 
	get_account_data: false,
	serial_array: false, 
	logger: default_logger,
	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(':') 

	self.logger = logger
	logger.info { '-' * 20 +' initialize ' + '-' * 20 }
	logger.tap{|l| l.progname =  'Gateway#Initialize' }

	@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, 
											s_a: subscribe_alerts,
											s_o_m: subscribe_order_messages,
											g_a_d:  }


	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 
	if connect 
		if connect(100)  # tries to connect for about 2h
			(watchlists: watchlists.map{|b| IB::Symbols.allocate_collection b})  if 
			#    request_open_orders() if request_open_orders || get_account_data 
		else
			@accounts = []   # definitivley reset @accounts
		end
	end

end

Instance Method Details

#account_data(account_or_id = nil) ⇒ Object

account_data provides a thread-safe access to linked content of accounts

(AccountValues, Portfolio-Values, Contracts and Orders)

It returns an Array of the return-values of the block

If called without a parameter, all clients are accessed



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/ib/gateway.rb', line 301

def  =nil

	safe = ->() do
		@account_lock.synchronize do
			yield  
		end
	end

	if block_given?
		if .present?
			sa = .is_a?(IB::Account) ?  :  @accounts.detect{|x| x. ==  }
			safe[sa] if sa.is_a? IB::Account
		else
			clients.map{|sa| safe[sa]}
		end
	end
end

#active_watchlistsObject



144
145
146
# File 'lib/ib/gateway.rb', line 144

def active_watchlists
	@watchlists
end

#advisorObject

The Advisor is always the first account



287
288
289
# File 'lib/ib/gateway.rb', line 287

def advisor
	@accounts.first
end

#cancel_order(*orders) ⇒ Object

Cancels one or multible orders

Argument is either an order-object or a local_id



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/ib/gateway.rb', line 259

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
							 else
								 o
							 end
		send_message :CancelOrder, :local_id => local_id.to_i
	end

end

#clientsObject

clients returns a list of Account-Objects

If only one Account is present, Client and Advisor are identical.



281
282
283
# File 'lib/ib/gateway.rb', line 281

def  clients
	@accounts.find_all &:user? 
end

#connect(maximal_count_of_retry = 100) ⇒ Object

Zentrale Methode Es wird ein Connection-Objekt (IB::Connection.current) angelegt. Sollte keine TWS vorhanden sein, wird eine entsprechende Meldung ausgegeben und der Verbindungsversuch wiederholt. Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/ib/gateway.rb', line 167

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
			if i.zero?
				logger.info 'No TWS!'
			else
				logger.info {"No TWS        Retry #{i}/ #{maximal_count_of_retry} " }
			end
			sleep i<50 ? 10 : 60   # Die ersten 50 Versuche im 10 Sekunden Abstand, danach 1 Min.
			retry
		else
			logger.info { "Giving up!!" }
			return false
		end
	rescue Errno::EHOSTUNREACH => e
		logger.error 'Cannot connect to specified host'
		logger.error  e
		return false
	rescue SocketError => e
		logger.error 'Wrong Adress, connection not possible'
		return false
	end

	tws.start_reader
	# let NextValidId-Event appear
	(1..30).each do |r|
		break if tws.next_local_id.present?
		sleep 0.1
		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
	logger.debug { "Communications successfully established" }
end

#disconnectObject



224
225
226
227
228
229
230
# File 'lib/ib/gateway.rb', line 224

def disconnect
	logger.progname = 'Gateway#disconnect'

	tws.disconnect if tws.present?
	@accounts = [] # each{|y| y.update_attribute :connected,  false }
	logger.info "Connection closed" 
end

#get_hostObject



148
149
150
# File 'lib/ib/gateway.rb', line 148

def get_host
	"#{@connection_parameter[:host]}: #{@connection_parameter[:port] }"
end

#reconnectObject



214
215
216
217
218
219
220
221
222
# File 'lib/ib/gateway.rb', line 214

def reconnect
	logger.progname = 'Gateway#reconnect'
	if tws.present?
		disconnect
		sleep 1
	end
	logger.info "trying to reconnect ..."
	connect
end

#send_message(what, *args) ⇒ Object

Proxy for Connection#SendMessage allows reconnection if a socket_error occurs

checks the connection before sending a message.



241
242
243
244
245
246
247
248
249
250
# File 'lib/ib/gateway.rb', line 241

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}" )
		end
	end
end

#update_local_order(order) ⇒ Object



152
153
154
155
# File 'lib/ib/gateway.rb', line 152

def update_local_order order
	# @local_orders is initialized by #PrepareConnection
	@local_orders.update_or_create order, :local_id
end