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
data/bin/gateway
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
### loads the active-orient environment
|
3
|
+
### and starts an interactive shell
|
4
|
+
###
|
5
|
+
### Parameter: t)ws | g)ateway (or number of port ) Default: Gateway ,
|
6
|
+
### client_id , Default 2000
|
7
|
+
###
|
8
|
+
### Define Parameter in file console.yml
|
9
|
+
###
|
10
|
+
require 'bundler/setup'
|
11
|
+
require 'yaml'
|
12
|
+
require 'ib-orientdb'
|
13
|
+
require 'ib/orient-gateway'
|
14
|
+
require 'logger'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# read items from console.yml
|
20
|
+
read_yml = -> (key) do
|
21
|
+
YAML::load_file( File.expand_path('../../connect.yml',__FILE__))[key]
|
22
|
+
end
|
23
|
+
project_root = File.expand_path('../..', __FILE__)
|
24
|
+
model_dir = project_root + '/lib/models'
|
25
|
+
|
26
|
+
puts
|
27
|
+
puts ">> IB-OrientDB Interactive Console <<"
|
28
|
+
puts '-'* 45
|
29
|
+
puts
|
30
|
+
puts " ... preparing environment"
|
31
|
+
include LogDev
|
32
|
+
include IB
|
33
|
+
require 'irb'
|
34
|
+
|
35
|
+
environment = ARGV[0] || 'Development'
|
36
|
+
environment = case environment
|
37
|
+
when /^[pP]/
|
38
|
+
:production
|
39
|
+
when /^[dD]/
|
40
|
+
:development
|
41
|
+
when /^[tT]/
|
42
|
+
:test
|
43
|
+
end
|
44
|
+
tws = read_yml[:tws][environment]
|
45
|
+
orientdb = read_yml[:orientdb][environment]
|
46
|
+
|
47
|
+
ARGV.clear
|
48
|
+
logger = default_logger # Logger.new STDOUT
|
49
|
+
|
50
|
+
## The Block takes instructions which are executed after initializing all instance-variables
|
51
|
+
## and prior to the connection-process
|
52
|
+
## Here we just subscribe to some events
|
53
|
+
|
54
|
+
module TG; end
|
55
|
+
ActiveOrient::Model.keep_models_without_file = false
|
56
|
+
ActiveOrient::Model.model_dir = model_dir
|
57
|
+
IB::Setup.connect( tws: tws, orientdb: orientdb, kind: :gateway) do |c|
|
58
|
+
module HC; end
|
59
|
+
ActiveOrient::Init.define_namespace { HC }
|
60
|
+
|
61
|
+
ActiveOrient::OrientDB.new# model_dir: model_dir
|
62
|
+
|
63
|
+
puts '-'* 83
|
64
|
+
print '-'* 35
|
65
|
+
print " TWS Connect "
|
66
|
+
puts '-'* 35
|
67
|
+
c.subscribe( :ContractData, :BondContractData) { |msg| logger.info { msg.contract.to_human } }
|
68
|
+
c.subscribe( :Alert, :ContractDataEnd, :ManagedAccounts, :OrderStatus ) {| m| logger.info { m.to_human } }
|
69
|
+
c.subscribe( :PortfolioValue, :AccountValue, :OrderStatus, :OpenOrderEnd, :ExecutionData ) {| m| logger.info { m.to_human }}
|
70
|
+
|
71
|
+
c.logger.level = Logger::WARN # INFO
|
72
|
+
end
|
73
|
+
|
74
|
+
C = IB::Connection.current
|
75
|
+
G = IB::OrientGateway.current
|
76
|
+
puts ActiveOrient::show_classes
|
77
|
+
# G.logger.level = 3
|
78
|
+
# G.get_account_data
|
79
|
+
|
80
|
+
|
81
|
+
puts ">> IB-OrientDB Interactive Console is ready -- using #{environment.to_s.upcase} environment <<"
|
82
|
+
puts '-'* 45
|
83
|
+
puts
|
84
|
+
puts "Namespace is IB ! "
|
85
|
+
puts
|
86
|
+
puts "----> C points to the Connection-Instance"
|
87
|
+
puts "----> G points to the Gateway-Instance"
|
88
|
+
puts
|
89
|
+
puts "some basic Messages are subscribed and accordingly displayed"
|
90
|
+
puts
|
91
|
+
puts '-'* 45
|
92
|
+
IRB.start(__FILE__)
|
data/bin/readme.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
../spec/readme.md
|
data/changelog.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
* Setup OrientDB-Database: lib/init_db.rb
|
4
|
+
* Test spec/lib/init_db_spec.rb
|
5
|
+
* Creating Setup.connect and integrate in console & spec_helper
|
6
|
+
* Migrating Setup.connect to /lib/setup.rb
|
7
|
+
|
8
|
+
|
9
|
+
* TG::Tag#portfolios : returns nil if no entry is detected (instead of [])
|
10
|
+
|
data/ib-orientdb.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'lib/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "ib-orientdb"
|
5
|
+
spec.version = IB::OrientDB::VERSION
|
6
|
+
spec.authors = ["Hartmut Bischoff"]
|
7
|
+
spec.email = ["topofocus@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{Part of IB-Ruby. Methods do store data from the TWS in a OrientDB-Database.}
|
10
|
+
spec.homepage = "https://ib-ruby.github.io/ib-doc/"
|
11
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
12
|
+
|
13
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.cm/ib-ruby/ib-orientdb"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.cm/ib-ruby/ib-orientdb/changelog.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
spec.add_development_dependency 'rspec-collection_matchers'
|
32
|
+
spec.add_development_dependency 'rspec-its'
|
33
|
+
|
34
|
+
spec.add_development_dependency "guard"
|
35
|
+
spec.add_development_dependency "guard-rspec"
|
36
|
+
|
37
|
+
|
38
|
+
spec.add_dependency "ib-api", "~>972.1"
|
39
|
+
spec.add_dependency "ib-extensions"
|
40
|
+
spec.add_dependency "active-orient", "~> 0.8"
|
41
|
+
spec.add_dependency 'orientdb-time-graph'
|
42
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module IB
|
2
|
+
class Alert
|
3
|
+
=begin
|
4
|
+
The Singleton IB::Alert handles any response to IB:Messages::Incomming:Alert
|
5
|
+
|
6
|
+
Individual methods can be defined as well as methods responding to a group of error-codes.
|
7
|
+
The default-behavior is defined in the method_missing-method. This just logs the object at the debug level.
|
8
|
+
|
9
|
+
To use the IB::Alert facility, first a logger has to be assigned to the Class.
|
10
|
+
IB::Alert.logger = Logger.new(STDOUT)
|
11
|
+
|
12
|
+
Default-wrappers to completely ignore the error-message (ignore_alert)
|
13
|
+
and to log the object in a different log-level (log_alert_in [warn,info,error] ) are defined in base_alert
|
14
|
+
Just add
|
15
|
+
module IB
|
16
|
+
class Alert
|
17
|
+
log_alert_in_warn {list of codennumbers}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
to your code
|
21
|
+
|
22
|
+
|
23
|
+
IB::Gateway calls the methods in response of subscribing to the :Alert signal by calling
|
24
|
+
IB::Alert.send("alert_#{msg.code}", msg )
|
25
|
+
|
26
|
+
To define a response to the code 134 ( Modify order failed) a method like
|
27
|
+
module IB
|
28
|
+
class Alert
|
29
|
+
def self.alert_134 msg
|
30
|
+
(your code)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
has to be written.
|
35
|
+
|
36
|
+
Important: The class is accessed asynchronically. Be careful while raising interrupts.
|
37
|
+
|
38
|
+
=end
|
39
|
+
|
40
|
+
# acts as prototype for any generated method
|
41
|
+
#require 'active_support'
|
42
|
+
|
43
|
+
def self.logger
|
44
|
+
IB::Connection.logger
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.method_missing( method_id =nil, msg = nil , *args, &block )
|
48
|
+
# puts "METHOD MISSING"
|
49
|
+
# puts msg
|
50
|
+
if msg.is_a? IB::Messages::Incoming::Alert
|
51
|
+
logger.debug { msg.to_human }
|
52
|
+
else
|
53
|
+
logger.error { "Argument to IB::Alert is not a IB::Messages::Incoming::Alert" }
|
54
|
+
logger.error { "The object: #{msg.inspect} " }
|
55
|
+
end
|
56
|
+
rescue NoMethodError
|
57
|
+
unless logger.nil?
|
58
|
+
logger.error { "The Argument is not a valid IB::Messages:Incoming::Alert object"}
|
59
|
+
logger.error { "The object: #{msg.inspect} " }
|
60
|
+
else
|
61
|
+
puts "No Logging-Device specified"
|
62
|
+
puts "The object: #{msg.inspect} "
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
class << self
|
69
|
+
|
70
|
+
def ignore_alert *codes
|
71
|
+
codes.each do |n|
|
72
|
+
class_eval <<-EOD
|
73
|
+
def self.alert_#{n} msg
|
74
|
+
# even the log_debug entry is suppressed
|
75
|
+
end
|
76
|
+
EOD
|
77
|
+
end
|
78
|
+
end
|
79
|
+
def log_alert_in_debug *codes
|
80
|
+
codes.each do |n|
|
81
|
+
class_eval <<-EOD
|
82
|
+
def self.alert_#{n} msg
|
83
|
+
logger.debug { msg.to_human }
|
84
|
+
end
|
85
|
+
EOD
|
86
|
+
end
|
87
|
+
end
|
88
|
+
def log_alert_in_info *codes
|
89
|
+
codes.each do |n|
|
90
|
+
class_eval <<-EOD
|
91
|
+
def self.alert_#{n} msg
|
92
|
+
logger.info { msg.to_human }
|
93
|
+
end
|
94
|
+
EOD
|
95
|
+
end
|
96
|
+
end
|
97
|
+
def log_alert_in_warn *codes
|
98
|
+
codes.each do |n|
|
99
|
+
class_eval <<-EOD
|
100
|
+
def self.alert_#{n} msg
|
101
|
+
logger.warn { msg.to_human }
|
102
|
+
end
|
103
|
+
EOD
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def log_alert_in_error *codes
|
108
|
+
codes.each do |n|
|
109
|
+
class_eval <<-EOD
|
110
|
+
def self.alert_#{n} msg
|
111
|
+
if msg.error_id.present? && msg.error_id > 0
|
112
|
+
logger.error { msg.message + ' id: ' + msg.error_id.to_s }
|
113
|
+
else
|
114
|
+
logger.error { msg.message }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
EOD
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
ignore_alert 200 , # is handled by IB::Contract.update_contract
|
123
|
+
2100, # API client has been unsubscribed from account data
|
124
|
+
2104, #
|
125
|
+
2105,
|
126
|
+
2106,
|
127
|
+
2158,
|
128
|
+
399, # your order will not be placed at the exchange until
|
129
|
+
321 # Error validating request.-'ce': cause - FA data operations ignored for non FA customers
|
130
|
+
|
131
|
+
log_alert_in_info 1102, #Connectivity between IB and Trader Workstation has been restored
|
132
|
+
2103 #Market data farm connection is broken
|
133
|
+
|
134
|
+
log_alert_in_error 320, 323, 324, #ServerError
|
135
|
+
## 110, # The price does not conform to the minimum price variation
|
136
|
+
# 103, #duplicate order ## order-alerts
|
137
|
+
# 201, #deleted objecta ## order-alerts
|
138
|
+
326 #Unable connect as the client id is already in use
|
139
|
+
|
140
|
+
log_alert_in_warn 354, #Requested market data is not subscribed
|
141
|
+
1100 #Connectivity between IB and Trader Workstation has been lost.
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# These Alerts are always active
|
2
|
+
module IB
|
3
|
+
class Alert
|
4
|
+
|
5
|
+
def self.alert_2102 msg
|
6
|
+
# Connectivity between IB and Trader Workstation has been restored - data maintained.
|
7
|
+
sleep 0.1 # no need to wait too long.
|
8
|
+
if IB::OrientGateway.current.check_connection
|
9
|
+
IB::Connection.logger.debug { "Alert 2102: Connection stable" }
|
10
|
+
else
|
11
|
+
IB::OrientGateway.current.reconnect
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module IB
|
2
|
+
class Alert
|
3
|
+
|
4
|
+
def self.alert_202 msg
|
5
|
+
# do anything in a secure mutex-synchronized-environment
|
6
|
+
any_order = IB::Gateway.current.account_data do | account |
|
7
|
+
order= account.locate_order( local_id: msg.error_id )
|
8
|
+
if order.present? && ( order.order_state.status != 'Cancelled' )
|
9
|
+
order.order_states.update_or_create( IB::OrderState.new( status: 'Cancelled',
|
10
|
+
perm_id: order.perm_id,
|
11
|
+
local_id: order.local_id ) ,
|
12
|
+
:status )
|
13
|
+
|
14
|
+
end
|
15
|
+
order # return_value
|
16
|
+
end
|
17
|
+
if any_order.compact.empty?
|
18
|
+
IB::Gateway.logger.error{"Alert 202: The deleted order was not registered: local_id #{msg.error_id}"}
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class << self
|
25
|
+
=begin
|
26
|
+
IB::Alert#AddOrderstateAlert
|
27
|
+
|
28
|
+
The OrderState-Record is used to record the history of the order.
|
29
|
+
If selected Alert-Messages appear, they are added to the Order.order_state-Array.
|
30
|
+
The last Status is available as Order.order_state, all states are accessible by Order.order_states
|
31
|
+
|
32
|
+
The TWS-Message-text is stored to the »warning-text«-field.
|
33
|
+
The Status is always »rejected«.
|
34
|
+
If the first OrderState-object of a Order is »rejected«, the order is not placed at all.
|
35
|
+
Otherwise only the last action is not applied and the order is unchanged.
|
36
|
+
|
37
|
+
=end
|
38
|
+
def add_orderstate_alert *codes
|
39
|
+
codes.each do |n|
|
40
|
+
class_eval <<-EOD
|
41
|
+
def self.alert_#{n} msg
|
42
|
+
|
43
|
+
if msg.error_id.present?
|
44
|
+
IB::Gateway.current.account_data do | account |
|
45
|
+
order= account.locate_order( local_id: msg.error_id )
|
46
|
+
if order.present? && ( order.order_state.status != 'Rejected' )
|
47
|
+
order.order_states.update_or_create( IB::OrderState.new( status: 'Rejected' ,
|
48
|
+
perm_id: order.perm_id,
|
49
|
+
warning_text: '#{n}: '+ msg.message,
|
50
|
+
local_id: msg.error_id ), :status )
|
51
|
+
|
52
|
+
IB::Gateway.logger.error{ msg.to_human }
|
53
|
+
end # order present?
|
54
|
+
end # mutex-environment
|
55
|
+
end # branch
|
56
|
+
end # def
|
57
|
+
EOD
|
58
|
+
end # loop
|
59
|
+
end # def
|
60
|
+
end
|
61
|
+
add_orderstate_alert 103, # duplicate order
|
62
|
+
201, # deleted object
|
63
|
+
105, # Order being modified does not match original order
|
64
|
+
462, # Cannot change to the new Time in Force:GTD
|
65
|
+
329, # Cannot change to the new order type:STP
|
66
|
+
10147 # OrderId 0 that needs to be cancelled is not found.
|
67
|
+
end # class Alert
|
68
|
+
end # module IB
|
data/lib/ib-orientdb.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'ib/alerts/base-alert'
|
2
|
+
require 'ib/models/account'
|
3
|
+
|
4
|
+
module IB
|
5
|
+
class Alert
|
6
|
+
class << self
|
7
|
+
def alert_2101 msg
|
8
|
+
logger.error {msg.message}
|
9
|
+
@status_2101 = msg.dup
|
10
|
+
end
|
11
|
+
|
12
|
+
def status_2101 account # resets status and raises IB::TransmissionError
|
13
|
+
error account.account + ": " +@status_2101.message, :reader unless @status_2101.nil?
|
14
|
+
@status_2101 = nil # always returns nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end # module
|
19
|
+
|
20
|
+
module AccountInfos
|
21
|
+
|
22
|
+
=begin
|
23
|
+
Queries the tws for Account- and PortfolioValues
|
24
|
+
The parameter can either be the account_id, the IB::Account-Object or
|
25
|
+
an Array of account_id and IB::Account-Objects.
|
26
|
+
|
27
|
+
raises an IB::TransmissionError if the account-data are not transmitted in time (1 sec)
|
28
|
+
|
29
|
+
raises an IB::Error if less then 100 items are recieved-
|
30
|
+
=end
|
31
|
+
def get_account_data *accounts, watchlists: []
|
32
|
+
|
33
|
+
logger.progname = 'Gateway#get_account_data'
|
34
|
+
|
35
|
+
@account_data_subscription ||= subscribe_account_updates
|
36
|
+
|
37
|
+
accounts = clients if accounts.empty?
|
38
|
+
logger.warn{ "No active account present. AccountData are NOT requested" } if accounts.empty?
|
39
|
+
# Account-infos have to be requested sequencially.
|
40
|
+
# subsequent (parallel) calls kill the former once on the tws-server-side
|
41
|
+
# In addition, there is no need to cancel the subscription of an request, as a new
|
42
|
+
# one overwrites the active one.
|
43
|
+
accounts.each do | ac |
|
44
|
+
account = ac.is_a?( IB::Account ) ? ac : clients.find{|x| x.account == ac }
|
45
|
+
error( "No Account detected " ) unless account.is_a? IB::Account
|
46
|
+
# don't repeat the query until 170 sec. have passed since the previous update
|
47
|
+
if account.last_updated.nil? || ( Time.now - account.last_updated ) > 170 # sec
|
48
|
+
logger.debug{ "#{account.account} :: Requesting AccountData " }
|
49
|
+
account.update_attribute :connected, false # indicates: AccountUpdate in Progress
|
50
|
+
# reset account and portfolio-values
|
51
|
+
account.portfolio_values = []
|
52
|
+
account.account_values = []
|
53
|
+
send_message :RequestAccountData, subscribe: true, account_code: account.account
|
54
|
+
Timeout::timeout(3, IB::TransmissionError, "RequestAccountData failed (#{account.account})") do
|
55
|
+
# initialize requests sequencially
|
56
|
+
loop{ sleep 0.1; break if account.connected }
|
57
|
+
end
|
58
|
+
if watchlists.present?
|
59
|
+
watchlists.each{|w| error "Watchlists must be IB::Symbols--Classes :.#{w.inspect}" unless w.is_a? IB::Symbols }
|
60
|
+
account.organize_portfolio_positions watchlists
|
61
|
+
end
|
62
|
+
send_message :RequestAccountData, subscribe: false ## do this only once
|
63
|
+
else
|
64
|
+
logger.info{ "#{account.account} :: Using stored AccountData " }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def all_contracts
|
71
|
+
clients.map(&:contracts).flat_map(&:itself).uniq(&:con_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# The subscription method should called only once per session.
|
78
|
+
# It places subscribers to AccountValue and PortfolioValue Messages, which should remain
|
79
|
+
# active through its session.
|
80
|
+
#
|
81
|
+
|
82
|
+
def subscribe_account_updates continously: true
|
83
|
+
tws.subscribe( :AccountValue, :PortfolioValue,:AccountDownloadEnd ) do | msg |
|
84
|
+
account_data( msg.account_name ) do | account | # enter mutex controlled zone
|
85
|
+
case msg
|
86
|
+
when IB::Messages::Incoming::AccountValue
|
87
|
+
account.account_values << msg.account_value
|
88
|
+
account.update_attribute :last_updated, Time.now
|
89
|
+
logger.debug { "#{account.account} :: #{msg.account_value.to_human }"}
|
90
|
+
when IB::Messages::Incoming::AccountDownloadEnd
|
91
|
+
if account.account_values.size > 10
|
92
|
+
# simply don't cancel the subscripton if continously is specified
|
93
|
+
# the connected flag is set in any case, indicating that valid data are present
|
94
|
+
send_message :RequestAccountData, subscribe: false, account_code: account.account unless continously
|
95
|
+
account.update_attribute :connected, true ## flag: Account is completely initialized
|
96
|
+
logger.info { "#{account.account} => Count of AccountValues: #{account.account_values.size}" }
|
97
|
+
else # unreasonable account_data recieved - request is still active
|
98
|
+
error "#{account.account} => Count of AccountValues too small: #{account.account_values.size}" , :reader
|
99
|
+
end
|
100
|
+
when IB::Messages::Incoming::PortfolioValue
|
101
|
+
account.contracts.update_or_create msg.contract
|
102
|
+
account.portfolio_values << msg.portfolio_value
|
103
|
+
# msg.portfolio_value.account = account
|
104
|
+
# link contract -> portfolio value
|
105
|
+
# account.contracts.find{ |x| x.con_id == msg.contract.con_id }
|
106
|
+
# .portfolio_values
|
107
|
+
# .update_or_create( msg.portfolio_value ) { :account }
|
108
|
+
logger.debug { "#{ account.account } :: #{ msg.contract.to_human }" }
|
109
|
+
end # case
|
110
|
+
end # account_data
|
111
|
+
end # subscribe
|
112
|
+
end # def
|
113
|
+
|
114
|
+
|
115
|
+
end # module
|