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
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
|