ib-extensions 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +33 -34
- data/README.md +12 -7
- data/bin/console +3 -5
- data/bin/{gateway.rb → gateway} +13 -4
- data/changelog.md +6 -0
- data/ib-extensions.gemspec +1 -0
- data/lib/ib/alerts/base-alert.rb +3 -1
- data/lib/ib/alerts/order-alerts.rb +5 -0
- data/lib/ib/eod.rb +19 -23
- data/lib/ib/extensions.rb +1 -0
- data/lib/ib/extensions/version.rb +1 -1
- data/lib/ib/gateway.rb +77 -63
- data/lib/ib/gateway/account-infos.rb +1 -2
- data/lib/ib/gateway/order-handling.rb +121 -126
- data/lib/ib/market-price.rb +2 -2
- data/lib/ib/models/account.rb +63 -56
- data/lib/ib/models/option.rb +20 -0
- data/lib/ib/option-chain.rb +180 -182
- data/lib/ib/order-prototypes.rb +1 -1
- data/lib/ib/spread-prototypes.rb +2 -2
- data/lib/ib/spread_prototypes/stock-spread.rb +1 -1
- data/lib/ib/verify.rb +192 -222
- metadata +19 -18
- data/examples/cancel_orders +0 -74
- data/examples/eod +0 -35
- data/examples/input.rb +0 -475
- data/examples/market_price +0 -57
- data/examples/option_chain +0 -67
- data/examples/place_and_modify_order +0 -162
- data/examples/place_bracket_order +0 -62
- data/examples/place_butterfly_order +0 -104
- data/examples/place_combo_order +0 -70
- data/examples/place_limit_order +0 -82
- data/examples/place_the_limit_order +0 -145
- data/examples/volatility_research +0 -139
- data/examples/what_if_order +0 -90
- data/lib/ib/models/spread.rb +0 -159
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57f9f4c819b5b0af154107d63a7cb970b2ab9209ef849f88246c185d287d3202
|
4
|
+
data.tar.gz: c8572a97aa46fe3ef9331bb7a4247f33ef43f5b4584cbd6223770ac1adcd3ced
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 383509cac1aa159affe7e8c73b62d0ea60fe825987b0cd8efaa60d24b50cd9f4eb8f6085c4b9d9bc22d0a06923a774df0c59436eabcfa4563620da60ae964503
|
7
|
+
data.tar.gz: c615ef1b9948f1db60d868911f6f169df14425c42ef50e6e2dad8082f8e1fe8666d47626251e0ec3e7e1dc8e2b0e25bb01d233c4c45da8c2935cbf39cf08a21b
|
data/Gemfile
CHANGED
@@ -4,6 +4,7 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
gem "ib-api", path: "../ib-api/"
|
6
6
|
## move this to gemspec and include as development dependency after the gem is released
|
7
|
-
gem "ib-symbols"
|
7
|
+
gem "ib-symbols" #, path: "../ib-symbols/"
|
8
8
|
#gem "ib-api", :git=> 'https://github.com/ib-ruby/ib-api.git'
|
9
9
|
gem "rake", "~> 12.0"
|
10
|
+
gem 'dry-core'
|
data/Gemfile.lock
CHANGED
@@ -1,38 +1,34 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../ib-api
|
3
3
|
specs:
|
4
|
-
ib-api (972.
|
4
|
+
ib-api (972.4)
|
5
5
|
activemodel
|
6
6
|
activesupport (>= 6.0)
|
7
7
|
|
8
|
-
PATH
|
9
|
-
remote: ../ib-symbols
|
10
|
-
specs:
|
11
|
-
ib-symbols (1.0)
|
12
|
-
ib-api
|
13
|
-
ox
|
14
|
-
|
15
8
|
PATH
|
16
9
|
remote: .
|
17
10
|
specs:
|
18
|
-
ib-extensions (1.
|
11
|
+
ib-extensions (1.1)
|
12
|
+
ib-api (~> 972.4)
|
19
13
|
ox
|
20
14
|
|
21
15
|
GEM
|
22
16
|
remote: https://rubygems.org/
|
23
17
|
specs:
|
24
|
-
activemodel (6.
|
25
|
-
activesupport (= 6.
|
26
|
-
activesupport (6.
|
18
|
+
activemodel (6.1.3.2)
|
19
|
+
activesupport (= 6.1.3.2)
|
20
|
+
activesupport (6.1.3.2)
|
27
21
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
28
|
-
i18n (>=
|
29
|
-
minitest (
|
30
|
-
tzinfo (~>
|
31
|
-
zeitwerk (~> 2.
|
22
|
+
i18n (>= 1.6, < 2)
|
23
|
+
minitest (>= 5.1)
|
24
|
+
tzinfo (~> 2.0)
|
25
|
+
zeitwerk (~> 2.3)
|
32
26
|
coderay (1.1.3)
|
33
|
-
concurrent-ruby (1.1.
|
27
|
+
concurrent-ruby (1.1.8)
|
34
28
|
diff-lcs (1.4.4)
|
35
|
-
|
29
|
+
dry-core (0.5.0)
|
30
|
+
concurrent-ruby (~> 1.0)
|
31
|
+
ffi (1.15.0)
|
36
32
|
formatador (0.2.5)
|
37
33
|
guard (2.16.2)
|
38
34
|
formatador (>= 0.2.4)
|
@@ -48,20 +44,23 @@ GEM
|
|
48
44
|
guard (~> 2.1)
|
49
45
|
guard-compat (~> 1.1)
|
50
46
|
rspec (>= 2.99.0, < 4.0)
|
51
|
-
i18n (1.8.
|
47
|
+
i18n (1.8.10)
|
52
48
|
concurrent-ruby (~> 1.0)
|
53
|
-
|
49
|
+
ib-symbols (1.0)
|
50
|
+
ib-api
|
51
|
+
ox
|
52
|
+
listen (3.5.1)
|
54
53
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
55
54
|
rb-inotify (~> 0.9, >= 0.9.10)
|
56
55
|
lumberjack (1.2.8)
|
57
56
|
method_source (1.0.0)
|
58
|
-
minitest (5.14.
|
57
|
+
minitest (5.14.4)
|
59
58
|
nenv (0.3.0)
|
60
59
|
notiffany (0.1.3)
|
61
60
|
nenv (~> 0.1)
|
62
61
|
shellany (~> 0.0)
|
63
|
-
ox (2.
|
64
|
-
pry (0.
|
62
|
+
ox (2.14.4)
|
63
|
+
pry (0.14.1)
|
65
64
|
coderay (~> 1.1)
|
66
65
|
method_source (~> 1.0)
|
67
66
|
rake (12.3.3)
|
@@ -74,39 +73,39 @@ GEM
|
|
74
73
|
rspec-mocks (~> 3.10.0)
|
75
74
|
rspec-collection_matchers (1.2.0)
|
76
75
|
rspec-expectations (>= 2.99.0.beta1)
|
77
|
-
rspec-core (3.10.
|
76
|
+
rspec-core (3.10.1)
|
78
77
|
rspec-support (~> 3.10.0)
|
79
|
-
rspec-expectations (3.10.
|
78
|
+
rspec-expectations (3.10.1)
|
80
79
|
diff-lcs (>= 1.2.0, < 2.0)
|
81
80
|
rspec-support (~> 3.10.0)
|
82
81
|
rspec-its (1.3.0)
|
83
82
|
rspec-core (>= 3.0.0)
|
84
83
|
rspec-expectations (>= 3.0.0)
|
85
|
-
rspec-mocks (3.10.
|
84
|
+
rspec-mocks (3.10.2)
|
86
85
|
diff-lcs (>= 1.2.0, < 2.0)
|
87
86
|
rspec-support (~> 3.10.0)
|
88
|
-
rspec-support (3.10.
|
87
|
+
rspec-support (3.10.2)
|
89
88
|
shellany (0.0.1)
|
90
|
-
thor (1.0
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
zeitwerk (2.4.1)
|
89
|
+
thor (1.1.0)
|
90
|
+
tzinfo (2.0.4)
|
91
|
+
concurrent-ruby (~> 1.0)
|
92
|
+
zeitwerk (2.4.2)
|
95
93
|
|
96
94
|
PLATFORMS
|
97
95
|
ruby
|
98
96
|
|
99
97
|
DEPENDENCIES
|
100
98
|
bundler (~> 2.0)
|
99
|
+
dry-core
|
101
100
|
guard
|
102
101
|
guard-rspec
|
103
102
|
ib-api!
|
104
103
|
ib-extensions!
|
105
|
-
ib-symbols
|
104
|
+
ib-symbols
|
106
105
|
rake (~> 12.0)
|
107
106
|
rspec (~> 3.0)
|
108
107
|
rspec-collection_matchers
|
109
108
|
rspec-its
|
110
109
|
|
111
110
|
BUNDLED WITH
|
112
|
-
2.
|
111
|
+
2.2.3
|
data/README.md
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
# IB/Extensions
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
**status:** A stable Gem-Release is in preparation.
|
3
|
+
---
|
6
4
|
|
7
5
|
__Documentation: [https://ib-ruby.github.io/ib-doc/](https://ib-ruby.github.io/ib-doc/)__
|
8
6
|
|
9
|
-
|
7
|
+
__Questions, Contributions, Remarks: [Discussions are opened in ib-api](https://github.com/ib-ruby/ib-api/discussions)__
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
__Helpers and Macros that ease the usage of the TWS-API of Interactive Brokers__
|
12
|
+
|
13
|
+
To activate use
|
10
14
|
```
|
11
|
-
gem 'ib-extensions'
|
15
|
+
gem 'ib-extensions'
|
12
16
|
```
|
13
17
|
in the Gemfile and require the extensions as needed
|
14
18
|
|
15
19
|
## Include all
|
20
|
+
(except gateway)
|
16
21
|
```
|
17
22
|
require 'ib-api'
|
18
23
|
require 'ib/extensions'
|
@@ -91,9 +96,9 @@ Its used in [Simple Monitor](https://github.com/ib-ruby/simple-monitor)
|
|
91
96
|
|
92
97
|
## Contributing
|
93
98
|
|
94
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/ib-ruby/ib-extensions. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[
|
99
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ib-ruby/ib-extensions. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[ib-ruby/ib-extensions/blob/master/CODE_OF_CONDUCT.md).
|
95
100
|
|
96
101
|
|
97
102
|
## Code of Conduct
|
98
103
|
|
99
|
-
Everyone interacting in the Ib::Extensions project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
104
|
+
Everyone interacting in the Ib::Extensions project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ib-ruby/ib-extensions/blob/master/CODE_OF_CONDUCT.md).
|
data/bin/console
CHANGED
@@ -48,7 +48,6 @@ read_yml = -> (key) do
|
|
48
48
|
puts "Namespace is IB ! "
|
49
49
|
puts
|
50
50
|
puts '-'* 45
|
51
|
-
include LogDev
|
52
51
|
include IB
|
53
52
|
require 'irb'
|
54
53
|
client_id = ARGV[1] || read_yml[:client_id]
|
@@ -63,16 +62,15 @@ read_yml = -> (key) do
|
|
63
62
|
end
|
64
63
|
|
65
64
|
ARGV.clear
|
66
|
-
logger = default_logger # Logger.new STDOUT
|
67
65
|
|
68
66
|
## The Block takes instructions which are executed after initializing all instance-variables
|
69
67
|
## and prior to the connection-process
|
70
68
|
## Here we just subscribe to some events
|
71
69
|
C = Connection.new client_id: client_id, port: port do |c| # future use__ , optional_capacities: "+PACEAPI" do |c|
|
72
70
|
|
73
|
-
c.subscribe( :ContractData, :BondContractData) { |msg| logger.info { msg.contract.to_human } }
|
74
|
-
c.subscribe( :Alert, :ContractDataEnd, :ManagedAccounts, :OrderStatus ) {| m| logger.info { m.to_human } }
|
75
|
-
c.subscribe( :PortfolioValue, :AccountValue, :OrderStatus, :OpenOrderEnd, :ExecutionData ) {| m| logger.info { m.to_human }}
|
71
|
+
c.subscribe( :ContractData, :BondContractData) { |msg| c.logger.info { msg.contract.to_human } }
|
72
|
+
c.subscribe( :Alert, :ContractDataEnd, :ManagedAccounts, :OrderStatus ) {| m| c.logger.info { m.to_human } }
|
73
|
+
c.subscribe( :PortfolioValue, :AccountValue, :OrderStatus, :OpenOrderEnd, :ExecutionData ) {| m| c.logger.info { m.to_human }}
|
76
74
|
# c.subscribe :ManagedAccounts do |msg|
|
77
75
|
# puts "------------------------------- Managed Accounts ----------------------------------"
|
78
76
|
# puts "Detected Accounts: #{msg.accounts.account.join(' -- ')} "
|
data/bin/{gateway.rb → gateway}
RENAMED
@@ -12,6 +12,7 @@ LogLevel = Logger::DEBUG ##INFO # DEBUG # ERROR
|
|
12
12
|
#require File.expand_path(File.dirname(__FILE__) + "/../config/boot")
|
13
13
|
|
14
14
|
require 'ib-gateway'
|
15
|
+
require 'ib/verify'
|
15
16
|
require 'ib/market-price'
|
16
17
|
require 'ib/option-chain'
|
17
18
|
require 'ib/eod'
|
@@ -48,7 +49,6 @@ read_yml = -> (key) do
|
|
48
49
|
puts
|
49
50
|
puts "Namespace is IB ! "
|
50
51
|
puts
|
51
|
-
puts '-'* 45
|
52
52
|
|
53
53
|
include IB
|
54
54
|
require 'irb'
|
@@ -74,17 +74,21 @@ read_yml = -> (key) do
|
|
74
74
|
## and prior to the connection-process
|
75
75
|
## Here we just subscribe to some events
|
76
76
|
begin
|
77
|
-
G = Gateway.new
|
78
|
-
client_id: client_id, port: port,
|
79
|
-
watchlists: [:Spreads, :BuyAndHold]
|
77
|
+
G = Gateway.new get_account_data: true, serial_array: true,
|
78
|
+
client_id: client_id, port: port , logger: logger,
|
79
|
+
watchlists: [:Spreads, :Stillhalter, :BuyAndHold]
|
80
80
|
rescue IB::TransmissionError => e
|
81
81
|
puts "E: #{e.inspect}"
|
82
82
|
end
|
83
83
|
|
84
84
|
C = G.tws
|
85
|
+
|
86
|
+
C.subscribe(:TickGeneric, :TickOption, :TickRequestParameters, :MarketDataType, :TickString, :TickSize ){|x| x }
|
85
87
|
unless C.received[:OpenOrder].blank?
|
86
88
|
puts "------------------------------- OpenOrders ----------------------------------"
|
87
89
|
puts C.received[:OpenOrder].to_human.join "\n"
|
90
|
+
puts " ---------------- Open Orders are present in G.clients.orders --------------"
|
91
|
+
puts ""
|
88
92
|
end
|
89
93
|
puts "Connection established on Port #{port}, client_id #{client_id} used"
|
90
94
|
puts
|
@@ -94,4 +98,9 @@ read_yml = -> (key) do
|
|
94
98
|
puts "some basic Messages are subscribed and accordingly displayed"
|
95
99
|
puts '-'* 45
|
96
100
|
|
101
|
+
begin
|
97
102
|
IRB.start(__FILE__)
|
103
|
+
#rescue IB::OrderAttributeError => e
|
104
|
+
# puts "OrderAtttribute ERROR"
|
105
|
+
end
|
106
|
+
|
data/changelog.md
CHANGED
@@ -27,5 +27,11 @@
|
|
27
27
|
* included specific alert-definitions in this directory:
|
28
28
|
order-alerts, gateway-alerts
|
29
29
|
|
30
|
+
* moving model/spread.rb to `ib-api` Gem
|
30
31
|
|
32
|
+
#### Preparation of a Gem-Release
|
31
33
|
|
34
|
+
* Gateway#connect: initializing order-array by calling :RequestAllOrders after establishing the connection.
|
35
|
+
|
36
|
+
* Gateway#connect: Occasionally the request for AccountPositions/AllOrders fails. Then a reconnect is
|
37
|
+
appropriate. This is now implemented.
|
data/ib-extensions.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
|
28
28
|
|
29
29
|
spec.add_dependency "ox"
|
30
|
+
spec.add_dependency "ib-api", "~> 972.4"
|
30
31
|
spec.add_development_dependency "bundler", "~> 2.0"
|
31
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
32
33
|
spec.add_development_dependency 'rspec-collection_matchers'
|
data/lib/ib/alerts/base-alert.rb
CHANGED
@@ -40,7 +40,6 @@ Important: The class is accessed asynchronically. Be careful while raising inter
|
|
40
40
|
# acts as prototype for any generated method
|
41
41
|
#require 'active_support'
|
42
42
|
|
43
|
-
mattr_accessor :logger
|
44
43
|
|
45
44
|
def self.method_missing( method_id, msg , *args, &block )
|
46
45
|
if msg.is_a? IB::Messages::Incoming::Alert
|
@@ -62,6 +61,9 @@ Important: The class is accessed asynchronically. Be careful while raising inter
|
|
62
61
|
|
63
62
|
|
64
63
|
class << self
|
64
|
+
def logger
|
65
|
+
IB::Connection.logger || IB::Gateway.logger
|
66
|
+
end
|
65
67
|
|
66
68
|
def ignore_alert *codes
|
67
69
|
codes.each do |n|
|
@@ -1,6 +1,11 @@
|
|
1
1
|
module IB
|
2
2
|
class Alert
|
3
3
|
|
4
|
+
def self.alert_388 msg
|
5
|
+
# Order size x is smaller than the minimum required size of yy.
|
6
|
+
IB::Gateway.logger.error msg.inspect
|
7
|
+
# error msg, :order, nil
|
8
|
+
end
|
4
9
|
def self.alert_202 msg
|
5
10
|
# do anything in a secure mutex-synchronized-environment
|
6
11
|
any_order = IB::Gateway.current.account_data do | account |
|
data/lib/ib/eod.rb
CHANGED
@@ -51,21 +51,21 @@ require 'active_support/core_ext/date/calculations'
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
class Contract
|
54
|
-
# Receive EOD-Data
|
55
|
-
#
|
54
|
+
# Receive EOD-Data
|
55
|
+
#
|
56
56
|
# The Enddate has to be specified (as Date Object), t
|
57
57
|
#
|
58
|
-
# The Duration can either be specified as Sting " yx D" or as Integer.
|
58
|
+
# The Duration can either be specified as Sting " yx D" or as Integer.
|
59
59
|
# Altenative a start date can be specified with the :start parameter.
|
60
60
|
#
|
61
|
-
# The parameter :what specified the kind of received data:
|
61
|
+
# The parameter :what specified the kind of received data:
|
62
62
|
# Valid values:
|
63
63
|
# :trades, :midpoint, :bid, :ask, :bid_ask,
|
64
64
|
# :historical_volatility, :option_implied_volatility,
|
65
65
|
# :option_volume, :option_open_interest
|
66
|
-
#
|
66
|
+
#
|
67
67
|
# The results can be preprocessed through a block, thus
|
68
|
-
#
|
68
|
+
#
|
69
69
|
# puts IB::Symbols::Index::stoxx.eod( duration: '10 d')){|r| r.to_human}
|
70
70
|
# <Bar: 2019-04-01 wap 0.0 OHLC 3353.67 3390.98 3353.67 3385.38 trades 1750 vol 0>
|
71
71
|
# <Bar: 2019-04-02 wap 0.0 OHLC 3386.18 3402.77 3382.84 3395.7 trades 1729 vol 0>
|
@@ -79,24 +79,24 @@ require 'active_support/core_ext/date/calculations'
|
|
79
79
|
# <Bar: 2019-04-12 wap 0.0 OHLC 3432.16 3454.77 3425.84 3447.83 trades 1715 vol 0>
|
80
80
|
#
|
81
81
|
# «to_human« is not needed here because ist aliased with `to_s`
|
82
|
-
#
|
83
|
-
# puts Symbols::Stocks.wfc.eod( start: Date.new(2019,10,9), duration: 3 )
|
82
|
+
#
|
83
|
+
# puts Symbols::Stocks.wfc.eod( start: Date.new(2019,10,9), duration: 3 )
|
84
84
|
# <Bar: 2020-10-23 wap 23.3675 OHLC 23.55 23.55 23.12 23.28 trades 5778 vol 50096>
|
85
85
|
# <Bar: 2020-10-26 wap 22.7445 OHLC 22.98 22.99 22.6 22.7 trades 6873 vol 79560>
|
86
86
|
# <Bar: 2020-10-27 wap 22.086 OHLC 22.55 22.58 21.82 21.82 trades 7503 vol 97691>
|
87
87
|
|
88
|
-
# puts Symbols::Stocks.wfc.eod( to: Date.new(2019,10,9), duration: 3 )
|
88
|
+
# puts Symbols::Stocks.wfc.eod( to: Date.new(2019,10,9), duration: 3 )
|
89
89
|
# <Bar: 2019-10-04 wap 48.964 OHLC 48.61 49.25 48.54 49.21 trades 9899 vol 50561>
|
90
90
|
# <Bar: 2019-10-07 wap 48.9445 OHLC 48.91 49.29 48.75 48.81 trades 10317 vol 50189>
|
91
91
|
# <Bar: 2019-10-08 wap 47.9165 OHLC 48.25 48.34 47.55 47.82 trades 12607 vol 53577>
|
92
92
|
#
|
93
|
-
def eod start:nil, to: Date.today, duration: nil , what: :trades
|
93
|
+
def eod start:nil, to: Date.today, duration: nil , what: :trades
|
94
94
|
|
95
95
|
tws = IB::Connection.current
|
96
96
|
recieved = Queue.new
|
97
97
|
r = nil
|
98
|
-
# the hole response is transmitted at once!
|
99
|
-
a= tws.subscribe(IB::Messages::Incoming::HistoricalData) do |msg|
|
98
|
+
# the hole response is transmitted at once!
|
99
|
+
a = tws.subscribe(IB::Messages::Incoming::HistoricalData) do |msg|
|
100
100
|
if msg.request_id == con_id
|
101
101
|
# msg.results.each { |entry| puts " #{entry}" }
|
102
102
|
r = block_given? ? msg.results.map{|y| yield y} : msg.results
|
@@ -108,11 +108,10 @@ require 'active_support/core_ext/date/calculations'
|
|
108
108
|
tws.logger.info msg.message
|
109
109
|
# TWS Error 200: No security definition has been found for the request
|
110
110
|
# TWS Error 354: Requested market data is not subscribed.
|
111
|
-
# TWS Error 162 # Historical Market Data Service error
|
112
|
-
|
111
|
+
# TWS Error 162 # Historical Market Data Service error
|
112
|
+
recieved.close
|
113
113
|
end
|
114
114
|
end
|
115
|
-
|
116
115
|
|
117
116
|
duration = if duration.present?
|
118
117
|
duration.is_a?(String) ? duration : duration.to_s + " D"
|
@@ -133,19 +132,16 @@ require 'active_support/core_ext/date/calculations'
|
|
133
132
|
:format_date => 2,
|
134
133
|
:keep_up_todate => 0)
|
135
134
|
|
136
|
-
Timeout::timeout(
|
137
|
-
sleep 0.1
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
break if recieved.empty? # finish if no more data received
|
142
|
-
end
|
135
|
+
Timeout::timeout(5) do # max 5 sec.
|
136
|
+
sleep 0.1
|
137
|
+
recieved.pop # blocks until a message is ready on the queue
|
138
|
+
break if recieved.closed? || recieved.empty? # finish if data received
|
139
|
+
end
|
143
140
|
tws.unsubscribe a
|
144
141
|
tws.unsubscribe b
|
145
142
|
|
146
143
|
r # the collected result
|
147
144
|
|
148
|
-
end
|
149
145
|
end # def
|
150
146
|
end # class
|
151
147
|
end # module
|
data/lib/ib/extensions.rb
CHANGED
data/lib/ib/gateway.rb
CHANGED
@@ -75,47 +75,46 @@ IB::Gateway.new serial_array: true (, ...)
|
|
75
75
|
|
76
76
|
=end
|
77
77
|
|
78
|
-
|
78
|
+
class Gateway
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
include Support::Logging # provides default_logger
|
81
|
+
include AccountInfos # provides Handling of Account-Data provided by the tws
|
82
|
+
include OrderHandling
|
83
83
|
|
84
|
-
|
84
|
+
# include GWSupport # introduces update_or_create, first_or_create and intercept to the Array-Class
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
mattr_accessor :tws
|
86
|
+
# from active-support. Add Logging at Class + Instance-Level
|
87
|
+
# similar to the Connection-Class: current represents the active instance of Gateway
|
88
|
+
mattr_accessor :current
|
89
|
+
mattr_accessor :tws
|
91
90
|
|
92
91
|
|
93
92
|
|
94
|
-
def initialize port: 4002, # 7497,
|
93
|
+
def initialize port: 4002, # 7497,
|
95
94
|
host: '127.0.0.1', # 'localhost:4001' is also accepted
|
96
95
|
client_id: random_id,
|
97
|
-
subscribe_managed_accounts: true,
|
98
|
-
subscribe_alerts: true,
|
99
|
-
subscribe_order_messages: true,
|
100
|
-
connect: true,
|
96
|
+
subscribe_managed_accounts: true,
|
97
|
+
subscribe_alerts: true,
|
98
|
+
subscribe_order_messages: true,
|
99
|
+
connect: true,
|
101
100
|
get_account_data: false,
|
102
|
-
serial_array: false,
|
103
|
-
|
101
|
+
serial_array: false,
|
102
|
+
logger: nil,
|
104
103
|
watchlists: [] , # array of watchlists (IB::Symbols::{watchlist}) containing descriptions for complex positions
|
105
104
|
**other_agruments_which_are_ignored,
|
106
105
|
&b
|
107
106
|
|
108
|
-
host, port = (host+':'+port.to_s).split(':')
|
107
|
+
host, port = (host+':'+port.to_s).split(':')
|
109
108
|
|
110
|
-
|
111
|
-
|
112
|
-
|
109
|
+
self.class.configure_logger logger
|
110
|
+
|
111
|
+
self.logger.info { '-' * 20 +' initialize ' + '-' * 20 }
|
113
112
|
|
114
113
|
@connection_parameter = { received: serial_array, port: port, host: host, connect: false, logger: logger, client_id: client_id }
|
115
114
|
|
116
115
|
@account_lock = Mutex.new
|
117
116
|
@watchlists = watchlists
|
118
|
-
@gateway_parameter = { s_m_a: subscribe_managed_accounts,
|
117
|
+
@gateway_parameter = { s_m_a: subscribe_managed_accounts,
|
119
118
|
s_a: subscribe_alerts,
|
120
119
|
s_o_m: subscribe_order_messages,
|
121
120
|
g_a_d: get_account_data }
|
@@ -124,18 +123,31 @@ IB::Gateway.new serial_array: true (, ...)
|
|
124
123
|
Thread.report_on_exception = true
|
125
124
|
# https://blog.bigbinary.com/2018/04/18/ruby-2-5-enables-thread-report_on_exception-by-default.html
|
126
125
|
Gateway.current = self
|
127
|
-
# establish Alert-framework
|
128
|
-
IB::Alert.logger = logger
|
129
126
|
# initialise Connection without connecting
|
130
127
|
prepare_connection &b
|
131
128
|
# finally connect to the tws
|
132
129
|
connect = true if get_account_data
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
130
|
+
|
131
|
+
if connect
|
132
|
+
i = 0
|
133
|
+
begin
|
134
|
+
i+=1
|
135
|
+
if connect(100) # tries to connect for about 2h
|
136
|
+
get_account_data(watchlists: watchlists.map{|b| IB::Symbols.allocate_collection b}) if get_account_data
|
137
|
+
# request_open_orders() if request_open_orders || get_account_data
|
138
|
+
else
|
139
|
+
@accounts = [] # definitivley reset @accounts
|
140
|
+
end
|
141
|
+
rescue IB::Error => e
|
142
|
+
disconnect
|
143
|
+
logger.fatal e.message
|
144
|
+
if e.message =~ /NextLocalId is not initialized/
|
145
|
+
Kernel.exit
|
146
|
+
elsif i < 5
|
147
|
+
retry
|
148
|
+
else
|
149
|
+
raise "could not get account data"
|
150
|
+
end
|
139
151
|
end
|
140
152
|
end
|
141
153
|
|
@@ -157,9 +169,9 @@ IB::Gateway.new serial_array: true (, ...)
|
|
157
169
|
|
158
170
|
## ------------------------------------- connect ---------------------------------------------##
|
159
171
|
=begin
|
160
|
-
Zentrale Methode
|
172
|
+
Zentrale Methode
|
161
173
|
Es wird ein Connection-Objekt (IB::Connection.current) angelegt.
|
162
|
-
Sollte keine TWS vorhanden sein, wird
|
174
|
+
Sollte keine TWS vorhanden sein, wird ein entsprechende Meldung ausgegeben und der Verbindungsversuch
|
163
175
|
wiederholt.
|
164
176
|
Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
165
177
|
|
@@ -167,7 +179,6 @@ Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
|
167
179
|
def connect maximal_count_of_retry=100
|
168
180
|
|
169
181
|
i= -1
|
170
|
-
logger.progname = 'Gateway#connect'
|
171
182
|
begin
|
172
183
|
tws.connect
|
173
184
|
rescue Errno::ECONNREFUSED => e
|
@@ -203,8 +214,10 @@ Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
|
203
214
|
end
|
204
215
|
end
|
205
216
|
# initialize @accounts (incl. aliases)
|
206
|
-
tws.send_message :RequestFA, fa_data_type: 3
|
217
|
+
tws.send_message( :RequestFA, fa_data_type: 3) if fa?
|
207
218
|
logger.debug { "Communications successfully established" }
|
219
|
+
# update open orders
|
220
|
+
request_open_orders if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
|
208
221
|
end # def
|
209
222
|
|
210
223
|
|
@@ -212,7 +225,6 @@ Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
|
212
225
|
|
213
226
|
|
214
227
|
def reconnect
|
215
|
-
logger.progname = 'Gateway#reconnect'
|
216
228
|
if tws.present?
|
217
229
|
disconnect
|
218
230
|
sleep 1
|
@@ -222,11 +234,10 @@ Weiterhin meldet sich die Anwendung zur Auswertung von Messages der TWS an.
|
|
222
234
|
end
|
223
235
|
|
224
236
|
def disconnect
|
225
|
-
logger.progname = 'Gateway#disconnect'
|
226
237
|
|
227
238
|
tws.disconnect if tws.present?
|
228
239
|
@accounts = [] # each{|y| y.update_attribute :connected, false }
|
229
|
-
logger.info "Connection closed"
|
240
|
+
logger.info "Connection closed"
|
230
241
|
end
|
231
242
|
|
232
243
|
|
@@ -239,7 +250,6 @@ checks the connection before sending a message.
|
|
239
250
|
=end
|
240
251
|
|
241
252
|
def send_message what, *args
|
242
|
-
logger.tap{|l| l.progname = 'Gateway#SendMessage' }
|
243
253
|
begin
|
244
254
|
if check_connection
|
245
255
|
tws.send_message what, *args
|
@@ -256,9 +266,8 @@ Argument is either an order-object or a local_id
|
|
256
266
|
|
257
267
|
=end
|
258
268
|
|
259
|
-
def cancel_order *orders
|
269
|
+
def cancel_order *orders
|
260
270
|
|
261
|
-
logger.tap{|l| l.progname = 'Gateway#CancelOrder' }
|
262
271
|
|
263
272
|
orders.compact.each do |o|
|
264
273
|
local_id = if o.is_a? (IB::Order)
|
@@ -273,14 +282,21 @@ Argument is either an order-object or a local_id
|
|
273
282
|
end
|
274
283
|
|
275
284
|
=begin
|
276
|
-
clients returns a list of Account-Objects
|
285
|
+
clients returns a list of Account-Objects
|
277
286
|
|
278
287
|
If only one Account is present, Client and Advisor are identical.
|
279
288
|
|
280
289
|
=end
|
281
290
|
def clients
|
282
|
-
@accounts.find_all &:user?
|
291
|
+
@accounts.find_all &:user?
|
283
292
|
end
|
293
|
+
|
294
|
+
# is the account a financial advisor
|
295
|
+
def fa?
|
296
|
+
!(advisor == clients.first)
|
297
|
+
end
|
298
|
+
|
299
|
+
|
284
300
|
=begin
|
285
301
|
The Advisor is always the first account
|
286
302
|
=end
|
@@ -311,44 +327,38 @@ If called without a parameter, all clients are accessed
|
|
311
327
|
sa = account_or_id.is_a?(IB::Account) ? account_or_id : @accounts.detect{|x| x.account == account_or_id }
|
312
328
|
safe[sa] if sa.is_a? IB::Account
|
313
329
|
else
|
314
|
-
clients.map{|
|
330
|
+
clients.map{|s| safe[s]}
|
315
331
|
end
|
316
332
|
end
|
317
333
|
end
|
318
334
|
|
319
335
|
|
320
|
-
private
|
321
|
-
|
322
|
-
def random_id
|
323
|
-
rand 99999
|
324
|
-
end
|
325
|
-
|
326
336
|
|
327
337
|
|
328
338
|
def prepare_connection &b
|
329
339
|
tws.disconnect if tws.is_a? IB::Connection
|
330
|
-
|
340
|
+
self.tws = IB::Connection.new **@connection_parameter.merge( logger: self.logger )
|
331
341
|
@accounts = @local_orders = Array.new
|
332
342
|
|
333
343
|
# prepare Advisor-User hierachy
|
334
344
|
initialize_managed_accounts if @gateway_parameter[:s_m_a]
|
335
345
|
initialize_alerts if @gateway_parameter[:s_a]
|
336
|
-
|
346
|
+
initialize_order_handling if @gateway_parameter[:s_o_m] || @gateway_parameter[:g_a_d]
|
337
347
|
## apply other initialisations which should apper before the connection as block
|
338
348
|
## i.e. after connection order-state events are fired if an open-order is pending
|
339
349
|
## a possible response is best defined before the connect-attempt is done
|
340
350
|
# ## Attention
|
341
351
|
# ## @accounts are not initialized yet (empty array)
|
342
|
-
if block_given?
|
352
|
+
if block_given?
|
343
353
|
yield self
|
344
354
|
|
345
355
|
end
|
346
356
|
end
|
347
357
|
|
348
358
|
=begin
|
349
|
-
InitializeManagedAccounts
|
359
|
+
InitializeManagedAccounts
|
350
360
|
defines the Message-Handler for :ManagedAccounts
|
351
|
-
Its always active.
|
361
|
+
Its always active.
|
352
362
|
=end
|
353
363
|
|
354
364
|
def initialize_managed_accounts
|
@@ -360,7 +370,6 @@ Its always active.
|
|
360
370
|
end
|
361
371
|
|
362
372
|
man_id = tws.subscribe( :ManagedAccounts ) do |msg|
|
363
|
-
logger.progname = 'Gateway#InitializeManagedAccounts'
|
364
373
|
if @accounts.empty?
|
365
374
|
# just validate the message and put all together into an array
|
366
375
|
@accounts = msg.accounts_list.split(',').map do |a|
|
@@ -377,8 +386,7 @@ Its always active.
|
|
377
386
|
def initialize_alerts
|
378
387
|
|
379
388
|
tws.subscribe( :AccountUpdateTime ){| msg | logger.debug{ msg.to_human }}
|
380
|
-
tws.subscribe(:Alert) do |msg|
|
381
|
-
logger.progname = 'Gateway#Alerts'
|
389
|
+
tws.subscribe(:Alert) do |msg|
|
382
390
|
logger.debug " ----------------#{msg.code}-----"
|
383
391
|
# delegate anything to IB::Alert
|
384
392
|
IB::Alert.send("alert_#{msg.code}", msg )
|
@@ -388,16 +396,16 @@ Its always active.
|
|
388
396
|
|
389
397
|
# Handy method to ensure that a connection is established and active.
|
390
398
|
#
|
391
|
-
# The connection is reset on the IB-side at least once a day. Then the
|
392
|
-
# IB-Ruby-Connection has to be reestablished, too.
|
393
|
-
#
|
394
|
-
# check_connection reconnects if necessary and returns false if the connection is lost.
|
395
|
-
#
|
399
|
+
# The connection is reset on the IB-side at least once a day. Then the
|
400
|
+
# IB-Ruby-Connection has to be reestablished, too.
|
401
|
+
#
|
402
|
+
# check_connection reconnects if necessary and returns false if the connection is lost.
|
403
|
+
#
|
396
404
|
# It delays the process by 6 ms (150 MBit Cable connection)
|
397
405
|
#
|
398
406
|
# a = Time.now; G.check_connection; b= Time.now ;b-a
|
399
407
|
# => 0.00066005
|
400
|
-
#
|
408
|
+
#
|
401
409
|
def check_connection
|
402
410
|
answer = nil; count=0
|
403
411
|
z= tws.subscribe( :CurrentTime ) { answer = true }
|
@@ -419,6 +427,12 @@ Its always active.
|
|
419
427
|
tws.unsubscribe z
|
420
428
|
count < 5 && answer # return value
|
421
429
|
end
|
430
|
+
private
|
431
|
+
|
432
|
+
def random_id
|
433
|
+
rand 99999
|
434
|
+
end
|
435
|
+
|
422
436
|
end # class
|
423
437
|
|
424
438
|
end # module
|