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.
- checksums.yaml +7 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CLAUDE.md +131 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +120 -0
- data/Guardfile +24 -0
- data/LICENSE +674 -0
- data/LLM_GUIDE.md +388 -0
- data/README.md +114 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/api.gemspec +50 -0
- data/bin/console +96 -0
- data/bin/console.yml +3 -0
- data/bin/setup +8 -0
- data/bin/simple +91 -0
- data/changelog.md +32 -0
- data/conditions/ib/execution_condition.rb +31 -0
- data/conditions/ib/margin_condition.rb +28 -0
- data/conditions/ib/order_condition.rb +29 -0
- data/conditions/ib/percent_change_condition.rb +34 -0
- data/conditions/ib/price_condition.rb +44 -0
- data/conditions/ib/time_condition.rb +42 -0
- data/conditions/ib/volume_condition.rb +36 -0
- data/lib/class_extensions.rb +167 -0
- data/lib/ib/base.rb +109 -0
- data/lib/ib/base_properties.rb +178 -0
- data/lib/ib/connection.rb +573 -0
- data/lib/ib/constants.rb +402 -0
- data/lib/ib/contract.rb +30 -0
- data/lib/ib/errors.rb +52 -0
- data/lib/ib/messages/abstract_message.rb +68 -0
- data/lib/ib/messages/incoming/abstract_message.rb +116 -0
- data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
- data/lib/ib/messages/incoming/account_message.rb +26 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +105 -0
- data/lib/ib/messages/incoming/contract_message.rb +13 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +50 -0
- data/lib/ib/messages/incoming/histogram_data.rb +30 -0
- data/lib/ib/messages/incoming/historical_data.rb +65 -0
- data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
- data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
- data/lib/ib/messages/incoming/market_depth.rb +34 -0
- data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
- data/lib/ib/messages/incoming/open_order.rb +290 -0
- data/lib/ib/messages/incoming/order_status.rb +85 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
- data/lib/ib/messages/incoming/position_data.rb +21 -0
- data/lib/ib/messages/incoming/positions_multi.rb +15 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/receive_fa.rb +30 -0
- data/lib/ib/messages/incoming/scanner_data.rb +54 -0
- data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
- data/lib/ib/messages/incoming/tick_efp.rb +18 -0
- data/lib/ib/messages/incoming/tick_generic.rb +12 -0
- data/lib/ib/messages/incoming/tick_option.rb +60 -0
- data/lib/ib/messages/incoming/tick_price.rb +60 -0
- data/lib/ib/messages/incoming/tick_size.rb +55 -0
- data/lib/ib/messages/incoming/tick_string.rb +13 -0
- data/lib/ib/messages/incoming.rb +292 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
- data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
- data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
- data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
- data/lib/ib/messages/outgoing/place_order.rb +149 -0
- data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
- data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
- data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
- data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
- data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
- data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
- data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
- data/lib/ib/messages/outgoing.rb +410 -0
- data/lib/ib/messages.rb +139 -0
- data/lib/ib/order_condition.rb +26 -0
- data/lib/ib/plugins.rb +27 -0
- data/lib/ib/prepare_data.rb +61 -0
- data/lib/ib/raw_message_parser.rb +99 -0
- data/lib/ib/socket.rb +83 -0
- data/lib/ib/support.rb +236 -0
- data/lib/ib/version.rb +6 -0
- data/lib/ib-api.rb +44 -0
- data/lib/server_versions.rb +145 -0
- data/lib/support/array_function.rb +28 -0
- data/lib/support/logging.rb +45 -0
- data/models/ib/account.rb +72 -0
- data/models/ib/account_value.rb +33 -0
- data/models/ib/bag.rb +55 -0
- data/models/ib/bar.rb +31 -0
- data/models/ib/combo_leg.rb +127 -0
- data/models/ib/contract.rb +411 -0
- data/models/ib/contract_detail.rb +118 -0
- data/models/ib/execution.rb +67 -0
- data/models/ib/forex.rb +12 -0
- data/models/ib/future.rb +64 -0
- data/models/ib/index.rb +14 -0
- data/models/ib/option.rb +149 -0
- data/models/ib/option_detail.rb +84 -0
- data/models/ib/order.rb +720 -0
- data/models/ib/order_state.rb +155 -0
- data/models/ib/portfolio_value.rb +86 -0
- data/models/ib/spread.rb +176 -0
- data/models/ib/stock.rb +25 -0
- data/models/ib/underlying.rb +32 -0
- data/plugins/ib/advanced-account.rb +442 -0
- data/plugins/ib/alerts/base-alert.rb +125 -0
- data/plugins/ib/alerts/gateway-alerts.rb +15 -0
- data/plugins/ib/alerts/order-alerts.rb +73 -0
- data/plugins/ib/auto-adjust.rb +0 -0
- data/plugins/ib/connection-tools.rb +122 -0
- data/plugins/ib/eod.rb +326 -0
- data/plugins/ib/greeks.rb +102 -0
- data/plugins/ib/managed-accounts.rb +274 -0
- data/plugins/ib/market-price.rb +150 -0
- data/plugins/ib/option-chain.rb +167 -0
- data/plugins/ib/order-flow.rb +157 -0
- data/plugins/ib/order-prototypes/abstract.rb +67 -0
- data/plugins/ib/order-prototypes/adaptive.rb +40 -0
- data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
- data/plugins/ib/order-prototypes/combo.rb +46 -0
- data/plugins/ib/order-prototypes/forex.rb +40 -0
- data/plugins/ib/order-prototypes/limit.rb +193 -0
- data/plugins/ib/order-prototypes/market.rb +116 -0
- data/plugins/ib/order-prototypes/pegged.rb +169 -0
- data/plugins/ib/order-prototypes/premarket.rb +31 -0
- data/plugins/ib/order-prototypes/stop.rb +202 -0
- data/plugins/ib/order-prototypes/volatility.rb +39 -0
- data/plugins/ib/order-prototypes.rb +118 -0
- data/plugins/ib/probability-of-expiring.rb +109 -0
- data/plugins/ib/process-orders.rb +155 -0
- data/plugins/ib/roll.rb +86 -0
- data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
- data/plugins/ib/spread-prototypes/calendar.rb +97 -0
- data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
- data/plugins/ib/spread-prototypes/straddle.rb +70 -0
- data/plugins/ib/spread-prototypes/strangle.rb +93 -0
- data/plugins/ib/spread-prototypes/vertical.rb +83 -0
- data/plugins/ib/spread-prototypes.rb +70 -0
- data/plugins/ib/symbols/abstract.rb +136 -0
- data/plugins/ib/symbols/bonds.rb +28 -0
- data/plugins/ib/symbols/cfd.rb +19 -0
- data/plugins/ib/symbols/combo.rb +46 -0
- data/plugins/ib/symbols/commodity.rb +17 -0
- data/plugins/ib/symbols/forex.rb +41 -0
- data/plugins/ib/symbols/futures.rb +127 -0
- data/plugins/ib/symbols/index.rb +43 -0
- data/plugins/ib/symbols/options.rb +99 -0
- data/plugins/ib/symbols/stocks.rb +44 -0
- data/plugins/ib/symbols/version.rb +5 -0
- data/plugins/ib/symbols.rb +118 -0
- data/plugins/ib/verify.rb +226 -0
- data/symbols/w20.yml +210 -0
- data/t.txt +20 -0
- data/update.md +71 -0
- 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
|