ib-api 972.0 → 972.1

Sign up to get free protection for your applications and to get access to all the features.
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