ib-api 972.0 → 972.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 542bca5318150ae448c4b7578abd64beec70e5b35760eea8abfd22c28f5da1fd
4
- data.tar.gz: cb68b0afa4812eb224936f5269be2f6d6c825ea5f9bd21b25c1c9dfe21b70777
3
+ metadata.gz: d9119cbc06f3b5e5e32b73fbe0d706b06d94c55ff2ce2ca4357e4f349f49f43f
4
+ data.tar.gz: b977e09dee0df176e673679839b738425871dbf25ca8cad4e8f497323647ecc6
5
5
  SHA512:
6
- metadata.gz: 96bac41a8432c7175dcba4fa38548db4af966d370fc8e12570c4cfd28f89284afb3188ad50d1131859861cc40d414515772cece8bed147093de04aaaf3e6b2c5
7
- data.tar.gz: 57c186381a3da9eb7e2169d588ca99440c24993a41905ca9828c4490471a229d1e078739b34f58e666fffb7d4031bba0dfcae4a48e987667ce6d0d65246a66be
6
+ metadata.gz: '02538d6ab965ee0024cef798aebbcdff42859025a848108c5ca7af1e305cae019b1db3f9489300497fde388f5b0e659064d305ec316695d4154d20abc05277f1'
7
+ data.tar.gz: 3245c0fda19a98f6ab5b29827b246b91d31c91a5daac10bcd8bbe114d699edc117922bae11503b3219f7bd18d8b654b6be6e19365c346bad3f4201829ce6f97b
@@ -7,21 +7,21 @@ GIT
7
7
  PATH
8
8
  remote: .
9
9
  specs:
10
- ib-api (972.0)
10
+ ib-api (972.1)
11
11
  activemodel
12
12
  activesupport (>= 6.0)
13
13
 
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- activemodel (6.0.3.4)
18
- activesupport (= 6.0.3.4)
19
- activesupport (6.0.3.4)
17
+ activemodel (6.1.0)
18
+ activesupport (= 6.1.0)
19
+ activesupport (6.1.0)
20
20
  concurrent-ruby (~> 1.0, >= 1.0.2)
21
- i18n (>= 0.7, < 2)
22
- minitest (~> 5.1)
23
- tzinfo (~> 1.1)
24
- zeitwerk (~> 2.2, >= 2.2.2)
21
+ i18n (>= 1.6, < 2)
22
+ minitest (>= 5.1)
23
+ tzinfo (~> 2.0)
24
+ zeitwerk (~> 2.3)
25
25
  coderay (1.1.3)
26
26
  concurrent-ruby (1.1.7)
27
27
  diff-lcs (1.4.4)
@@ -80,11 +80,10 @@ GEM
80
80
  rspec-support (3.9.3)
81
81
  shellany (0.0.1)
82
82
  thor (1.0.1)
83
- thread_safe (0.3.6)
84
- tzinfo (1.2.7)
85
- thread_safe (~> 0.1)
83
+ tzinfo (2.0.4)
84
+ concurrent-ruby (~> 1.0)
86
85
  value_semantics (3.6.0)
87
- zeitwerk (2.4.0)
86
+ zeitwerk (2.4.2)
88
87
 
89
88
  PLATFORMS
90
89
  ruby
data/README.md CHANGED
@@ -3,6 +3,7 @@ Ruby interface to Interactive Brokers' TWS API
3
3
 
4
4
  Reimplementation of the basic functions of ib-ruby
5
5
 
6
+ __Documentation: [https://ib-ruby.github.io/ib-doc/](https://ib-ruby.github.io/ib-doc/)__ (_work in progress_)
6
7
 
7
8
  ----
8
9
  `ib-ruby` offers a modular access to the TWS-API-Interface of Interactive Brokers.
@@ -11,12 +12,18 @@ Reimplementation of the basic functions of ib-ruby
11
12
 
12
13
  ----
13
14
 
14
- In its plain vanilla usage, it just exchanges messages with the TWS. The user is responsible for any further data processing.
15
+ Install in the usual way
15
16
 
17
+ ```
18
+ $ gem install ib-api
19
+ ```
20
+
21
+ In its plain vanilla usage, it just exchanges messages with the TWS. Any response is stored in the `recieved-Array`.
16
22
 
17
23
  Even then, it needs just a few lines of code to place an order
18
24
 
19
25
  ```ruby
26
+ require 'ib-api'
20
27
  # connect with default parameters
21
28
  ib = IB::Connection.new
22
29
 
@@ -38,10 +45,48 @@ puts ib.recieved[:OrderStatus].to_human
38
45
 
39
46
  # => ["<OrderState: Submitted #17/1528367295 from 2000 filled 0.0/100.0 at 0.0/0.0 why_held >"]
40
47
 
48
+ ```
49
+
50
+ ##### User-specific Actions
51
+ Besides storing any TWS-response in an array, callbacks are implemented.
52
+
53
+ The user subscribes to a certain response and defines the actions in a typically ruby manner. These actions
54
+ can be defined globaly
55
+ ```ruby
56
+ ib = IB::Connection.new do |tws|
57
+ # Subscribe to TWS alerts/errors and order-related messages
58
+ tws.subscribe(:Alert, :OpenOrder, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
59
+ end
60
+
61
+ ```
62
+
63
+ or occationally
64
+
65
+ ```ruby
66
+ # first define actions
67
+ a = ib.subscribe(:Alert, :ContractData ) do |msg|
68
+ case msg
69
+ when Messages::Incoming::Alert
70
+ if msg.code == 200 # No security found
71
+ # do someting
72
+ end
73
+ when Messages::Incoming::ContractData # security returned
74
+ # do something
75
+
76
+ end # case
77
+ end
78
+ # perform request
79
+ ib.send_message :RequestContractData, :contract => #{some contract}
80
+
81
+ # wait until the :ContractDataEnd message returned
82
+ ib.wait_for :ContractDataEnd
83
+
84
+ ib.unsubscribe a # release subscriptions
85
+
41
86
  ```
42
87
  ## Minimal TWS-Version
43
88
 
44
- `ib-api` is tested via the _stable ib-Gateway_ (Version 9.72)
89
+ `ib-api` is tested via the _stable IB-Gateway_ (Version 9.72) and should work with any current tws-installation.
45
90
 
46
91
  ## Tests
47
92
 
@@ -58,7 +103,7 @@ You have to edit `spec/spec.yml` and replace the `:account`-Setting with your ow
58
103
 
59
104
  ## Contributing
60
105
 
61
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ib-api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
106
+ Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
62
107
 
63
108
  ## Code of Conduct
64
109
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 972.0
1
+ 972.1
@@ -5,3 +5,14 @@ Changelog
5
5
  |=++++ |=++++++++++++ |
6
6
  | 30.8.2020 | migrating lib-files from ib-ruby-project |
7
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
+
@@ -19,11 +19,10 @@ module IB
19
19
 
20
20
  # Comparison support
21
21
  def content_attributes
22
- #HashWithIndifferentAccess[attributes.reject do |(attr, _)|
23
22
  #NoMethodError if a Hash is assigned to an attribute
24
23
  Hash[attributes.reject do |(attr, _)|
25
24
  attr.to_s =~ /(_count)\z/ ||
26
- [:created_at, :updated_at, :type,
25
+ [:created_at, :type, # :updated_at,
27
26
  :id, :order_id, :contract_id].include?(attr.to_sym)
28
27
  end]
29
28
  end
@@ -151,9 +150,9 @@ Remove all Time-Stamps from the list of Attributes
151
150
  end
152
151
 
153
152
  # Timestamps in lightweight models
154
- unless defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
153
+ # unless defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
155
154
  prop :created_at #, :updated_at
156
- end
155
+ # end
157
156
 
158
157
  end # included
159
158
  end # module BaseProperties
@@ -139,7 +139,7 @@ module IB
139
139
  TickRequestParameters = def_message [81, 0], [ :ticker_id, :int ],
140
140
  [ :min_tick, :decimal],
141
141
  [ :exchange, :string ],
142
- [ :snapshot_prermissions, :int ]
142
+ [ :snapshot_permissions, :int ]
143
143
  # class TickRequestParameters
144
144
  # def load
145
145
  # simple_load
@@ -56,8 +56,6 @@ module IB
56
56
  @contract_detail = IB::ContractDetail.new @data[:contract_detail]
57
57
  end
58
58
 
59
- alias contract_details contract_detail
60
-
61
59
  def to_human
62
60
  "<Contract #{contract.to_human} #{contract_detail.to_human}>"
63
61
  end
@@ -13,7 +13,7 @@ module IB
13
13
  "<#{self.message_type} #{type}:" +
14
14
  @data.map do |key, value|
15
15
  " #{key} #{value}" unless [:version, :ticker_id, :tick_type].include?(key)
16
- end.compact.join(',') + " >"
16
+ end.compact.join('",') + " >"
17
17
  end
18
18
 
19
19
  def the_data
@@ -129,71 +129,32 @@ module IB
129
129
  [:theta, :decimal_limit_2], # -2 -"-
130
130
  [:under_price, :decimal_limit_1]) do
131
131
 
132
- "<TickOption #{type} for #{ticker_id}: underlying @ #{under_price}, "+
133
- "option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
134
- "gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
132
+ "<TickOption #{type} " +
133
+ "option @ #{"%8.3f" % (option_price || -1)}, IV: #{"%4.3f" % (implied_volatility || -1)}, " +
134
+ "delta: #{"%5.3f" % (delta || -1)}, " +
135
+ "gamma: #{"%6.4f" % (gamma || -1)}, vega: #{ "%6.5f" % (vega || -1)}, " +
136
+ "theta: #{"%7.6f" % (theta || -1)}, pv_dividend: #{"%5.3f" % (pv_dividend || -1)}, " +
137
+ "underlying @ #{"% 8.3f" % (under_price || -1)} >"
135
138
  end
136
139
 
137
- TickSnapshotEnd = def_message 57, [:ticker_id, :int]
138
- =begin
139
- def processTickByTickMsg(self, fields):
140
- next(fields)
141
- reqId = decode(int, fields)
142
- tickType = decode(int, fields)
143
- time = decode(int, fields)
144
-
145
- if tickType == 0:
146
- # None
147
- pass
148
- elif tickType == 1 or tickType == 2:
149
- # Last or AllLast
150
- price = decode(float, fields)
151
- size = decode(int, fields)
152
- mask = decode(int, fields)
153
- class TickAttribLast(Object):
154
- def __init__(self):
155
- self.pastLimit = False
156
- self.unreported = False
157
-
158
- def __str__(self):
159
- return "PastLimit: %d, Unreported: %d" % (self.pastLimit, self.unreported)
160
-
161
- tickAttribLast = TickAttribLast()
162
- tickAttribLast.pastLimit = mask & 1 != 0
163
- tickAttribLast.unreported = mask & 2 != 0
164
- exchange = decode(str, fields)
165
- specialConditions = decode(str, fields)
140
+ class TickOption
141
+ def greeks
142
+ { delta: delta, gamma: gamma, vega: vega, theta: theta }
143
+ end
166
144
 
167
- self.wrapper.tickByTickAllLast(reqId, tickType, time, price, size, tickAttribLast,
168
- exchange, specialConditions)
169
- elif tickType == 3:
170
- # BidAsk
171
- bidPrice = decode(float, fields)
172
- askPrice = decode(float, fields)
173
- bidSize = decode(int, fields)
174
- askSize = decode(int, fields)
175
- mask = decode(int, fields)
176
- class TickAttribBidAsk(Object):
177
- def __init__(self):
178
- self.bidPastLow = False
179
- self.askPastHigh = False
145
+ def iv
146
+ implied_volatility
147
+ end
148
+
149
+
150
+ def greeks?
151
+ greeks.values.any? &:present?
152
+ end
180
153
 
181
- def __str__(self):
182
- return "BidPastLow: %d, AskPastHigh: %d" % (self.bidPastLow, self.askPastHigh)
154
+ end
183
155
 
156
+ TickSnapshotEnd = def_message 57, [:ticker_id, :int]
184
157
 
185
- tickAttribBidAsk = TickAttribBidAsk()
186
- tickAttribBidAsk.bidPastLow = mask & 1 != 0
187
- tickAttribBidAsk.askPastHigh = mask & 2 != 0
188
-
189
- self.wrapper.tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize,
190
- askSize, tickAttribBidAsk)
191
- elif tickType == 4:
192
- # MidPoint
193
- midPoint = decode(float, fields)
194
-
195
- self.wrapper.tickByTickMidPoint(reqId, time, midPoint)
196
- =end
197
158
  TickByTick = def_message [99, 0], [:ticker_id, :int ],
198
159
  [ :tick_type, :int],
199
160
  [ :time, :int_date ]
@@ -249,10 +249,12 @@ module IB
249
249
  CancelHistogramData =
250
250
  def_message [89,0 ] # , :(request_)id required
251
251
 
252
+ ## Attention: If not reasonable data are used, simply nothing is returned.
253
+ ## There is no error message either.
252
254
  RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
253
255
  RequestImpliedVolatility =
254
256
  def_message([ 54,3 ],:request_id, # autogenerated
255
- [:contract, :serialize_short, :primary_exchange],
257
+ [:contract, :serialize_short],
256
258
  :option_price,
257
259
  :under_price,
258
260
  [:implied_volatility_options_count, 0],
@@ -262,7 +264,7 @@ module IB
262
264
  # :volatility => double, :under_price => double }
263
265
  RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
264
266
  def_message([ 55, 3], :request_id, #autogenerated if not specified
265
- [:contract, :serialize_short, :primary_exchange],
267
+ [:contract, :serialize_short],
266
268
  :volatility,
267
269
  :under_price,
268
270
  [:implied_volatility_options_count, 0],
@@ -4,7 +4,8 @@ module IB
4
4
  module Outgoing
5
5
  extend Messages # def_message macros
6
6
 
7
-
7
+ # ==> details: https://interactivebrokers.github.io/tws-api/tick_types.html
8
+ #
8
9
  # @data={:id => int: ticker_id - Must be a unique value. When the market data
9
10
  # returns, it will be identified by this tag,
10
11
  # if omitted, id-autogeneration process is performed
@@ -32,7 +33,7 @@ module IB
32
33
  # 292 - (Wide News)
33
34
  # 293 - (TradeCount)
34
35
  # 295 - (VolumeRate)
35
- # 318 - (iLastRTHT-Trade)
36
+ # 318 - (LastRTHT-Trade)
36
37
  # 370 - (Participation Monitor)
37
38
  # 375 - RTTrdVolumne
38
39
  # 377 - CttTickTag
@@ -46,7 +47,11 @@ module IB
46
47
  # 411 - Realtime Historical Volatility - 58
47
48
  # 428 - Monetary Close
48
49
  # 439 - MonitorTicTag
49
- # 456/59 - IB Dividends
50
+ # 456/59 - IB Dividends, 4 comma separated values: 12 Month dividend,
51
+ # projected 12 Month dividend,
52
+ # next dividend date,
53
+ # next dividend value
54
+ # (use primary exchange instead of smart)
50
55
  # 459 - RTCLOSE
51
56
  # 460 - Bond Factor Multiplier
52
57
  # 499 - Fee and Rebate Ratge
@@ -79,7 +84,7 @@ module IB
79
84
  # :tick_list values if you use snapshot.
80
85
  #
81
86
  # :regulatory_snapshot => bool - With the US Value Snapshot Bundle for stocks,
82
- # regulatory snapshots are available for 0.01 USD each.
87
+ # regulatory snapshots are available for 0.01 USD each. (applies on demo accounts as well)
83
88
  # :mktDataOptions => (TagValueList) For internal use only.
84
89
  # Use default value XYZ.
85
90
  #
@@ -88,9 +93,7 @@ module IB
88
93
  [:contract, :serialize_short, :primary_exchange], # include primary exchange in request
89
94
  [:contract, :serialize_legs, []],
90
95
  [:contract, :serialize_under_comp, []],
91
- [:tick_list, lambda do |tick_list|
92
- tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')
93
- end, []],
96
+ [:tick_list, ->(tick_list){ tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')}, []],
94
97
  [:snapshot, false],
95
98
  [:regulatory_snapshot, false],
96
99
  [:mkt_data_options, "XYZ"]
@@ -1,4 +1,3 @@
1
- require 'ib/model'
2
1
 
3
2
  require 'models/ib/account'
4
3
  require 'models/ib/account_value'
@@ -11,4 +10,5 @@
11
10
  require 'models/ib/combo_leg'
12
11
  require 'models/ib/execution'
13
12
  require 'models/ib/bar'
13
+ require 'models/ib/spread'
14
14
  require 'models/ib/condition'
@@ -1,7 +1,6 @@
1
1
  module IB
2
2
  class Account < IB::Model
3
3
  include BaseProperties
4
- # include Redis::Objects
5
4
  # attr_accessible :alias, :account, :connected
6
5
 
7
6
  prop :account, # String
@@ -10,10 +9,6 @@ module IB
10
9
  :last_updated,
11
10
  :connected => :bool
12
11
 
13
- # redis_id_field :account
14
- # value :my_alias
15
- # value :the_account
16
- # value :active
17
12
 
18
13
 
19
14
  validates_format_of :account, :with => /\A[D]?[UF]{1}\d{5,8}\z/ , :message => 'should be (X)X00000'
@@ -37,14 +32,6 @@ module IB
37
32
  Connection.logger
38
33
  end
39
34
 
40
- # Setze Account connect/disconnect und undate!
41
- def connect!
42
- update_attribute :connected , true
43
- end
44
- def disconnect!
45
- update_attribute :connected , false
46
- end
47
-
48
35
  def print_type #nodoc#
49
36
  (test_environment? ? "demo_" : "") + ( user? ? "user" : "advisor" )
50
37
  end
@@ -1,4 +1,4 @@
1
- require 'models/ib/contract'
1
+ #require 'models/ib/contract'
2
2
 
3
3
  module IB
4
4
 
@@ -4,7 +4,13 @@ require 'models/ib/underlying'
4
4
 
5
5
 
6
6
  module IB
7
- class Contract < IB::Model
7
+
8
+ if defined?(Contract)
9
+ puts "Contract already a #{defined?(Contract)}"
10
+ # puts Contract.ancestors
11
+ # IB.send(:remove_const, 'Contract')
12
+ end
13
+ class Contract < IB::Model
8
14
  include BaseProperties
9
15
 
10
16
  # Fields are Strings unless noted otherwise
@@ -211,17 +217,24 @@ module IB
211
217
  # the link to contract-details is __not__ maintained.
212
218
  def essential
213
219
 
214
- self_attributes = [ :right, :sec_type]
215
- the_attributes = [ :symbol , :con_id, :exchange,
220
+ self_attributes = [ :sec_type]
221
+ the_attributes = [ :symbol , :con_id, :exchange, :right,
216
222
  :currency, :expiry, :strike, :local_symbol, :last_trading_day,
217
223
  :multiplier, :primary_exchange, :trading_class ]
218
- the_hash= the_attributes.map{|x| y= attributes[x]; [x,y] if y.present? }.compact.to_h
219
- the_hash[:description] = @description if @description.present?
220
- self.class.new the_hash.merge( self_attributes.map{|x| y = self.send(x); [x,y] unless y == :none}.compact.to_h )
224
+ the_hash= the_attributes.map{|x| y= self.send(x); [x,y] if y.present? }.compact.to_h
225
+ the_hash[:description] =
226
+ if @description.present?
227
+ @description
228
+ elsif contract_detail.present?
229
+ contract_detail.long_name
230
+ else
231
+ ""
232
+ end
233
+ self.class.new the_hash.merge( self_attributes.map{|x| y = self.send(x); [x,y] unless y == :none }.compact.to_h )
221
234
  end
222
235
 
223
236
 
224
- # creates a new Contract substituting attributes by the provied key-value pairs.
237
+ # creates a new Contract substituting attributes by the provided key-value pairs.
225
238
  #
226
239
  # con_id is resetted
227
240
  def merge **new_attributes
@@ -327,6 +340,10 @@ module IB
327
340
  self[:sec_type] == 'IND'
328
341
  end
329
342
 
343
+
344
+ def verify # :nodoc:
345
+ error "verify must be overloaded. Please require at least `ib/verify` from the `ib-extenstions` gem "
346
+ end
330
347
  =begin
331
348
  From the release notes of TWS 9.50
332
349
 
@@ -365,7 +382,7 @@ In places where these terms are used to indicate a concept, we have left them as
365
382
 
366
383
  ### Now let's deal with Contract subclasses
367
384
 
368
- require 'models/ib/option'
385
+ require_relative 'option'
369
386
  require 'models/ib/bag'
370
387
  require 'models/ib/forex'
371
388
  require 'models/ib/future'
@@ -378,6 +395,7 @@ In places where these terms are used to indicate a concept, we have left them as
378
395
  Subclasses = Hash.new(Contract)
379
396
  Subclasses[:bag] = IB::Bag
380
397
  Subclasses[:option] = IB::Option
398
+ Subclasses[:future_option] = IB::FutureOption
381
399
  Subclasses[:future] = IB::Future
382
400
  Subclasses[:stock] = IB::Stock
383
401
  Subclasses[:forex] = IB::Forex
@@ -95,13 +95,19 @@ module IB
95
95
  end
96
96
 
97
97
  def to_human
98
- ret = "<ContractDetails #{long_name}, market-name:#{market_name}, "
99
- ret << "category:#{category}, industry:#{industry} / #{subcategory}, " if category.present?
100
- ret << "underlying: con_id:#{under_con_id} , sec_type:#{under_sec_type}, symbol:#{under_symbol} " unless under_con_id.zero?
101
- ret << "ev_multiplier:#{ev_multiplier}, convertible:#{convertible}, cupon:#{coupon}, "
102
- ret << "md_size_multiplier:#{md_size_multiplier}, min_tick:#{min_tick}, next_option_partial:#{next_option_partial} "
103
- ret <<"price_magnifier:#{price_magnifier}, puttable:#{puttable}, sec_id-list:#{sec_id_list}, "
104
- ret <<"valid exchanges: #{ valid_exchanges}, order types: #{order_types} >"
98
+ ret = "<ContractDetails #{long_name}, "
99
+ ret << "--> #{market_name}, " if market_name.present?
100
+ ret << "/C/ #{category}, /I/ #{industry} /SC/ #{subcategory}, " if category.present?
101
+ ret << "Underlying:#{under_symbol}[#{under_sec_type}](#{under_con_id}), " unless under_con_id.zero?
102
+ ret << "ev_multiplier:#{ev_multiplier}, " if ev_multiplier.present?
103
+ ret << "convertible:#{convertible}, " if convertible
104
+ ret << "coupon:#{coupon}, " if coupon.present? && coupon > 0
105
+ ret << "md_size_multiplier:#{md_size_multiplier}, min_tick:#{min_tick}, "
106
+ ret << "next_option_partial:#{next_option_partial}, " if next_option_partial.present?
107
+ ret << "price_magnifier:#{price_magnifier}, "
108
+ ret << "puttable:#{puttable}, " if puttable.present?
109
+ ret << "sec_id-list:#{sec_id_list}, " unless sec_id_list.empty?
110
+ ret <<"valid exchanges: #{ valid_exchanges}; order types: #{order_types} >"
105
111
  end
106
112
 
107
113
  end # class ContractDetail
@@ -7,7 +7,7 @@ module IB
7
7
  super.merge :sec_type => :ind
8
8
  end
9
9
  def to_human
10
- "<Index: " + [symbol, currency].join(" ") + ">"
10
+ "<Index: " + [symbol, currency].join(" ") + " (#{description}) >"
11
11
  end
12
12
 
13
13
  end
@@ -1,5 +1,5 @@
1
- require 'models/ib/contract'
2
- require 'models/ib/option_detail'
1
+ #require_relative 'contract'
2
+ require_relative 'option_detail'
3
3
 
4
4
  module IB
5
5
  class Option < Contract
@@ -75,4 +75,8 @@ module IB
75
75
  end
76
76
 
77
77
  end # class Option
78
+
79
+ class FutureOption < Option
80
+
81
+ end
78
82
  end # module IB
@@ -17,7 +17,8 @@ module IB
17
17
  :next_strike,
18
18
  :prev_expiry,
19
19
  :next_expiry,
20
- :option_price
20
+ :option_price,
21
+ :updated_at
21
22
  belongs_to :option
22
23
 
23
24
  # returns true if all datafields are filled with reasonal data
@@ -32,19 +33,34 @@ module IB
32
33
 
33
34
  def greeks?
34
35
  fields= [ :delta, :gamma, :vega, :theta,
35
- :implied_volatility, :pv_dividend]
36
+ :implied_volatility]
36
37
 
37
38
  !fields.detect{|y| self.send(y).nil?}
38
39
 
39
40
  end
40
41
 
42
+ def prices?
43
+ fields = [:implied_volatility, :under_price, :option_price]
44
+ !fields.detect{|y| self.send(y).nil?}
45
+ end
46
+
47
+ def iv
48
+ implied_volatility
49
+ end
50
+
51
+ def spread
52
+ bid_price - ask_price
53
+ end
54
+
41
55
  def to_human
42
56
  outstr= ->( item ) { if item.nil? then "--" else sprintf("%g" , item) end }
43
57
  att = " optionPrice: #{ outstr[ option_price ]}, UnderlyingPrice: #{ outstr[ under_price] } impl.Vola: #{ outstr[ implied_volatility ]} ; dividend: #{ outstr[ pv_dividend ]}; "
44
58
  greeks = "Greeks:: delta: #{ outstr[ delta ] }; gamma: #{ outstr[ gamma ]}, vega: #{ outstr[ vega ] }; theta: #{ outstr[ theta ]}"
45
59
  prices= " close: #{ outstr[ close_price ]}; bid: #{ outstr[ bid_price ]}; ask: #{ outstr[ ask_price ]} "
46
60
  if complete?
47
- "< "+ prices + "\n" + att + "\n" + greeks + " >"
61
+ "< "+ prices + "\n" + att + "\n" + greeks + " >"
62
+ elsif prices?
63
+ "< " + att + greeks + " >"
48
64
  else
49
65
  "< " + greeks + " >"
50
66
  end
@@ -0,0 +1,159 @@
1
+ #require 'ib/verify'
2
+ module IB
3
+ class Spread < Bag
4
+ has_many :legs
5
+
6
+ using IBSupport
7
+
8
+ =begin
9
+ Parameters: front: YYYMM(DD)
10
+ back: {n}w, {n}d or YYYYMM(DD)
11
+
12
+ Adds (or substracts) relative (back) measures to the front month, just passes absolute YYYYMM(DD) value
13
+
14
+ front: 201809 back: 2m (-1m) --> 201811 (201808)
15
+ front: 20180908 back: 1w (-1w) --> 20180918 (20180902)
16
+ =end
17
+
18
+ def transform_distance front, back
19
+ # Check Format of back: 201809 --> > 200.000
20
+ # 20180989 ---> 20.000.000
21
+ start_date = front.to_i < 20000000 ? Date.strptime(front.to_s,"%Y%m") : Date.strptime(front.to_s,"%Y%m%d")
22
+ nb = if back.to_i > 200000
23
+ back.to_i
24
+ elsif back[-1] == "w" && front.to_i > 20000000
25
+ start_date + (back.to_i * 7)
26
+ elsif back[-1] == "m" && front.to_i > 200000
27
+ start_date >> back.to_i
28
+ else
29
+ error "Wrong date #{back} required format YYYMM, YYYYMMDD ord {n}w or {n}m"
30
+ end
31
+ if nb.is_a?(Date)
32
+ if back[-1]=='w'
33
+ nb.strftime("%Y%m%d")
34
+ else
35
+ nb.strftime("%Y%m")
36
+ end
37
+ else
38
+ nb
39
+ end
40
+ end # def
41
+
42
+ def to_human
43
+ self.description
44
+ end
45
+
46
+ def calculate_spread_value( array_of_portfolio_values )
47
+ array_of_portfolio_values.map{|x| x.send yield }.sum if block_given?
48
+ end
49
+
50
+ def fake_portfolio_position( array_of_portfolio_values )
51
+ calculate_spread_value= ->( a_o_p_v, attribute ) do
52
+ a_o_p_v.map{|x| x.send attribute }.sum
53
+ end
54
+ ar=array_of_portfolio_values
55
+ IB::PortfolioValue.new contract: self,
56
+ average_cost: calculate_spread_value[ar, :average_cost],
57
+ market_price: calculate_spread_value[ar, :market_price],
58
+ market_value: calculate_spread_value[ar, :market_value],
59
+ unrealized_pnl: calculate_spread_value[ar, :unrealized_pnl],
60
+ realized_pnl: calculate_spread_value[ar, :realized_pnl],
61
+ position: 0
62
+
63
+ end
64
+
65
+
66
+
67
+ def serialize_rabbit
68
+ { "Spread" => serialize( :option, :trading_class ),
69
+ 'legs' => legs.map{ |y| y.serialize :option, :trading_class }, 'combo_legs' => combo_legs.map(&:serialize),
70
+ 'misc' => [description]
71
+ }
72
+ end
73
+
74
+ # adds a leg to any spread
75
+ #
76
+ # Parameter:
77
+ # contract: Will be verified. Contract.essential is added to legs-array
78
+ # action: :buy or :sell
79
+ # weight:
80
+ # ratio:
81
+ #
82
+ # Default: action: :buy, weight: 1
83
+
84
+ def add_leg contract, **leg_params
85
+ evaluated_contracts = []
86
+ nc = contract.verify.first.essential
87
+ # weigth = 1 --> sets Combo.side to buy and overwrites the action statement
88
+ # leg_params[:weight] = 1 unless leg_params.key?(:weight) || leg_params.key?(:ratio)
89
+ if nc.is_a?( IB::Contract) && nc.con_id.present?
90
+ the_leg= ComboLeg.new( nc.attributes.slice( :con_id, :exchange )
91
+ .merge( leg_params ))
92
+ self.combo_legs << the_leg
93
+ self.description = description + " added #{nc.to_human}" rescue "Spread: #{nc.to_human}"
94
+ self.legs << nc
95
+ end
96
+ self # return value to enable chaining
97
+
98
+
99
+ end
100
+
101
+ # removes the contract from the spread definition
102
+ #
103
+ def remove_leg contract
104
+ contract.verify do |c|
105
+ legs.delete_if { |x| x.con_id == c.con_id }
106
+ combo_legs.delete_if { |x| x.con_id == c.con_id }
107
+ self.description = description + " removed #{c.to_human}"
108
+ end
109
+ self
110
+ end
111
+
112
+
113
+ def essential
114
+ legs.each{ |x| x.essential }
115
+ self
116
+ end
117
+ def multiplier
118
+ (legs.map(&:multiplier).sum/legs.size).to_i
119
+ end
120
+
121
+ # provide a negative con_id
122
+ def con_id
123
+ -legs.map(&:con_id).sum
124
+ end
125
+
126
+
127
+ def non_guaranteed= x
128
+ super.merge combo_params: [ ['NonGuaranteed', x] ]
129
+ end
130
+
131
+
132
+ def non_guaranteed
133
+ combo_params['NonGuaranteed']
134
+ end
135
+ # optional: specify default order prarmeters for all spreads
136
+ # def order_requirements
137
+ # super.merge symbol: symbol
138
+ # end
139
+
140
+
141
+ def self.build_from_json container
142
+ read_leg = ->(a) do
143
+ IB::ComboLeg.new :con_id => a.read_int,
144
+ :ratio => a.read_int,
145
+ :action => a.read_string,
146
+ :exchange => a.read_string
147
+
148
+ end
149
+ object= self.new container['Spread'].read_contract
150
+ object.legs = container['legs'].map{|x| IB::Contract.build x.read_contract}
151
+ object.combo_legs = container['combo_legs'].map{ |x| read_leg[ x ] }
152
+ object.description = container['misc'].read_string
153
+ object
154
+
155
+ end
156
+ end
157
+
158
+
159
+ end
@@ -1,14 +1,20 @@
1
- require_relative 'contract'
1
+ #require_relative 'contract'
2
2
  module IB
3
3
  class Stock < IB::Contract
4
4
  validates_format_of :sec_type, :with => /\Astock\z/,
5
5
  :message => "should be a Stock"
6
+ validates_format_of :symbol, with: /\A.*\z/,
7
+ message: 'should not be blank'
6
8
  def default_attributes
7
9
  super.merge :sec_type => :stock, currency:'USD', exchange:'SMART'
8
10
  end
9
11
 
10
- def to_human
11
- att = [ symbol, currency, ( exchange == 'SMART' ? nil: exchange ), (primary_exchange.present? && !primary_exchange.empty? ? primary_exchange : nil)].compact
12
+ def to_human
13
+ att = [ symbol,
14
+ currency, ( exchange == 'SMART' ? nil: exchange ),
15
+ (primary_exchange.present? && !primary_exchange.empty? ? primary_exchange : nil),
16
+ @description.present? ? " (#{@description}) " : nil,
17
+ ].compact
12
18
  "<Stock: " + att.join(" ") + ">"
13
19
  end
14
20
 
@@ -1,4 +1,7 @@
1
1
  module IB
2
+ if defined?(Underlying)
3
+ puts "Underlying already a #{defined?(Underlying)}"
4
+ else
2
5
 
3
6
  # Calculated characteristics of underlying Contract (volatile)
4
7
  class Underlying < IB::Model
@@ -30,5 +33,5 @@ module IB
30
33
 
31
34
  end # class Underlying
32
35
  UnderComp = Underlying
33
-
36
+ end
34
37
  end # module IB
@@ -7,6 +7,13 @@ require 'ib/errors'
7
7
  require 'ib/constants'
8
8
  require 'ib/connection'
9
9
 
10
- require 'ib/models'
11
- require 'ib/messages'
12
-
10
+ # An external model- or database-driver provides the base class for models
11
+ # if the constant DB is defined
12
+ #
13
+ # basically IB::Model has to be assigned to the substitute base class
14
+ # the database-driver requires models and messages at the appropoate time
15
+ unless defined?(DB)
16
+ require 'ib/model'
17
+ require 'ib/models'
18
+ require 'ib/messages'
19
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ib-api
3
3
  version: !ruby/object:Gem::Version
4
- version: '972.0'
4
+ version: '972.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hartmut Bischoff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-22 00:00:00.000000000 Z
11
+ date: 2020-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -172,6 +172,7 @@ files:
172
172
  - lib/models/ib/order.rb
173
173
  - lib/models/ib/order_state.rb
174
174
  - lib/models/ib/portfolio_value.rb
175
+ - lib/models/ib/spread.rb
175
176
  - lib/models/ib/stock.rb
176
177
  - lib/models/ib/underlying.rb
177
178
  - lib/models/ib/vertical.rb