ib-extensions 1.0 → 1.1
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 +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
|