ib-extensions 1.0

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +6 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +9 -0
  7. data/Gemfile.lock +112 -0
  8. data/Guardfile +24 -0
  9. data/README.md +99 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +96 -0
  12. data/bin/console.yml +3 -0
  13. data/bin/gateway.rb +97 -0
  14. data/bin/setup +8 -0
  15. data/changelog.md +31 -0
  16. data/examples/cancel_orders +74 -0
  17. data/examples/eod +35 -0
  18. data/examples/input.rb +475 -0
  19. data/examples/market_price +57 -0
  20. data/examples/option_chain +67 -0
  21. data/examples/place_and_modify_order +162 -0
  22. data/examples/place_bracket_order +62 -0
  23. data/examples/place_butterfly_order +104 -0
  24. data/examples/place_combo_order +70 -0
  25. data/examples/place_limit_order +82 -0
  26. data/examples/place_the_limit_order +145 -0
  27. data/examples/volatility_research +139 -0
  28. data/examples/what_if_order +90 -0
  29. data/ib-extensions.gemspec +37 -0
  30. data/lib/ib-gateway.rb +5 -0
  31. data/lib/ib/alerts/base-alert.rb +128 -0
  32. data/lib/ib/alerts/gateway-alerts.rb +15 -0
  33. data/lib/ib/alerts/order-alerts.rb +68 -0
  34. data/lib/ib/eod.rb +152 -0
  35. data/lib/ib/extensions.rb +9 -0
  36. data/lib/ib/extensions/contract.rb +37 -0
  37. data/lib/ib/extensions/version.rb +5 -0
  38. data/lib/ib/flex.rb +150 -0
  39. data/lib/ib/gateway.rb +425 -0
  40. data/lib/ib/gateway/account-infos.rb +115 -0
  41. data/lib/ib/gateway/order-handling.rb +150 -0
  42. data/lib/ib/market-price.rb +134 -0
  43. data/lib/ib/models/account.rb +329 -0
  44. data/lib/ib/models/spread.rb +159 -0
  45. data/lib/ib/option-chain.rb +198 -0
  46. data/lib/ib/option-greeks.rb +88 -0
  47. data/lib/ib/order-prototypes.rb +110 -0
  48. data/lib/ib/order_prototypes/abstract.rb +67 -0
  49. data/lib/ib/order_prototypes/combo.rb +46 -0
  50. data/lib/ib/order_prototypes/forex.rb +40 -0
  51. data/lib/ib/order_prototypes/limit.rb +177 -0
  52. data/lib/ib/order_prototypes/market.rb +116 -0
  53. data/lib/ib/order_prototypes/pegged.rb +173 -0
  54. data/lib/ib/order_prototypes/premarket.rb +31 -0
  55. data/lib/ib/order_prototypes/stop.rb +202 -0
  56. data/lib/ib/order_prototypes/volatility.rb +39 -0
  57. data/lib/ib/spread-prototypes.rb +62 -0
  58. data/lib/ib/spread_prototypes/butterfly.rb +79 -0
  59. data/lib/ib/spread_prototypes/calendar.rb +85 -0
  60. data/lib/ib/spread_prototypes/stock-spread.rb +48 -0
  61. data/lib/ib/spread_prototypes/straddle.rb +75 -0
  62. data/lib/ib/spread_prototypes/strangle.rb +96 -0
  63. data/lib/ib/spread_prototypes/vertical.rb +84 -0
  64. data/lib/ib/verify.rb +226 -0
  65. metadata +206 -0
@@ -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
@@ -0,0 +1,198 @@
1
+ require 'ib/verify'
2
+ require 'ib/market-price'
3
+ module IB
4
+ # define a custom ErrorClass which can be fired if a verification fails
5
+ class VerifyError < StandardError
6
+ end
7
+
8
+ class Contract
9
+
10
+
11
+
12
+ # returns the Option Chain of the contract (if available)
13
+ #
14
+ ## parameters
15
+ ### right:: :call, :put, :straddle
16
+ ### ref_price:: :request or a numeric value
17
+ ### sort:: :strike, :expiry
18
+ ### exchange:: List of Exchanges to be queried (Blank for all available Exchanges)
19
+ def option_chain ref_price: :request, right: :put, sort: :strike, exchange: ''
20
+
21
+ ib = Connection.current
22
+
23
+ ## Enable Cashing of Definition-Matrix
24
+ @option_chain_definition ||= []
25
+
26
+ my_req = nil; finalize= false
27
+
28
+ # -----------------------------------------------------------------------------------------------------
29
+ # get OptionChainDefinition from IB ( instantiate cashed Hash )
30
+ if @option_chain_definition.blank?
31
+ sub_sdop = ib.subscribe( :SecurityDefinitionOptionParameterEnd ) { |msg| finalize = true if msg.request_id == my_req }
32
+ sub_ocd = ib.subscribe( :OptionChainDefinition ) do | msg |
33
+ if msg.request_id == my_req
34
+ message = msg.data
35
+ # transfer the first record to @option_chain_definition
36
+ if @option_chain_definition.blank?
37
+ @option_chain_definition = msg.data
38
+
39
+ end
40
+ # override @option_chain_definition if a decent combination of attributes is met
41
+ # us- options: use the smart dataset
42
+ # other options: prefer options of the default trading class
43
+ if message[:exchange] == 'SMART'
44
+ @option_chain_definition = msg.data
45
+ finalize = true
46
+ end
47
+ if @option_chain_definition.blank? && message[:trading_class] == symbol
48
+ @option_chain_definition = msg.data
49
+ end
50
+ end
51
+ end
52
+
53
+ c = verify.first # ensure a complete set of attributes
54
+ my_req = ib.send_message :RequestOptionChainDefinition, con_id: c.con_id,
55
+ symbol: c.symbol,
56
+ exchange: c.sec_type == :future ? c.exchange : "", # BOX,CBOE',
57
+ sec_type: c[:sec_type]
58
+
59
+ Thread.new do
60
+
61
+ Timeout::timeout(1, IB::TransmissionError,"OptionChainDefinition not received" ) do
62
+ loop{ sleep 0.1; break if finalize }
63
+ end
64
+ ib.unsubscribe sub_sdop , sub_ocd
65
+ end.join
66
+ else
67
+ Connection.logger.error { "#{to_human} : using cached data" }
68
+ end
69
+
70
+ # -----------------------------------------------------------------------------------------------------
71
+ # select values and assign to options
72
+ #
73
+ unless @option_chain_definition.blank?
74
+ requested_strikes = if block_given?
75
+ ref_price = market_price if ref_price == :request
76
+ if ref_price.nil?
77
+ ref_price = @option_chain_definition[:strikes].min +
78
+ ( @option_chain_definition[:strikes].max -
79
+ @option_chain_definition[:strikes].min ) / 2
80
+ Connection.logger.error{ "#{to_human} :: market price not set – using midpoint of available strikes instead: #{ref_price.to_f}" }
81
+ end
82
+ atm_strike = @option_chain_definition[:strikes].min_by { |x| (x - ref_price).abs }
83
+ the_grouped_strikes = @option_chain_definition[:strikes].group_by{|e| e <=> atm_strike}
84
+ begin
85
+ the_strikes = yield the_grouped_strikes
86
+ the_strikes.unshift atm_strike unless the_strikes.first == atm_strike # the first item is the atm-strike
87
+ the_strikes
88
+ rescue
89
+ Connection.logger.error "#{to_human} :: not enough strikes :#{@option_chain_definition[:strikes].map(&:to_f).join(',')} "
90
+ []
91
+ end
92
+ else
93
+ @option_chain_definition[:strikes]
94
+ end
95
+
96
+ # third Friday of a month
97
+ monthly_expirations = @option_chain_definition[:expirations].find_all{|y| (15..21).include? y.day }
98
+ # puts @option_chain_definition.inspect
99
+ option_prototype = -> ( ltd, strike ) do
100
+ IB::Option.new( symbol: symbol,
101
+ exchange: @option_chain_definition[:exchange],
102
+ trading_class: @option_chain_definition[:trading_class],
103
+ multiplier: @option_chain_definition[:multiplier],
104
+ currency: currency,
105
+ last_trading_day: ltd,
106
+ strike: strike,
107
+ right: right )
108
+ end
109
+ options_by_expiry = -> ( schema ) do
110
+ # Array: [ yymm -> Options] prepares for the correct conversion to a Hash
111
+ Hash[ monthly_expirations.map do | l_t_d |
112
+ [ l_t_d.strftime('%y%m').to_i , schema.map{ | strike | option_prototype[ l_t_d, strike ]}.compact ]
113
+ end ] # by Hash[ ]
114
+ end
115
+ options_by_strike = -> ( schema ) do
116
+ Hash[ schema.map do | strike |
117
+ [ strike , monthly_expirations.map{ | l_t_d | option_prototype[ l_t_d, strike ]}.compact ]
118
+ end ] # by Hash[ ]
119
+ end
120
+
121
+ if sort == :strike
122
+ options_by_strike[ requested_strikes ]
123
+ else
124
+ options_by_expiry[ requested_strikes ]
125
+ end
126
+ else
127
+ Connection.logger.error "#{to_human} ::No Options available"
128
+ nil # return_value
129
+ end
130
+ end # def
131
+
132
+ # return a set of AtTheMoneyOptions
133
+ def atm_options ref_price: :request, right: :put
134
+ option_chain( right: right, ref_price: ref_price, sort: :expiry) do | chain |
135
+ chain[0]
136
+ end
137
+
138
+
139
+ end
140
+
141
+ # return InTheMoneyOptions
142
+ def itm_options count: 5, right: :put, ref_price: :request, sort: :strike
143
+ option_chain( right: right, ref_price: ref_price, sort: sort ) do | chain |
144
+ if right == :put
145
+ above_market_price_strikes = chain[1][0..count-1]
146
+ else
147
+ below_market_price_strikes = chain[-1][-count..-1].reverse
148
+ end # branch
149
+ end
150
+ end # def
151
+
152
+ # return OutOfTheMoneyOptions
153
+ def otm_options count: 5, right: :put, ref_price: :request, sort: :strike
154
+ option_chain( right: right, ref_price: ref_price, sort: sort ) do | chain |
155
+ if right == :put
156
+ # puts "Chain: #{chain}"
157
+ below_market_price_strikes = chain[-1][-count..-1].reverse
158
+ else
159
+ above_market_price_strikes = chain[1][0..count-1]
160
+ end
161
+ end
162
+ end
163
+
164
+
165
+ def associate_ticdata
166
+
167
+ tws= IB::Connection.current # get the initialized ib-ruby instance
168
+ the_id = nil
169
+ finalize= false
170
+ # switch to delayed data
171
+ tws.send_message :RequestMarketDataType, :market_data_type => :delayed
172
+
173
+ s_id = tws.subscribe(:TickSnapshotEnd) { |msg| finalize = true if msg.ticker_id == the_id }
174
+
175
+ sub_id = tws.subscribe(:TickPrice, :TickSize, :TickGeneric, :TickOption) do |msg|
176
+ self.bars << msg.the_data if msg.ticker_id == the_id
177
+ end
178
+
179
+ # initialize »the_id« that is used to identify the received tick messages
180
+ # by firing the market data request
181
+ the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
182
+
183
+ #keep the method-call running until the request finished
184
+ #and cancel subscriptions to the message handler.
185
+ Thread.new do
186
+ i=0; loop{ i+=1; sleep 0.1; break if finalize || i > 1000 }
187
+ tws.unsubscribe sub_id
188
+ tws.unsubscribe s_id
189
+ #puts "#{symbol} data gathered"
190
+ end # method returns the (running) thread
191
+
192
+ end # def
193
+ end # class
194
+
195
+
196
+
197
+
198
+ end # module
@@ -0,0 +1,88 @@
1
+ module IB
2
+
3
+
4
+
5
+ class Option
6
+ # Ask for the Greeks and implied Vola
7
+ #
8
+ # The result can be customized by a provided block.
9
+ #
10
+ # IB::Symbols::Options.aapl.greeks{ |x| x }
11
+ # -> {"bid"=>0.10142e3, "ask"=>0.10144e3, "last"=>0.10142e3, "close"=>0.10172e3}
12
+ #
13
+ # Possible values for Parameter :what --> :all :model, :bid, :ask, :bidask, :last
14
+ #
15
+ def request_greeks delayed: true, what: :model, thread: false
16
+
17
+ tws= Connection.current # get the initialized ib-ruby instance
18
+ # define requested tick-attributes
19
+ request_data_type = IB::MARKET_DATA_TYPES.rassoc( delayed ? :frozen_delayed : :frozen ).first
20
+ # possible types = [ [ :delayed_model_option , :model_option ] , [:delayed_last_option , :last_option ],
21
+ # [ :delayed_bid_option , :bid_option ], [ :delayed_ask_option , :ask_option ] ]
22
+ tws.send_message :RequestMarketDataType, :market_data_type => request_data_type
23
+ tickdata = []
24
+
25
+ self.greek = OptionDetail.new if greek.nil?
26
+ greek.updated_at = Time.now
27
+
28
+ #keep the method-call running until the request finished
29
+ #and cancel subscriptions to the message handler
30
+ # method returns the (running) thread
31
+ th = Thread.new do
32
+ the_id = nil
33
+ finalize= false
34
+ # subscribe to TickPrices
35
+ s_id = tws.subscribe(:TickSnapshotEnd) { |msg| finalize = true if msg.ticker_id == the_id }
36
+ e_id = tws.subscribe(:Alert){|x| finalize = true if [200,353].include?( x.code) && x.error_id == the_id }
37
+ # TWS Error 200: No security definition has been found for the request
38
+ # TWS Error 354: Requested market data is not subscribed.
39
+
40
+ sub_id = tws.subscribe(:TickOption ) do |msg| #, :TickSize, :TickGeneric do |msg|
41
+ if msg.ticker_id == the_id && tickdata.is_a?(Array) # do nothing if tickdata have already gathered
42
+ case msg.type
43
+ when /ask/
44
+ greek.ask_price = msg.option_price unless msg.option_price.nil?
45
+ tickdata << msg if [ :all, :ask, :bidask ].include?( what )
46
+
47
+ when /bid/
48
+ greek.bid_price = msg.option_price unless msg.option_price.nil?
49
+ tickdata << msg if [ :all, :bid, :bidask ].include?( what )
50
+ when /last/
51
+ tickdata << msg if msg.type =~ /last/
52
+ when /model/
53
+ # transfer attributs from TickOption to OptionDetail
54
+ bf =[ :option_price, :implied_volatility, :under_price, :pv_dividend ]
55
+ (bf + msg.greeks.keys).each{ |a| greek.send( a.to_s+"=", msg.send( a)) }
56
+ tickdata << msg if [ :all, :model ].include?( what )
57
+ end
58
+ tickdata = tickdata &.first unless [:bidask, :all].include? what
59
+ finalize = true if tickdata.is_a?(IB::Messages::Incoming::TickOption) || (tickdata.size == 2 && what== :bidask) || (tickdata.size == 4 && what == :all)
60
+ end
61
+ end # if sub_id
62
+
63
+ # initialize »the_id« that is used to identify the received tick messages
64
+ # by firing the market data request
65
+ the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
66
+
67
+ begin
68
+ # todo implement config-feature to set timeout in configuration (DRY-Feature)
69
+ Timeout::timeout(5) do # max 5 sec.
70
+ loop{ break if finalize ; sleep 0.05 }
71
+ # reduce :close_price delayed_close to close a.s.o
72
+ self.misc = tickdata if thread # store internally if in thread modus
73
+ end
74
+ rescue Timeout::Error
75
+ Connection.logger.info{ "#{to_human} --> No Marketdata received " }
76
+ end
77
+ tws.unsubscribe sub_id, s_id, e_id
78
+ end # thread
79
+ if thread
80
+ th # return thread
81
+ else
82
+ th.join
83
+ tickdata # return
84
+ end
85
+ end #
86
+
87
+ end
88
+ end
@@ -0,0 +1,110 @@
1
+ # These modules are used to facilitate referencing of most common Ordertypes
2
+
3
+ module IB
4
+ module OrderPrototype
5
+
6
+
7
+ #The Module OrderPrototypes provides a wrapper to define even complex ordertypes.
8
+ #
9
+ #The Order is build by
10
+ #
11
+ # IB::<OrderPrototye>.order
12
+ #
13
+ #A description is available through
14
+ #
15
+ # puts IB::<OrderPrototype>.summary
16
+ #
17
+ #Nessesary and optional arguments are printed by
18
+ #
19
+ # puts IB::<OrderPrototype>.parameters
20
+ #
21
+ #Orders can be setup interactively
22
+ #
23
+ # > d = Discretionary.order
24
+ # Traceback (most recent call last): (..)
25
+ # IB::ArgumentError (IB::Discretionary.order -> A necessary field is missing:
26
+ # action: --> {"B"=>:buy, "S"=>:sell, "T"=>:short, "X"=>:short_exempt})
27
+ # > d = Discretionary.order action: :buy
28
+ # IB::ArgumentError (IB::Discretionary.order -> A necessary field is missing:
29
+ # total_quantity: --> also aliased as :size)
30
+ # > d = Discretionary.order action: :buy, size: 100
31
+ # Traceback (most recent call last):
32
+ # IB::ArgumentError (IB::Discretionary.order -> A necessary field is missing: limit_price: --> decimal)
33
+ #
34
+ #
35
+ #
36
+ #Prototypes are defined as module. They extend OrderPrototype and establish singleton methods, which
37
+ #can adress and extend similar methods from OrderPrototype.
38
+ #
39
+ #
40
+
41
+
42
+
43
+ def order **fields
44
+
45
+ # special treatment of size: positive numbers --> buy order, negative: sell
46
+ if fields[:size].present? && fields[:action].blank?
47
+ error "Size = 0 is not possible" if fields[:size].zero?
48
+ fields[:action] = fields[:size] >0 ? :buy : :sell
49
+ fields[:size] = fields[:size].abs
50
+ end
51
+ # change aliases to the original. We are modifying the fields-hash.
52
+ fields.keys.each{|x| fields[aliases.key(x)] = fields.delete(x) if aliases.has_value?(x)}
53
+ # inlcude defaults (arguments override defaults)
54
+ the_arguments = defaults.merge fields
55
+ # check if requirements are fullfilled
56
+ necessary = requirements.keys.detect{|y| the_arguments[y].nil?}
57
+ if necessary.present?
58
+ msg =self.name + ".order -> A necessary field is missing: #{necessary}: --> #{requirements[necessary]}"
59
+ error msg, :args, nil
60
+ end
61
+ if alternative_parameters.present?
62
+ unless ( alternative_parameters.keys & the_arguments.keys ).size == 1
63
+ msg =self.name + ".order -> One of the alternative fields needs to be specified: \n\t:" +
64
+ "#{alternative_parameters.map{|x| x.join ' => '}.join(" or \n\t:")}"
65
+ error msg, :args, nil
66
+ end
67
+ end
68
+
69
+ # initialise order with given attributes
70
+ IB::Order.new the_arguments
71
+ end
72
+
73
+ def alternative_parameters
74
+ {}
75
+ end
76
+ def requirements
77
+ { action: IB::VALUES[:side], total_quantity: 'also aliased as :size' }
78
+ end
79
+
80
+ def defaults
81
+ { tif: :good_till_cancelled }
82
+ end
83
+
84
+ def optional
85
+ { account: 'Account(number) to trade on' }
86
+ end
87
+
88
+ def aliases
89
+ { total_quantity: :size }
90
+ end
91
+
92
+ def parameters
93
+ the_output = ->(var){ var.map{|x| x.join(" --> ") }.join("\n\t: ")}
94
+
95
+ "Required : " + the_output[requirements] + "\n --------------- \n" +
96
+ "Optional : " + the_output[optional] + "\n --------------- \n"
97
+
98
+ end
99
+
100
+ end
101
+ end
102
+
103
+ require 'ib/order_prototypes/forex'
104
+ require 'ib/order_prototypes/market'
105
+ require 'ib/order_prototypes/limit'
106
+ require 'ib/order_prototypes/stop'
107
+ require 'ib/order_prototypes/volatility'
108
+ require 'ib/order_prototypes/premarket'
109
+ require 'ib/order_prototypes/pegged'
110
+ require 'ib/order_prototypes/combo'