ib-orientdb 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +56 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +128 -0
- data/Guardfile +24 -0
- data/LICENSE +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +99 -0
- data/bin/gateway +92 -0
- data/bin/readme.md +1 -0
- data/changelog.md +10 -0
- data/ib-orientdb.gemspec +42 -0
- data/lib/alerts/base-alert.rb +143 -0
- data/lib/alerts/gateway-alerts.rb +16 -0
- data/lib/alerts/order-alerts.rb +68 -0
- data/lib/ib-orientdb.rb +12 -0
- data/lib/ib/account-infos.rb +115 -0
- data/lib/ib/account-init.rb +151 -0
- data/lib/ib/orient-gateway.rb +362 -0
- data/lib/ib/setup-orientdb.rb +112 -0
- data/lib/logging.rb +34 -0
- data/lib/models/hc/d2_f.rb +0 -0
- data/lib/models/hc/grid.rb +0 -0
- data/lib/models/hc/has_portfolio.rb +0 -0
- data/lib/models/hc/has_position.rb +0 -0
- data/lib/models/hc/has_strategy.rb +0 -0
- data/lib/models/hc/hc_grid.rb +0 -0
- data/lib/models/hc/my_user.rb +0 -0
- data/lib/models/hc/p2_u.rb +0 -0
- data/lib/models/hc/portfolio.rb +161 -0
- data/lib/models/ib/account.rb +5 -0
- data/lib/models/ib/account_value.rb +29 -0
- data/lib/models/ib/advisor.rb +0 -0
- data/lib/models/ib/contract.rb +15 -0
- data/lib/models/ib/demo_advisor.rb +0 -0
- data/lib/models/ib/demo_user.rb +0 -0
- data/lib/models/ib/financials.rb +6 -0
- data/lib/models/ib/has_account.rb +0 -0
- data/lib/models/ib/portfolio_value.rb +10 -0
- data/lib/models/ib/spread.rb +0 -0
- data/lib/models/ib/user.rb +0 -0
- data/lib/models/tg/tag.rb +16 -0
- data/lib/support.rb +21 -0
- data/lib/version.rb +5 -0
- data/setup.md +83 -0
- metadata +231 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
module IB
|
2
|
+
module AccountInfos
|
3
|
+
|
4
|
+
def load_managed_accounts
|
5
|
+
|
6
|
+
# defines the callback of the ManagedAccount message
|
7
|
+
#
|
8
|
+
# The @accounts-array is initialized with active accounts
|
9
|
+
Thread.new do
|
10
|
+
account_class = ->(a) do
|
11
|
+
case a
|
12
|
+
when /^U/
|
13
|
+
IB::User
|
14
|
+
when /^F/
|
15
|
+
IB::Advisor
|
16
|
+
when /^DF/
|
17
|
+
IB::DemoAdvisor
|
18
|
+
when /^DU/
|
19
|
+
IB::DemoUser
|
20
|
+
else
|
21
|
+
IB::Account
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@accounts =[]
|
25
|
+
c= IB::Connection.current
|
26
|
+
man_id = c.subscribe( :ManagedAccounts ) do |msg|
|
27
|
+
@accounts = msg.accounts_list.split(',').map do |a|
|
28
|
+
account_class[a].new( account: a.upcase , last_access: Time.now ).save
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rec_id = c.subscribe( :ReceiveFA ) do |msg|
|
32
|
+
msg.accounts.each{ |a| IB::Account.where( account: a.account ).first.update alias: a.alias }
|
33
|
+
end
|
34
|
+
|
35
|
+
loop{ sleep 1 ; break if !@accounts.empty? } # keep it active for 1 second
|
36
|
+
c.unsubscribe man_id , rec_id # release callbacks
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
#2.6.3 :013 > IB::Account.delete all: true
|
44
|
+
#03.12.(17:45:47) INFO->delete vertex ib_account
|
45
|
+
# => 8
|
46
|
+
#2.6.3 :014 > load_managed_accounts
|
47
|
+
# => #<Thread:0x0000000003d57c78@/home/ubuntu/workspace/ib-orientdb/lib/ib/account_init.rb:9 run>
|
48
|
+
#2.6.3 :015 > C.disconnect
|
49
|
+
# => false
|
50
|
+
#2.6.3 :016 > C.connect
|
51
|
+
#Connected to server, version: 137,
|
52
|
+
# connection time: 2020-12-03 17:46:03 +0000 local, 2020-12-03T17:46:03+00:00 remote.
|
53
|
+
#< ManagedAccounts: DF167347 - DU167348 - DU167349>
|
54
|
+
# => #<Thread:0x0000000003fecee8@/home/ubuntu/workspace/ib-api/lib/ib/connection.rb:379 run>
|
55
|
+
#2.6.3 :017 > Got next valid order id: 1.
|
56
|
+
#TWS Warning 2104: Market data farm connection is OK:eufarm
|
57
|
+
#
|
58
|
+
#2.6.3 :018 > IB::Account.all.to_human
|
59
|
+
#03.12.(17:46:12) INFO->select from ib_account
|
60
|
+
# => ["<demo_advisor DF167347>", "<demo_user DU167348>", "<demo_user DU167349>"]
|
61
|
+
#
|
62
|
+
#
|
63
|
+
|
64
|
+
=begin
|
65
|
+
Queries the tws for Account- and PortfolioValues
|
66
|
+
The parameter can either be the account_id, the IB::Account-Object or
|
67
|
+
an Array of account_id and IB::Account-Objects.
|
68
|
+
|
69
|
+
raises an IB::TransmissionError if the account-data are not transmitted in time (1 sec)
|
70
|
+
|
71
|
+
raises an IB::Error if less then 100 items are received-
|
72
|
+
=end
|
73
|
+
def get_account_data
|
74
|
+
|
75
|
+
logger.progname = 'Gateway#get_account_data'
|
76
|
+
|
77
|
+
|
78
|
+
# Account-infos have to be requested sequentially.
|
79
|
+
# subsequent (parallel) calls kill the former once on the tws-server-side
|
80
|
+
# In addition, there is no need to cancel the subscription of an request, as a new
|
81
|
+
# one overwrites the active one.
|
82
|
+
@accounts.each do | account |
|
83
|
+
# don't repeat the query until 170 sec. have passed since the previous update
|
84
|
+
if account.lad.nil? || ( Time.now - account.lad ) > 170 # sec
|
85
|
+
logger.debug{ "#{account.account} :: Requesting AccountData " }
|
86
|
+
account[:active] = false # indicates: AccountUpdate in Progress, volatile
|
87
|
+
# reset account and portfolio-values
|
88
|
+
account.portfolio_values = []
|
89
|
+
account.account_values = []
|
90
|
+
send_message :RequestAccountData, subscribe: true, account_code: account.account
|
91
|
+
loop{ sleep 0.1; break if account.active }
|
92
|
+
# if watchlists.present?
|
93
|
+
# watchlists.each{|w| error "Watchlists must be IB::Symbols--Classes :.#{w.inspect}" unless w.is_a? IB::Symbols }
|
94
|
+
# account.organize_portfolio_positions watchlists
|
95
|
+
# end
|
96
|
+
send_message :RequestAccountData, subscribe: false ## do this only once
|
97
|
+
else
|
98
|
+
logger.info{ "#{account.account} :: Using stored AccountData " }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def all_contracts
|
106
|
+
clients.map(&:contracts).flat_map(&:itself).uniq(&:con_id)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
# private
|
112
|
+
|
113
|
+
# The subscription method should called only once per session.
|
114
|
+
# It places subscribers to AccountValue and PortfolioValue Messages, which should remain
|
115
|
+
# active through its session.
|
116
|
+
#
|
117
|
+
# Account- and Portfolio-Values are stored in account.account_values and account.portfolio_values
|
118
|
+
# The Arrays are volatile.
|
119
|
+
|
120
|
+
def subscribe_account_updates continously: true
|
121
|
+
puts "SELF: #{self.class.to_s}"
|
122
|
+
IB::Connection.current.subscribe( :AccountValue, :PortfolioValue,:AccountDownloadEnd ) do | msg |
|
123
|
+
account = @accounts.detect{|a| a.account == msg.account_name }
|
124
|
+
case msg
|
125
|
+
when IB::Messages::Incoming::AccountValue
|
126
|
+
account.account_values = [] unless account.account_values.present?
|
127
|
+
account.account_values.<< msg.account_value
|
128
|
+
|
129
|
+
account[:lad] = Time.now
|
130
|
+
# logger.debug { "#{account.account} :: #{msg.account_value.to_human }"}
|
131
|
+
when IB::Messages::Incoming::AccountDownloadEnd
|
132
|
+
if account.account_values.size > 10
|
133
|
+
# simply don't cancel the subscription if continuously is specified
|
134
|
+
# the connected flag is set in any case, indicating that valid data are present
|
135
|
+
send_message :RequestAccountData, subscribe: false, account_code: account.account unless continously
|
136
|
+
account[:active] = true ## flag: Account is completely initialized
|
137
|
+
logger.info { "#{account.account} => Count of AccountValues: #{account.account_values.size}" }
|
138
|
+
else # unreasonable account_data received - request is still active
|
139
|
+
error "#{account.account} => Count of AccountValues too small: #{account.account_values.size}" , :reader
|
140
|
+
end
|
141
|
+
when IB::Messages::Incoming::PortfolioValue
|
142
|
+
account.contracts << msg.contract.save
|
143
|
+
account.portfolio_values << msg.portfolio_value
|
144
|
+
logger.debug { "#{ account.account } :: #{ msg.contract.to_human }" }
|
145
|
+
end # case
|
146
|
+
end # subscribe
|
147
|
+
end # def
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,362 @@
|
|
1
|
+
#
|
2
|
+
require_relative 'account-init'
|
3
|
+
#require 'ib/gateway/order-handling'
|
4
|
+
require_relative '../alerts/base-alert'
|
5
|
+
require_relative '../alerts/gateway-alerts'
|
6
|
+
require_relative '../alerts/order-alerts'
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors' # provides m_accessor
|
8
|
+
#module GWSupport
|
9
|
+
# provide AR4- ActiveRelation-like-methods to Array-Class
|
10
|
+
|
11
|
+
module IB
|
12
|
+
|
13
|
+
=begin
|
14
|
+
The Gateway-Class defines anything which has to be done before a connection can be established.
|
15
|
+
The Default Skeleton can easily be substituted by customized actions
|
16
|
+
|
17
|
+
The IB::Gateway can be used in three modes
|
18
|
+
(1) IB::Gateway.new( connect:true, --other arguments-- ) do | gateway |
|
19
|
+
** subscribe to Messages and define the response **
|
20
|
+
# This block is executed before a connect-attempt is made
|
21
|
+
end
|
22
|
+
(2) gw = IB:Gateway.new
|
23
|
+
** subscribe to Messages **
|
24
|
+
gw.connect
|
25
|
+
(3) IB::Gateway.new connect:true, host: 'localhost' ....
|
26
|
+
|
27
|
+
Independently IB::Alert.alert_#{nnn} should be defined for a proper response to warnings, error-
|
28
|
+
and system-messages.
|
29
|
+
|
30
|
+
|
31
|
+
The Connection to the TWS is realized throught IB::Connection. Additional to __IB::Connection.current__
|
32
|
+
IB::Gateway.tws points to the active Connection.
|
33
|
+
|
34
|
+
To support asynchronic access, the :recieved-Array of the Connection-Class is not active.
|
35
|
+
The Array is easily confused, if used in production mode with a FA-Account and has limits.
|
36
|
+
Thus IB::Conncetion.wait_for(message) is not available until the programm is called with
|
37
|
+
IB::Gateway.new serial_array: true (, ...)
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
=end
|
42
|
+
|
43
|
+
class OrientGateway
|
44
|
+
|
45
|
+
include LogDev # provides default_logger
|
46
|
+
include IB::AccountInfos
|
47
|
+
# include IB::AccountInfos # provides Handling of Account-Data provided by the tws
|
48
|
+
# include OrderHandling
|
49
|
+
|
50
|
+
# include GWSupport # introduces update_or_create, first_or_create and intercept to the Array-Class
|
51
|
+
|
52
|
+
# from active-support. Add Logging at Class + Instance-Level
|
53
|
+
mattr_accessor :logger
|
54
|
+
# similar to the Connection-Class: current represents the active instance of Gateway
|
55
|
+
mattr_accessor :current
|
56
|
+
mattr_accessor :tws
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
def initialize port: 4002, # 7497,
|
61
|
+
host: '127.0.0.1', # 'localhost:4001' is also accepted
|
62
|
+
client_id: random_id,
|
63
|
+
subscribe_managed_accounts: true,
|
64
|
+
subscribe_alerts: true,
|
65
|
+
subscribe_order_messages: true,
|
66
|
+
connect: true,
|
67
|
+
get_account_data: false,
|
68
|
+
serial_array: false,
|
69
|
+
logger: default_logger,
|
70
|
+
watchlists: [] , # array of watchlists (IB::Symbols::{watchlist}) containing descriptions for complex positions
|
71
|
+
**other_agruments_which_are_ignored,
|
72
|
+
&b
|
73
|
+
|
74
|
+
host, port = (host+':'+port.to_s).split(':')
|
75
|
+
|
76
|
+
self.logger = logger
|
77
|
+
logger.info { '-' * 20 +' initialize ' + '-' * 20 }
|
78
|
+
logger.tap{|l| l.progname = 'Gateway#Initialize' }
|
79
|
+
|
80
|
+
@connection_parameter = { received: serial_array, port: port, host: host, connect: false, logger: logger, client_id: client_id }
|
81
|
+
|
82
|
+
@accounts = []
|
83
|
+
@watchlists = watchlists
|
84
|
+
@gateway_parameter = { s_m_a: subscribe_managed_accounts,
|
85
|
+
s_a: subscribe_alerts,
|
86
|
+
s_o_m: subscribe_order_messages,
|
87
|
+
g_a_d: get_account_data }
|
88
|
+
|
89
|
+
|
90
|
+
Thread.report_on_exception = true
|
91
|
+
# https://blog.bigbinary.com/2018/04/18/ruby-2-5-enables-thread-report_on_exception-by-default.html
|
92
|
+
self.current = self
|
93
|
+
# establish Alert-framework
|
94
|
+
#IB::Alert.logger = logger
|
95
|
+
# initialise Connection without connecting
|
96
|
+
prepare_connection &b
|
97
|
+
# finally connect to the tws
|
98
|
+
connect = true if get_account_data
|
99
|
+
if connect
|
100
|
+
if connect(100) # tries to connect for about 2h
|
101
|
+
get_account_data() if get_account_data
|
102
|
+
# request_open_orders() if request_open_orders || get_account_data
|
103
|
+
else
|
104
|
+
@accounts = [] # definitivley reset @accounts
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
def active_watchlists
|
111
|
+
@watchlists
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_host
|
115
|
+
"#{@connection_parameter[:host]}: #{@connection_parameter[:port] }"
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_local_order order
|
119
|
+
# @local_orders is initialized by #PrepareConnection
|
120
|
+
@local_orders.update_or_create order, :local_id
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
## ------------------------------------- connect ---------------------------------------------##
|
125
|
+
=begin
|
126
|
+
Zentrale Methode
|
127
|
+
Es wird ein Connection-Objekt (IB::Connection.current) angelegt.
|
128
|
+
Sollte keine TWS vorhanden sein, wird eine entsprechende Meldung ausgegeben und der Verbindungsversuch
|
129
|
+
wiederholt.
|
130
|
+
Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
131
|
+
|
132
|
+
=end
|
133
|
+
def connect maximal_count_of_retry=100
|
134
|
+
|
135
|
+
i= -1
|
136
|
+
logger.progname = 'Gateway#connect'
|
137
|
+
begin
|
138
|
+
tws.connect
|
139
|
+
rescue Errno::ECONNREFUSED => e
|
140
|
+
i+=1
|
141
|
+
if i < maximal_count_of_retry
|
142
|
+
if i.zero?
|
143
|
+
logger.info 'No TWS!'
|
144
|
+
else
|
145
|
+
logger.info {"No TWS Retry #{i}/ #{maximal_count_of_retry} " }
|
146
|
+
end
|
147
|
+
sleep i<50 ? 10 : 60 # Die ersten 50 Versuche im 10 Sekunden Abstand, danach 1 Min.
|
148
|
+
retry
|
149
|
+
else
|
150
|
+
logger.info { "Giving up!!" }
|
151
|
+
return false
|
152
|
+
end
|
153
|
+
rescue Errno::EHOSTUNREACH => e
|
154
|
+
logger.error 'Cannot connect to specified host'
|
155
|
+
logger.error e
|
156
|
+
return false
|
157
|
+
rescue SocketError => e
|
158
|
+
logger.error 'Wrong Adress, connection not possible'
|
159
|
+
return false
|
160
|
+
end
|
161
|
+
|
162
|
+
tws.start_reader
|
163
|
+
# let NextValidId-Event appear
|
164
|
+
(1..30).each do |r|
|
165
|
+
break if tws.next_local_id.present?
|
166
|
+
sleep 0.1
|
167
|
+
if r == 30
|
168
|
+
error "Connected, NextLocalId is not initialized. Repeat with another client_id"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
# initialize @accounts (incl. aliases)
|
172
|
+
tws.send_message :RequestFA, fa_data_type: 3
|
173
|
+
logger.debug { "Communications successfully established" }
|
174
|
+
end # def
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
|
180
|
+
def reconnect
|
181
|
+
logger.progname = 'Gateway#reconnect'
|
182
|
+
if tws.present?
|
183
|
+
disconnect
|
184
|
+
sleep 1
|
185
|
+
end
|
186
|
+
logger.info "trying to reconnect ..."
|
187
|
+
connect
|
188
|
+
end
|
189
|
+
|
190
|
+
def disconnect
|
191
|
+
logger.progname = 'Gateway#disconnect'
|
192
|
+
|
193
|
+
tws.disconnect if tws.present?
|
194
|
+
@accounts = [] # each{|y| y.update_attribute :connected, false }
|
195
|
+
logger.info "Connection closed"
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
=begin
|
200
|
+
Proxy for Connection#SendMessage
|
201
|
+
allows reconnection if a socket_error occurs
|
202
|
+
|
203
|
+
checks the connection before sending a message.
|
204
|
+
|
205
|
+
=end
|
206
|
+
|
207
|
+
def send_message what, *args
|
208
|
+
logger.tap{|l| l.progname = 'Gateway#SendMessage' }
|
209
|
+
begin
|
210
|
+
if check_connection
|
211
|
+
tws.send_message what, *args
|
212
|
+
else
|
213
|
+
error( "Connection lost. Could not send message #{what}" )
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
=begin
|
219
|
+
Cancels one or multible orders
|
220
|
+
|
221
|
+
Argument is either an order-object or a local_id
|
222
|
+
|
223
|
+
=end
|
224
|
+
|
225
|
+
def cancel_order *orders
|
226
|
+
|
227
|
+
logger.tap{|l| l.progname = 'Gateway#CancelOrder' }
|
228
|
+
|
229
|
+
orders.compact.each do |o|
|
230
|
+
local_id = if o.is_a? (IB::Order)
|
231
|
+
logger.info{ "Cancelling #{o.to_human}" }
|
232
|
+
o.local_id
|
233
|
+
else
|
234
|
+
o
|
235
|
+
end
|
236
|
+
send_message :CancelOrder, :local_id => local_id.to_i
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
=begin
|
242
|
+
clients returns a list of Account-Objects
|
243
|
+
|
244
|
+
If only one Account is present, Client and Advisor are identical.
|
245
|
+
|
246
|
+
=end
|
247
|
+
def clients
|
248
|
+
@accounts.find_all{ |x| x.is_a? IB::User } # IB::User and IB::DemoUser
|
249
|
+
end
|
250
|
+
=begin
|
251
|
+
The Advisor is always the first account. Anything works with single user accounts as well.
|
252
|
+
=end
|
253
|
+
def advisor
|
254
|
+
@accounts.first
|
255
|
+
end
|
256
|
+
|
257
|
+
=begin
|
258
|
+
account_data provides a thread-safe access to linked content of accounts
|
259
|
+
|
260
|
+
(AccountValues, Portfolio-Values, Contracts and Orders)
|
261
|
+
|
262
|
+
It returns an Array of the return-values of the block
|
263
|
+
|
264
|
+
If called without a parameter, all clients are accessed
|
265
|
+
=end
|
266
|
+
|
267
|
+
def account_data account_or_id=nil
|
268
|
+
|
269
|
+
safe = ->(account) do
|
270
|
+
@account_lock.synchronize do
|
271
|
+
yield account
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
if block_given?
|
276
|
+
if account_or_id.present?
|
277
|
+
sa = account_or_id.is_a?(IB::Account) ? account_or_id : @accounts.detect{|x| x.account == account_or_id }
|
278
|
+
safe[sa] if sa.is_a? IB::Account
|
279
|
+
else
|
280
|
+
clients.map{|sa| safe[sa]}
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def random_id
|
289
|
+
rand 99999
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
def prepare_connection &b
|
294
|
+
self.tws = IB::Connection.new @connection_parameter, &b
|
295
|
+
@accounts = @local_orders = Array.new
|
296
|
+
load_managed_accounts if @gateway_parameter[:s_m_a]
|
297
|
+
# prepare Advisor-User hierachy
|
298
|
+
initialize_alerts if @gateway_parameter[:s_a]
|
299
|
+
# initialize_order_handling if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
|
300
|
+
subscribe_account_updates # account-init.rb
|
301
|
+
|
302
|
+
## apply other initialisations which should apper before the connection as block
|
303
|
+
## i.e. after connection order-state events are fired if an open-order is pending
|
304
|
+
## a possible response is best defined before the connect-attempt is done
|
305
|
+
# ## Attention
|
306
|
+
# ## @accounts are not initialized yet (empty array)
|
307
|
+
# if block_given?
|
308
|
+
# yield self
|
309
|
+
|
310
|
+
# end
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
def initialize_alerts
|
315
|
+
|
316
|
+
tws.subscribe( :AccountUpdateTime ){| msg | logger.debug{ msg.to_human }}
|
317
|
+
tws.subscribe(:Alert) do |msg|
|
318
|
+
logger.progname = 'Gateway#Alerts'
|
319
|
+
logger.debug " ----------------#{msg.code}-----"
|
320
|
+
# delegate anything to IB::Alert
|
321
|
+
IB::Alert.send("alert_#{msg.code}", msg )
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
# Handy method to ensure that a connection is established and active.
|
327
|
+
#
|
328
|
+
# The connection is reset on the IB-side at least once a day. Then the
|
329
|
+
# IB-Ruby-Connection has to be reestablished, too.
|
330
|
+
#
|
331
|
+
# check_connection reconnects if necessary and returns false if the connection is lost.
|
332
|
+
#
|
333
|
+
# It delays the process by 6 ms (150 MBit Cable connection)
|
334
|
+
#
|
335
|
+
# a = Time.now; G.check_connection; b= Time.now ;b-a
|
336
|
+
# => 0.00066005
|
337
|
+
#
|
338
|
+
def check_connection
|
339
|
+
answer = nil; count=0
|
340
|
+
z= tws.subscribe( :CurrentTime ) { answer = true }
|
341
|
+
while (answer.nil?)
|
342
|
+
begin
|
343
|
+
tws.send_message(:RequestCurrentTime) # 10 ms ##
|
344
|
+
i=0; loop{ break if answer || i > 40; i+=1; sleep 0.0001}
|
345
|
+
rescue IOError, Errno::ECONNREFUSED # connection lost
|
346
|
+
count = 6
|
347
|
+
rescue IB::Error # not connected
|
348
|
+
reconnect
|
349
|
+
count +=1
|
350
|
+
sleep 1
|
351
|
+
retry if count <= 5
|
352
|
+
end
|
353
|
+
count +=1
|
354
|
+
break if count > 5
|
355
|
+
end
|
356
|
+
tws.unsubscribe z
|
357
|
+
count < 5 && answer # return value
|
358
|
+
end
|
359
|
+
end # class
|
360
|
+
|
361
|
+
end # module
|
362
|
+
|