Class: IB::Account
- Inherits:
-
Object
- Object
- IB::Account
- Defined in:
- lib/ib/models/account.rb
Instance Method Summary collapse
- #account_data_scan(search_key, search_currency = nil) ⇒ Object
-
#cancel(order:) ⇒ Object
just a wrapper to the Gateway-cancel-order method.
-
#close(order:, contract: nil, reverse: false, **args_which_are_ignored) ⇒ Object
the action- and total_amount attributes of the assigned order are overwritten.
-
#complex_position(con_id) ⇒ Object
returns the contract definition of an complex portfolio-position detected in the account.
- #locate_contract(con_id) ⇒ Object
-
#locate_order(local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil) ⇒ Object
given any key of local_id, perm_id or order_ref and an optional status, which can be a string or a regexp ( status: /mitted/ matches Submitted and Presubmitted) the last associated Orderrecord is returned.
- #modify_order(local_id: nil, order_ref: nil, order: nil) ⇒ Object (also: #modify)
-
#organize_portfolio_positions(the_watchlists = IB::Gateway.current.active_watchlists) ⇒ Object
returns an hash where portfolio_positions are grouped into Watchlists.
-
#place_order(order:, contract: nil, auto_adjust: true, convert_size: true) ⇒ Object
(also: #place)
requires an IB::Order as parameter.
-
#preview(order:, contract: nil, **args_which_are_ignored) ⇒ Object
Submits a “WhatIf” Order.
Instance Method Details
#account_data_scan(search_key, search_currency = nil) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/ib/models/account.rb', line 6 def account_data_scan search_key, search_currency=nil if account_values.is_a? Array if search_currency.present? account_values.find_all{|x| x.key.match( search_key ) && x.currency == search_currency.upcase } else account_values.find_all{|x| x.key.match( search_key ) } end else # not tested!! if search_currency.present? account_values.where( ['key like %', search_key] ).where( currency: search_currency ) else # any currency account_values.where( ['key like %', search_key] ) end end end |
#cancel(order:) ⇒ Object
just a wrapper to the Gateway-cancel-order method
307 308 309 |
# File 'lib/ib/models/account.rb', line 307 def cancel order: Gateway.current.cancel_order order end |
#close(order:, contract: nil, reverse: false, **args_which_are_ignored) ⇒ Object
the action- and total_amount attributes of the assigned order are overwritten.
if a ratio-value (0 ..1) is specified in order.total_quantity only a fraction of the position is closed. Other values are silently ignored
if reverse is specified, the opposide position is established. Any value in total_quantity is overwritten
returns the order transmitted
raises an IB::Error if no PortfolioValues have been loaded to the IB::Acoount
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/ib/models/account.rb', line 274 def close order:, contract: nil, reverse: false, **args_which_are_ignored error "must only be called after initializing portfolio_values " if portfolio_values.blank? contract_size = ->(c) do # note: portfolio_value.position is either positiv or negativ if c.con_id <0 # Spread p = portfolio_values.detect{|p| p.contract.con_id ==c.legs.first.con_id} &.position.to_i p/ c.combo_legs.first.weight unless p.to_i.zero? else portfolio_values.detect{|x| x.contract.con_id == c.con_id} &.position.to_i # nil.to_i -->0 end end contract &.verify{|c| order.contract = c} # if contract is specified: don't touch the parameter, get a new object . error "Cannot transmit the order – No Contract given " unless order.contract.is_a?(IB::Contract) the_quantity = if reverse -contract_size[order.contract] * 2 elsif order.total_quantity.abs < 1 && !order.total_quantity.zero? -contract_size[order.contract] * order.total_quantity.abs else -contract_size[order.contract] end if the_quantity.zero? logger.info{ "Cannot close #{order.contract.to_human} - no position detected"} else order.total_quantity = the_quantity order.action = nil order.local_id = nil # in any case, close is a new order logger.info { "Order modified to close, reduce or revese position: #{order.to_human}" } place order: order, convert_size: true end end |
#complex_position(con_id) ⇒ Object
returns the contract definition of an complex portfolio-position detected in the account
338 339 340 341 |
# File 'lib/ib/models/account.rb', line 338 def complex_position con_id con_id = con_id.con_id if con_id.is_a?(IB::Contract) focuses.map{|x,y| y.detect{|x,y| x.con_id.to_i== con_id.to_i} }.compact.flatten.first end |
#locate_contract(con_id) ⇒ Object
333 334 335 |
# File 'lib/ib/models/account.rb', line 333 def locate_contract con_id contracts.detect{|x| x.con_id.to_i == con_id.to_i } end |
#locate_order(local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil) ⇒ Object
given any key of local_id, perm_id or order_ref and an optional status, which can be a string or a regexp ( status: /mitted/ matches Submitted and Presubmitted) the last associated Orderrecord is returned.
Thus if several Orders are placed with the same order_ref, the active one is returned
(If multible keys are specified, local_id preceeds perm_id)
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/ib/models/account.rb', line 36 def locate_order local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil search_option = [ local_id.present? ? [:local_id , local_id] : nil , perm_id.present? ? [:perm_id, perm_id] : nil, order_ref.present? ? [:order_ref , order_ref ] : nil ].compact.first matched_items = if search_option.nil? orders else orders.find_all{|x| x[search_option.first].to_i == search_option.last.to_i } end if contract.present? if contract.con_id.zero? && !contract.is_a?( IB::Bag ) contract = contract.verify.first end matched_items = matched_items.find_all{|o| o.contract.essential == contract.essential } elsif con_id.present? matched_items = matched_items.find_all{|o| o.contract.con_id == con_id } end if status.present? status = Regexp.new(status) unless status.is_a? Regexp matched_items.detect{|x| x.order_state.status =~ status } else matched_items.last # return the last item end end |
#modify_order(local_id: nil, order_ref: nil, order: nil) ⇒ Object Also known as: modify
217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/ib/models/account.rb', line 217 def modify_order local_id: nil, order_ref: nil, order:nil result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } } order ||= locate_order( local_id: local_id, status: /ubmitted/ , order_ref: order_ref ) if order.is_a? IB::Order order.modify else error "No suitable IB::Order provided/detected. Instead: #{order.inspect}" end end |
#organize_portfolio_positions(the_watchlists = IB::Gateway.current.active_watchlists) ⇒ Object
returns an hash where portfolio_positions are grouped into Watchlists.
Watchlist => [ contract => [ portfoliopositon] , … ] ]
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/ib/models/account.rb', line 315 def organize_portfolio_positions the_watchlists= IB::Gateway.current.active_watchlists the_watchlists = [ the_watchlists ] unless the_watchlists.is_a?(Array) self.focuses = portfolio_values.map do | pw | z= the_watchlists.map do | w | ref_con_id = pw.contract.con_id watchlist_contract = w.find do |c| c.is_a?(IB::Bag) ? c.combo_legs.map(&:con_id).include?(ref_con_id) : c.con_id == ref_con_id end rescue nil watchlist_contract.present? ? [w,watchlist_contract] : nil end.compact z.empty? ? [ IB::Symbols::Unspecified, pw.contract, pw ] : z.first << pw end.group_by{|a,_,_| a }.map{|x,y|[x, y.map{|_,d,e|[d,e]}.group_by{|e,_| e}.map{|f,z| [f, z.map(&:last)]} ] }.to_h # group:by --> [a,b,c] .group_by {|_g,_| g} --->{ a => [a,b,c] } # group_by+map --> removes "a" from the resulting array end |
#place_order(order:, contract: nil, auto_adjust: true, convert_size: true) ⇒ Object Also known as: place
requires an IB::Order as parameter.
If attached, the associated IB::Contract is used to specify the tws-command
The associated Contract overtakes the specified (as parameter)
auto_adjust: Limit- and Aux-Prices are adjusted to Min-Tick
convert_size: The action-attribute (:buy :sell) is associated according the content of :total_quantity.
The parameter «order» is modified!
It can further used to modify and eventually cancel
Example
j36 = IB::Stock.new symbol: 'J36', exchange: 'SGX'
order = IB::Limit.order size: 100, price: 65.5
g = IB::Gateway.current.clients.last
g.preview contract: j36, order: order
=> {:init_margin=>0.10864874e6,
:maint_margin=>0.9704137e5,
:equity_with_loan=>0.97877973e6,
:commission=>0.524e1,
:commission_currency=>"USD",
:warning=>""
the_local_id = g.place order: order
=> 67 # returns local_id
order.contract # updated contract-record
=> #<IB::Contract:0x00000000013c94b0 @attributes={:con_id=>9534669,
:exchange=>"SGX",
:right=>"",
:include_expired=>false}>
order.limit_price = 65 # set new price
g.modify order: order # and transmit
=> 67 # returns local_id
g.locate_order( local_id: the_local_id )
=> returns the assigned order-record for inspection
g.cancel order: order
# logger output: 05:17:11 Cancelling 65 New #250/ from 3000/DU167349>
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/ib/models/account.rb', line 115 def place_order order:, contract: nil, auto_adjust: true, convert_size: true # adjust the orderprice to min-tick result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } } #·IB::Symbols are always qualified. They carry a description-field qualified_contract = ->(c) { c.is_a?(IB::Contract) && ( c.description.present? || !c.con_id.to_i.zero? || (c.con_id.to_i <0 && c.sec_type == :bag )) } order.contract ||= if qualified_contract[ contract ] contract else contract.verify.first end error "No valid contract given" unless order.contract.is_a?(IB::Contract) ## sending of plain vanilla IB::Bags will fail using account.place, unless a (negative) con-id is provided! error "place order: ContractVerification failed. No con_id assigned" unless qualified_contract[order.contract] ib = IB::Connection.current wrong_order = nil the_local_id = nil q = Queue.new ### Handle Error messages ### Default action: raise IB::Transmission Error sa = ib.subscribe( :Alert ) do | msg | # puts "local_id: #{the_local_id}"a puts msg.inspect if msg.error_id == the_local_id if [ 110, # The price does not confirm to the minimum price variation for this contract 201, # Order rejected, No Trading permissions 203, # Security is not allowed for trading 325, # Disretionary Orders are not supported for ths combination of oerder-type and exchange 355, # Order size does not conform to market rule 361, 362, 363, 364, # invalid trigger or stop-price 388, # Order size x is smaller than the minimum required size of yy. ].include? msg.code wrong_order = msg. ib.logger.error msg. q.close # closing the queue indicates that no order was transmitted end end end sb = ib.subscribe( :OpenOrder ){|m| q << m.order if m.order.local_id.to_i == the_local_id.to_i } # modify order (parameter) order.account = account # assign the account_id to the account-field of IB::Order self.orders.update_or_create order, :order_ref order.auto_adjust if auto_adjust # /defined in file order_handling.rb if convert_size order.action = order.total_quantity.to_i < 0 ? :sell : :buy unless order.action == :sell logger.info{ "Converted ordesize to #{order.total_quantity} and triggered a #{order.action} order"} if order.total_quantity.to_i < 0 order.total_quantity = order.total_quantity.to_i.abs end # apply non_guarenteed and other stuff bound to the contract to order. order.attributes.merge! order.contract.order_requirements unless order.contract.order_requirements.blank? # con_id and exchange fully qualify a contract, no need to transmit other data # if no contract is passed to order.place, order.contract is used for placement the_contract = order.contract.con_id.to_i >0 ? Contract.new( con_id: order.contract.con_id, exchange: order.contract.exchange) : nil the_local_id = order.place the_contract # return the local_id # if transmit is false, just include the local_id in the order-record Thread.new{ if order.transmit || order.what_if then sleep 1 else sleep 0.001 end ; q.close } tws_answer = q.pop ib.unsubscribe sa ib.unsubscribe sb if q.closed? if wrong_order.present? raise IB::SymbolError, wrong_order elsif the_local_id.present? order.local_id = the_local_id else error " #{order.to_human} is not transmitted properly", :symbol end else order=tws_answer # return order-record received from tws end the_local_id # return_value end |
#preview(order:, contract: nil, **args_which_are_ignored) ⇒ Object
Submits a “WhatIf” Order
Returns the order_state.forcast
The order received from the TWS is kept in account.orders
Raises IB::SymbolError if the Order could not be placed properly
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/ib/models/account.rb', line 242 def preview order:, contract: nil, **args_which_are_ignored # to_do: use a copy of order instead of temporary setting order.what_if q = Queue.new ib = IB::Connection.current the_local_id = nil req = ib.subscribe( :OpenOrder ){|m| q << m.order if m.order.local_id.to_i == the_local_id.to_i } result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } } order.what_if = true order.account = account the_local_id = order.place contract Thread.new{ sleep 1 ; q.close } returned_order = q.pop ib.unsubscribe req order.what_if = false # reset what_if flag order.local_id = nil # reset local_id to enable re-using the order-object for placing raise IB::SymbolError,"(Preview-) #{order.to_human} is not transmitted properly" if q.closed? returned_order.order_state.forcast # return_value end |