ib-api 10.33.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.
Files changed (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CLAUDE.md +131 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +17 -0
  8. data/Gemfile.lock +120 -0
  9. data/Guardfile +24 -0
  10. data/LICENSE +674 -0
  11. data/LLM_GUIDE.md +388 -0
  12. data/README.md +114 -0
  13. data/Rakefile +11 -0
  14. data/VERSION +1 -0
  15. data/api.gemspec +50 -0
  16. data/bin/console +96 -0
  17. data/bin/console.yml +3 -0
  18. data/bin/setup +8 -0
  19. data/bin/simple +91 -0
  20. data/changelog.md +32 -0
  21. data/conditions/ib/execution_condition.rb +31 -0
  22. data/conditions/ib/margin_condition.rb +28 -0
  23. data/conditions/ib/order_condition.rb +29 -0
  24. data/conditions/ib/percent_change_condition.rb +34 -0
  25. data/conditions/ib/price_condition.rb +44 -0
  26. data/conditions/ib/time_condition.rb +42 -0
  27. data/conditions/ib/volume_condition.rb +36 -0
  28. data/lib/class_extensions.rb +167 -0
  29. data/lib/ib/base.rb +109 -0
  30. data/lib/ib/base_properties.rb +178 -0
  31. data/lib/ib/connection.rb +573 -0
  32. data/lib/ib/constants.rb +402 -0
  33. data/lib/ib/contract.rb +30 -0
  34. data/lib/ib/errors.rb +52 -0
  35. data/lib/ib/messages/abstract_message.rb +68 -0
  36. data/lib/ib/messages/incoming/abstract_message.rb +116 -0
  37. data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
  38. data/lib/ib/messages/incoming/account_message.rb +26 -0
  39. data/lib/ib/messages/incoming/alert.rb +34 -0
  40. data/lib/ib/messages/incoming/contract_data.rb +105 -0
  41. data/lib/ib/messages/incoming/contract_message.rb +13 -0
  42. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  43. data/lib/ib/messages/incoming/execution_data.rb +50 -0
  44. data/lib/ib/messages/incoming/histogram_data.rb +30 -0
  45. data/lib/ib/messages/incoming/historical_data.rb +65 -0
  46. data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
  47. data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
  48. data/lib/ib/messages/incoming/market_depth.rb +34 -0
  49. data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
  50. data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
  51. data/lib/ib/messages/incoming/open_order.rb +290 -0
  52. data/lib/ib/messages/incoming/order_status.rb +85 -0
  53. data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
  54. data/lib/ib/messages/incoming/position_data.rb +21 -0
  55. data/lib/ib/messages/incoming/positions_multi.rb +15 -0
  56. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  57. data/lib/ib/messages/incoming/receive_fa.rb +30 -0
  58. data/lib/ib/messages/incoming/scanner_data.rb +54 -0
  59. data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
  60. data/lib/ib/messages/incoming/tick_efp.rb +18 -0
  61. data/lib/ib/messages/incoming/tick_generic.rb +12 -0
  62. data/lib/ib/messages/incoming/tick_option.rb +60 -0
  63. data/lib/ib/messages/incoming/tick_price.rb +60 -0
  64. data/lib/ib/messages/incoming/tick_size.rb +55 -0
  65. data/lib/ib/messages/incoming/tick_string.rb +13 -0
  66. data/lib/ib/messages/incoming.rb +292 -0
  67. data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
  68. data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
  69. data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
  70. data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
  71. data/lib/ib/messages/outgoing/place_order.rb +149 -0
  72. data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
  73. data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
  74. data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
  75. data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
  76. data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
  77. data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
  78. data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
  79. data/lib/ib/messages/outgoing.rb +410 -0
  80. data/lib/ib/messages.rb +139 -0
  81. data/lib/ib/order_condition.rb +26 -0
  82. data/lib/ib/plugins.rb +27 -0
  83. data/lib/ib/prepare_data.rb +61 -0
  84. data/lib/ib/raw_message_parser.rb +99 -0
  85. data/lib/ib/socket.rb +83 -0
  86. data/lib/ib/support.rb +236 -0
  87. data/lib/ib/version.rb +6 -0
  88. data/lib/ib-api.rb +44 -0
  89. data/lib/server_versions.rb +145 -0
  90. data/lib/support/array_function.rb +28 -0
  91. data/lib/support/logging.rb +45 -0
  92. data/models/ib/account.rb +72 -0
  93. data/models/ib/account_value.rb +33 -0
  94. data/models/ib/bag.rb +55 -0
  95. data/models/ib/bar.rb +31 -0
  96. data/models/ib/combo_leg.rb +127 -0
  97. data/models/ib/contract.rb +411 -0
  98. data/models/ib/contract_detail.rb +118 -0
  99. data/models/ib/execution.rb +67 -0
  100. data/models/ib/forex.rb +12 -0
  101. data/models/ib/future.rb +64 -0
  102. data/models/ib/index.rb +14 -0
  103. data/models/ib/option.rb +149 -0
  104. data/models/ib/option_detail.rb +84 -0
  105. data/models/ib/order.rb +720 -0
  106. data/models/ib/order_state.rb +155 -0
  107. data/models/ib/portfolio_value.rb +86 -0
  108. data/models/ib/spread.rb +176 -0
  109. data/models/ib/stock.rb +25 -0
  110. data/models/ib/underlying.rb +32 -0
  111. data/plugins/ib/advanced-account.rb +442 -0
  112. data/plugins/ib/alerts/base-alert.rb +125 -0
  113. data/plugins/ib/alerts/gateway-alerts.rb +15 -0
  114. data/plugins/ib/alerts/order-alerts.rb +73 -0
  115. data/plugins/ib/auto-adjust.rb +0 -0
  116. data/plugins/ib/connection-tools.rb +122 -0
  117. data/plugins/ib/eod.rb +326 -0
  118. data/plugins/ib/greeks.rb +102 -0
  119. data/plugins/ib/managed-accounts.rb +274 -0
  120. data/plugins/ib/market-price.rb +150 -0
  121. data/plugins/ib/option-chain.rb +167 -0
  122. data/plugins/ib/order-flow.rb +157 -0
  123. data/plugins/ib/order-prototypes/abstract.rb +67 -0
  124. data/plugins/ib/order-prototypes/adaptive.rb +40 -0
  125. data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
  126. data/plugins/ib/order-prototypes/combo.rb +46 -0
  127. data/plugins/ib/order-prototypes/forex.rb +40 -0
  128. data/plugins/ib/order-prototypes/limit.rb +193 -0
  129. data/plugins/ib/order-prototypes/market.rb +116 -0
  130. data/plugins/ib/order-prototypes/pegged.rb +169 -0
  131. data/plugins/ib/order-prototypes/premarket.rb +31 -0
  132. data/plugins/ib/order-prototypes/stop.rb +202 -0
  133. data/plugins/ib/order-prototypes/volatility.rb +39 -0
  134. data/plugins/ib/order-prototypes.rb +118 -0
  135. data/plugins/ib/probability-of-expiring.rb +109 -0
  136. data/plugins/ib/process-orders.rb +155 -0
  137. data/plugins/ib/roll.rb +86 -0
  138. data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
  139. data/plugins/ib/spread-prototypes/calendar.rb +97 -0
  140. data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
  141. data/plugins/ib/spread-prototypes/straddle.rb +70 -0
  142. data/plugins/ib/spread-prototypes/strangle.rb +93 -0
  143. data/plugins/ib/spread-prototypes/vertical.rb +83 -0
  144. data/plugins/ib/spread-prototypes.rb +70 -0
  145. data/plugins/ib/symbols/abstract.rb +136 -0
  146. data/plugins/ib/symbols/bonds.rb +28 -0
  147. data/plugins/ib/symbols/cfd.rb +19 -0
  148. data/plugins/ib/symbols/combo.rb +46 -0
  149. data/plugins/ib/symbols/commodity.rb +17 -0
  150. data/plugins/ib/symbols/forex.rb +41 -0
  151. data/plugins/ib/symbols/futures.rb +127 -0
  152. data/plugins/ib/symbols/index.rb +43 -0
  153. data/plugins/ib/symbols/options.rb +99 -0
  154. data/plugins/ib/symbols/stocks.rb +44 -0
  155. data/plugins/ib/symbols/version.rb +5 -0
  156. data/plugins/ib/symbols.rb +118 -0
  157. data/plugins/ib/verify.rb +226 -0
  158. data/symbols/w20.yml +210 -0
  159. data/t.txt +20 -0
  160. data/update.md +71 -0
  161. metadata +327 -0
data/bin/simple ADDED
@@ -0,0 +1,91 @@
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
+
13
+ require 'ib-api'
14
+
15
+ class Array
16
+ # enables calling members of an array. which are hashes by it name
17
+ # i.e
18
+ #
19
+ # 2.5.0 :006 > C.received[:OpenOrder].local_id
20
+ # => [16, 17, 21, 20, 19, 8, 7]
21
+ # 2.5.0 :007 > C.received[:OpenOrder].contract.to_human
22
+ # => ["<Bag: IECombo SMART USD legs: >", "<Stock: GE USD>", "<Stock: GE USD>", "<Stock: GE USD>", "<Stock: GE USD>", "<Stock: WFC USD>", "<Stock: WFC USD>"]
23
+ #
24
+ # its included only in the console, for inspection purposes
25
+
26
+ def method_missing(method, *key)
27
+ unless method == :to_hash || method == :to_str #|| method == :to_int
28
+ return self.map{|x| x.public_send(method, *key)}
29
+ end
30
+
31
+ end
32
+ end # Array
33
+
34
+
35
+ # read items from console.yml
36
+ read_yml = -> (key) do
37
+ YAML::load_file( File.expand_path('../console.yml',__FILE__))[key]
38
+ end
39
+
40
+
41
+ puts
42
+ puts ">> IB-Core Interactive Console <<"
43
+ puts '-'* 45
44
+ puts
45
+ puts "Namespace is IB ! "
46
+ puts
47
+ puts '-'* 45
48
+ include IB
49
+ require 'irb'
50
+ client_id = ARGV[1] || read_yml[:client_id]
51
+ specified_host = ARGV[0] || 'Gateway'
52
+ host = case specified_host
53
+ when Integer
54
+ specified_port # just use the number
55
+ when /^[gG]/
56
+ read_yml[:gateway]
57
+ when /^[Tt]/
58
+ read_yml[:tws]
59
+ end
60
+
61
+ ARGV.clear
62
+
63
+ ## The Block takes instructions which are executed after initializing all instance-variables
64
+ ## and prior to the connection-process
65
+ ## Here we just subscribe to some events
66
+ C = Connection.new client_id: client_id, host: host do |c| # future use__ , optional_capacities: "+PACEAPI" do |c|
67
+ c.received = true
68
+ c.subscribe( :ContractData, :BondContractData) { |msg| c.logger.info { msg.contract.to_human } }
69
+ c.subscribe( :Alert, :ContractDataEnd, :ManagedAccounts, :OrderStatus ) {| m| c.logger.info { m.to_human } }
70
+ c.subscribe( :PortfolioValue, :AccountValue, :OrderStatus, :OpenOrderEnd, :ExecutionData ) {| m| c.logger.info { m.to_human }}
71
+ # c.subscribe :ManagedAccounts do |msg|
72
+ # puts "------------------------------- Managed Accounts ----------------------------------"
73
+ # puts "Detected Accounts: #{msg.accounts.account.join(' -- ')} "
74
+ # puts
75
+ # end
76
+
77
+ c.subscribe( :OpenOrder){ |msg| "Open Order detected and stored: C.received[:OpenOrders] " }
78
+ end
79
+ #C.logger.level = Logger::FATAL
80
+ unless C.received[:OpenOrder].blank?
81
+ puts "------------------------------- OpenOrders ----------------------------------"
82
+ puts C.received[:OpenOrder].to_human.join "\n"
83
+ end
84
+ puts "Connection established through #{host}, client_id #{client_id} used"
85
+ puts
86
+ puts "----> C points to the connection-instance"
87
+ puts
88
+ puts "some basic Messages are subscribed and accordingly displayed"
89
+ puts '-'* 45
90
+
91
+ IRB.start(__FILE__)
data/changelog.md ADDED
@@ -0,0 +1,32 @@
1
+ Changelog
2
+ =============
3
+
4
+ | Date | Description |
5
+ |=++++ |=++++++++++++ |
6
+ | 30.8.2020 | migrating lib-files from ib-ruby-project |
7
+
8
+ | 28.11.2020| separating lib/model and lib/models to enable extension with
9
+ ActiveRecord/Rails and OrientDB/ActiveOrient. |
10
+ | | Introducing a Database-Switch in /lib/requires to omit
11
+ loading of model- and messages files. This has to be done
12
+ manually after assigning the database-model framework. |
13
+
14
+ | 1.12.2020 | moving model/ib/spread.rb from `ib-extensions` to `ib-api`.|
15
+ | 1.12.2020 | creating a dummy Contract#verify to guaranty safe operation of spreads |
16
+
17
+ | | Preparation of a Gem-Release |
18
+ | 23.2.2021 | Fixed retrieving of ContractDetail requests of Options with strikes < 1
19
+ | | Gem Release |
20
+
21
+ | 1.4.2024 | Proper monkey patching of classes through class_extensions (Prepare for Zeitwerk, V10)
22
+ | 2.4.2024 | Renaming of IBSupport and IBSocket to IB::Support and IB::Socket (Prepare for Zeitwerk, V10)
23
+ | 4.4.2024 | Apply Zeitwerk, V10
24
+ Put `model` to the root directory (the files are then easily fetched through zeitwerk)
25
+ Reorganizing Messages. One message class per file. Keeping general incoming and outgoing-files
26
+ | 1.1.2025 | introducing plugins
27
+ | | using a state machine to organize access to advanced featurs
28
+
29
+ | 7.5.2025 | Disabled verify buffer size to enable receiving historical datastreams
30
+ | | Removed contract#verify! in favour of #contract.verify
31
+
32
+
@@ -0,0 +1,31 @@
1
+ module IB
2
+ class ExecutionCondition < OrderCondition
3
+ using IB::Support # refine Array-method for decoding of IB-Messages
4
+
5
+ def condition_type
6
+ 5
7
+ end
8
+
9
+ def self.make buffer
10
+ m =self.new conjunction_connection: buffer.read_string,
11
+ operator: buffer.read_int
12
+
13
+ the_contract = IB::Contract.new sec_type: buffer.read_string,
14
+ exchange: buffer.read_string,
15
+ symbol: buffer.read_string
16
+ m.contract = the_contract
17
+ m
18
+ end
19
+
20
+ def serialize
21
+ super << contract[:sec_type] <<(contract.primary_exchange.presence || contract.exchange) << contract.symbol
22
+ end
23
+
24
+ def self.fabricate contract
25
+ self.new contract: verify_contract_if_necessary( contract )
26
+ end
27
+
28
+ end
29
+
30
+
31
+ end # module
@@ -0,0 +1,28 @@
1
+ module IB
2
+
3
+ class MarginCondition < OrderCondition
4
+ using IB::Support # refine Array-method for decoding of IB-Messages
5
+
6
+ prop :percent
7
+
8
+ def condition_type
9
+ 4
10
+ end
11
+
12
+ def self.make buffer
13
+ self.new conjunction_connection: buffer.read_string,
14
+ operator: buffer.read_int,
15
+ percent: buffer.read_int
16
+ end
17
+
18
+ def serialize
19
+ super << self[:operator] << percent
20
+ end
21
+ def self.fabricate operator, percent
22
+ error "Condition Operator has to be \">=\" or \"<=\" " unless ["<=", ">="].include? operator
23
+ self.new operator: operator,
24
+ percent: percent
25
+ end
26
+ end
27
+
28
+ end # module
@@ -0,0 +1,29 @@
1
+ module IB
2
+ class OrderCondition < IB::Base
3
+ include BaseProperties
4
+ using IB::Support # refine Array-method for decoding of IB-Messages
5
+
6
+ prop :operator, # 1 -> " >= " , 0 -> " <= " see /lib/ib/constants # 338f
7
+ :conjunction_connection, # "o" -> or "a"
8
+ :contract
9
+
10
+ def self.verify_contract_if_necessary c
11
+ c.con_id.to_i.zero? ||( c.primary_exchange.blank? && c.exchange.blank?) ? c.verify! : c
12
+ end
13
+ def condition_type
14
+ error "condition_type method is abstract"
15
+ end
16
+ def default_attributes
17
+ super.merge( operator: ">=" , conjunction_connection: :and )
18
+ end
19
+
20
+ def serialize_contract_by_con_id
21
+ [ contract.con_id , contract.primary_exchange.presence || contract.exchange ]
22
+ end
23
+
24
+ def serialize
25
+ [ condition_type, self[:conjunction_connection] ]
26
+ end
27
+ end
28
+
29
+ end # module
@@ -0,0 +1,34 @@
1
+ module IB
2
+
3
+ class PercentChangeCondition < OrderCondition
4
+ using IB::Support # refine Array-method for decoding of IB-Messages
5
+ prop :percent_change
6
+ include BaseProperties
7
+
8
+ def condition_type
9
+ 7
10
+ end
11
+
12
+ def self.make buffer
13
+ m = self.new conjunction_connection: buffer.read_string,
14
+ operator: buffer.read_int,
15
+ percent_change: buffer.read_decimal
16
+
17
+ the_contract = IB::Contract.new con_id: buffer.read_int, exchange: buffer.read_string
18
+ m.contract = the_contract
19
+ m
20
+ end
21
+
22
+ def serialize
23
+ super << self[:operator] << percent_change << serialize_contract_by_con_id
24
+
25
+ end
26
+ # dsl: PercentChangeCondition.fabricate some_contract, ">=", "5%"
27
+ def self.fabricate contract, operator, change
28
+ error "Condition Operator has to be \">=\" or \"<=\" " unless ["<=", ">="].include? operator
29
+ self.new operator: operator,
30
+ percent_change: change.to_i,
31
+ contract: verify_contract_if_necessary( contract )
32
+ end
33
+ end
34
+ end # module
@@ -0,0 +1,44 @@
1
+ module IB
2
+
3
+
4
+ class PriceCondition < OrderCondition
5
+ using IB::Support # refine Array-method for decoding of IB-Messages
6
+ include BaseProperties
7
+ prop :price,
8
+ :trigger_method # see /models/ib/order.rb# 51 ff and /lib/ib/constants # 210 ff
9
+
10
+ def default_attributes
11
+ super.merge( :trigger_method => :default )
12
+ end
13
+
14
+ def condition_type
15
+ 1
16
+ end
17
+
18
+ def self.make buffer
19
+ m= self.new conjunction_connection: buffer.read_string,
20
+ operator: buffer.read_int,
21
+ price: buffer.read_decimal
22
+
23
+ the_contract = IB::Contract.new con_id: buffer.read_int, exchange: buffer.read_string
24
+ m.contract = the_contract
25
+ m.trigger_method = buffer.read_int
26
+ m
27
+
28
+ end
29
+
30
+ def serialize
31
+ super << self[:operator] << price << serialize_contract_by_con_id << self[:trigger_method]
32
+ end
33
+
34
+ # dsl: PriceCondition.fabricate some_contract, ">=", 500
35
+ def self.fabricate contract, operator, price
36
+ error "Condition Operator has to be \">=\" or \"<=\" " unless ["<=", ">="].include? operator
37
+ self.new operator: operator,
38
+ price: price.to_i,
39
+ contract: verify_contract_if_necessary( contract )
40
+ end
41
+
42
+ end
43
+
44
+ end # module
@@ -0,0 +1,42 @@
1
+ module IB
2
+
3
+
4
+ class TimeCondition < OrderCondition
5
+ using IB::Support # refine Array-method for decoding of IB-Messages
6
+ include BaseProperties
7
+ prop :time
8
+
9
+ def condition_type
10
+ 3
11
+ end
12
+
13
+ def self.make buffer
14
+ self.new conjunction_connection: buffer.read_string,
15
+ operator: buffer.read_int,
16
+ time: buffer.read_parse_date
17
+ end
18
+
19
+ def serialize
20
+ t = self[:time]
21
+ if t.is_a?(String) && t =~ /^\d{8}\z/ # expiry-format yyymmmdd
22
+ self.time = DateTime.new t[0..3],t[4..5],t[-2..-1]
23
+ end
24
+ serialized_time = case self[:time] # explicity formatting of time-object
25
+ when String
26
+ self[:time]
27
+ when DateTime
28
+ self[:time].gmtime.strftime("%Y%m%d %H:%M:%S %Z")
29
+ when Date, Time
30
+ self[:time].strftime("%Y%m%d %H:%M:%S")
31
+ end
32
+
33
+ super << self[:operator] << serialized_time
34
+ end
35
+
36
+ def self.fabricate operator, time
37
+ self.new operator: operator,
38
+ time: time
39
+ end
40
+ end
41
+
42
+ end # module
@@ -0,0 +1,36 @@
1
+ module IB
2
+ class VolumeCondition < OrderCondition
3
+ using IB::Support # refine Array-method for decoding of IB-Messages
4
+ include BaseProperties
5
+
6
+ prop :volume
7
+
8
+ def condition_type
9
+ 6
10
+ end
11
+
12
+ def self.make buffer
13
+ m = self.new conjunction_connection: buffer.read_string,
14
+ operator: buffer.read_int,
15
+ volumne: buffer.read_int
16
+
17
+ the_contract = IB::Contract.new con_id: buffer.read_int, exchange: buffer.read_string
18
+ m.contract = the_contract
19
+ m
20
+ end
21
+
22
+ def serialize
23
+
24
+ super << self[:operator] << volume << serialize_contract_by.con_id
25
+ end
26
+
27
+ # dsl: VolumeCondition.fabricate some_contract, ">=", 50000
28
+ def self.fabricate contract, operator, volume
29
+ error "Condition Operator has to be \">=\" or \"<=\" " unless ["<=", ">="].include? operator
30
+ self.new operator: operator,
31
+ volume: volume,
32
+ contract: verify_contract_if_necessary( contract )
33
+ end
34
+ end
35
+
36
+ end # module
@@ -0,0 +1,167 @@
1
+ # Include the method `to_bool` to some basic classes
2
+ #
3
+ # Prepare the output of arrays via Terminal::Table
4
+ #
5
+ # Define the method `count_duplicates` for Arrays
6
+ #
7
+ require 'date'
8
+
9
+ module ClassExtensions
10
+ module Array
11
+ module DuplicatesCounter
12
+ def count_duplicates
13
+ self.each_with_object(Hash.new(0)) { |element, counter| counter[element] += 1 }.sort_by{|k,v| -v}.to_h
14
+ end
15
+ end
16
+
17
+ module TablePresenter
18
+ def as_table(&b)
19
+ the_table_header = first.table_header(&b)
20
+ the_table_rows = map &:table_row
21
+ Terminal::Table.new headings: the_table_header, rows: the_table_rows , style: { border: :unicode }
22
+ end
23
+ end
24
+ end
25
+
26
+ module Date
27
+ # Render datetime in IB format (zero padded "yyyymmdd 12:00:00")
28
+
29
+ def to_ib timezone = 'UTC'
30
+ t = to_time + 12 * 60 * 60 # convert to time (noon)
31
+ s= "#{t.year}#{sprintf("%02d", t.month)}#{sprintf("%02d", t.day)}"
32
+
33
+ if timezone == 'UTC'
34
+ s + "-#{sprintf("%02d", t.hour)}:#{sprintf("%02d", t.min)}:#{sprintf("%02d", t.sec)}"
35
+ else
36
+ s + " #{sprintf("%02d", t.hour)}:#{sprintf("%02d", t.min)}:#{sprintf("%02d", t.sec)} #{timezone}"
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ module Time
43
+ # Render datetime in IB format (zero padded "yyyymmdd HH:mm:ss")
44
+ # Without specifying the timezone utc is used
45
+
46
+ def to_ib timezone = 'UTC'
47
+ s= "#{year}#{sprintf("%02d", month)}#{sprintf("%02d", day)}"
48
+ if timezone == 'UTC'
49
+ unless utc?
50
+ self.clone.utc.to_ib
51
+ else
52
+ s + "-#{sprintf("%02d", hour)}:#{sprintf("%02d", min)}:#{sprintf("%02d", sec)}"
53
+ end
54
+ else
55
+ s + " #{sprintf("%02d", hour)}:#{sprintf("%02d", min)}:#{sprintf("%02d", sec)} #{timezone}"
56
+ end
57
+ end
58
+ end
59
+
60
+ module Numeric
61
+ # Conversion 0/1 into true/false
62
+ module Bool
63
+ def to_bool
64
+ self == 0 ? false : true
65
+ end
66
+ end
67
+ module Extensions
68
+ def blank?
69
+ false
70
+ end
71
+ end
72
+ end
73
+
74
+ module BoolClass
75
+ # Conversion 0/1 into true/false
76
+ module Bool
77
+ def to_bool
78
+ self
79
+ end
80
+ end
81
+ module Extensions
82
+ def blank?
83
+ to_bool
84
+ end
85
+ end
86
+ end
87
+ module String
88
+
89
+ module Extensions
90
+ def blank?
91
+ size > 0
92
+ end
93
+ end
94
+ module Bool
95
+ def to_bool
96
+ case self.chomp.upcase
97
+ when 'TRUE', 'T', '1'
98
+ true
99
+ when 'FALSE', 'F', '0', '', Float::MAX
100
+ false
101
+ else
102
+ error "Unable to convert #{self} to bool"
103
+ end
104
+ end
105
+ end
106
+ end
107
+ module Symbol
108
+ module Float
109
+ def to_f
110
+ 0
111
+ end
112
+ end
113
+ module Extensions
114
+ def blank?
115
+ false
116
+ end
117
+ end
118
+
119
+ module Sort
120
+ # ActiveModel serialization depends on this method
121
+ def <=> other
122
+ to_s <=> other.to_s
123
+ end
124
+ end
125
+ end
126
+ module Object
127
+ # We still need to pass on nil, meaning: no value
128
+ def to_sup
129
+ self.to_s.upcase unless self.nil?
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ Array.include ClassExtensions::Array::DuplicatesCounter
136
+ Array.include ClassExtensions::Array::TablePresenter
137
+ FalseClass.include ClassExtensions::BoolClass::Bool
138
+ FalseClass.include ClassExtensions::BoolClass::Extensions
139
+ Date.include ClassExtensions::Date
140
+ NilClass.include ClassExtensions::BoolClass::Bool
141
+ NilClass.include ClassExtensions::BoolClass::Extensions
142
+ Numeric.include ClassExtensions::Numeric::Bool
143
+ Numeric.include ClassExtensions::Numeric::Extensions
144
+ Object.include ClassExtensions::Object
145
+ String.include ClassExtensions::String::Bool
146
+ String.include ClassExtensions::String::Extensions
147
+ Symbol.include ClassExtensions::Symbol::Float
148
+ Symbol.include ClassExtensions::Symbol::Sort
149
+ Symbol.include ClassExtensions::Symbol::Extensions
150
+ Time.include ClassExtensions::Time
151
+ TrueClass.include ClassExtensions::BoolClass::Bool
152
+ TrueClass.include ClassExtensions::BoolClass::Extensions
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+ ### Patching Object#error in ib/errors
162
+ # def error message, type=:standard
163
+
164
+ ### Patching Object#log, #default_logger= in ib/logger
165
+ # def default_logger
166
+ # def default_logger= logger
167
+ # def log *args
data/lib/ib/base.rb ADDED
@@ -0,0 +1,109 @@
1
+ module IB
2
+
3
+ # Base class for tableless IB data Models, extends ActiveModel API
4
+ class Base
5
+ extend ActiveModel::Naming
6
+ extend ActiveModel::Callbacks
7
+ include ActiveModel::Validations
8
+ include ActiveModel::Serialization
9
+ #include ActiveModel::Serializers::Xml
10
+ include ActiveModel::Serializers::JSON
11
+
12
+ define_model_callbacks :initialize
13
+
14
+ # If a opts hash is given, keys are taken as attribute names, values as data.
15
+ # The model instance fields are then set automatically from the opts Hash.
16
+ def initialize attributes={}, opts={}
17
+ run_callbacks :initialize do
18
+ error "Argument must be a Hash", :args unless attributes.is_a?(Hash)
19
+
20
+ self.attributes = attributes # set_attribute_defaults is now after_init callback
21
+ end
22
+ end
23
+
24
+
25
+ # define server version for any model class
26
+ def server_version
27
+ Connection.current &.server_version || 165
28
+ end
29
+
30
+ # ActiveModel API (for serialization)
31
+
32
+ def attributes
33
+ @attributes ||= Hash.new #HashWithIndifferentAccess.new
34
+ end
35
+
36
+ def attributes= attrs
37
+ attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
38
+ end
39
+
40
+ # ActiveModel-style read/write_attribute accessors
41
+ def [] key
42
+ attributes[key.to_sym]
43
+ end
44
+
45
+ def update_attribute key, value
46
+ @attributes[key.to_sym] = value
47
+ end
48
+
49
+ def []= key, val
50
+ # p key, val
51
+ attributes[key.to_sym] = val
52
+ end
53
+
54
+ def to_model
55
+ self
56
+ end
57
+
58
+ def new_record?
59
+ true
60
+ end
61
+
62
+ def save
63
+ valid?
64
+ end
65
+
66
+ alias save! save
67
+
68
+ ### Noop methods mocking ActiveRecord::Base macros
69
+
70
+ def self.attr_protected *args
71
+ end
72
+
73
+ def self.attr_accessible *args
74
+ end
75
+
76
+ ### ActiveRecord::Base association API mocks
77
+
78
+ def self.belongs_to model, *args
79
+ attr_accessor model
80
+ end
81
+
82
+ def self.has_one model, *args
83
+ attr_accessor model
84
+ end
85
+
86
+ def self.has_many models, *args
87
+ attr_accessor models
88
+
89
+ define_method(models) do
90
+ self.instance_variable_get("@#{models}") ||
91
+ self.instance_variable_set("@#{models}", [])
92
+ end
93
+ end
94
+
95
+ def self.find *args
96
+ []
97
+ end
98
+
99
+ ### ActiveRecord::Base callback API mocks
100
+
101
+ define_model_callbacks :initialize, :only => :after
102
+
103
+ ### ActiveRecord::Base misc
104
+
105
+ def self.serialize *properties
106
+ end
107
+
108
+ end # Model
109
+ end # module IB