ib-extensions 1.0

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