ib-orientdb 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|