ib-extensions 1.0 → 1.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 +4 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +33 -34
- data/README.md +12 -7
- data/bin/console +3 -5
- data/bin/{gateway.rb → gateway} +13 -4
- data/changelog.md +6 -0
- data/ib-extensions.gemspec +1 -0
- data/lib/ib/alerts/base-alert.rb +3 -1
- data/lib/ib/alerts/order-alerts.rb +5 -0
- data/lib/ib/eod.rb +19 -23
- data/lib/ib/extensions.rb +1 -0
- data/lib/ib/extensions/version.rb +1 -1
- data/lib/ib/gateway.rb +77 -63
- data/lib/ib/gateway/account-infos.rb +1 -2
- data/lib/ib/gateway/order-handling.rb +121 -126
- data/lib/ib/market-price.rb +2 -2
- data/lib/ib/models/account.rb +63 -56
- data/lib/ib/models/option.rb +20 -0
- data/lib/ib/option-chain.rb +180 -182
- data/lib/ib/order-prototypes.rb +1 -1
- data/lib/ib/spread-prototypes.rb +2 -2
- data/lib/ib/spread_prototypes/stock-spread.rb +1 -1
- data/lib/ib/verify.rb +192 -222
- metadata +19 -18
- data/examples/cancel_orders +0 -74
- data/examples/eod +0 -35
- data/examples/input.rb +0 -475
- data/examples/market_price +0 -57
- data/examples/option_chain +0 -67
- data/examples/place_and_modify_order +0 -162
- data/examples/place_bracket_order +0 -62
- data/examples/place_butterfly_order +0 -104
- data/examples/place_combo_order +0 -70
- data/examples/place_limit_order +0 -82
- data/examples/place_the_limit_order +0 -145
- data/examples/volatility_research +0 -139
- data/examples/what_if_order +0 -90
- data/lib/ib/models/spread.rb +0 -159
| @@ -30,7 +30,6 @@ raises an IB::Error if less then 100 items are recieved- | |
| 30 30 | 
             
            =end
         | 
| 31 31 | 
             
            	def get_account_data  *accounts,  watchlists: []
         | 
| 32 32 |  | 
| 33 | 
            -
            		logger.progname = 'Gateway#get_account_data'
         | 
| 34 33 |  | 
| 35 34 | 
             
            		@account_data_subscription ||=   subscribe_account_updates
         | 
| 36 35 |  | 
| @@ -106,7 +105,7 @@ raises an IB::Error if less then 100 items are recieved- | |
| 106 105 | 
             
            #								.portfolio_values
         | 
| 107 106 | 
             
            #								.update_or_create( msg.portfolio_value ) { :account } 
         | 
| 108 107 | 
             
            						logger.debug { "#{ account.account } :: #{ msg.contract.to_human }" }
         | 
| 109 | 
            -
             | 
| 108 | 
            +
                    end # case
         | 
| 110 109 | 
             
            			end # account_data 
         | 
| 111 110 | 
             
            		end # subscribe
         | 
| 112 111 | 
             
            	end  # def 
         | 
| @@ -4,109 +4,105 @@ module OrderHandling | |
| 4 4 | 
             
            UpdateOrderDependingObject
         | 
| 5 5 |  | 
| 6 6 | 
             
            Generic method which enables operations on the order-Object,
         | 
| 7 | 
            -
            which is associated to OrderState-, Execution-, CommissionReport- | 
| 7 | 
            +
            which is associated to OrderState-, Execution-, CommissionReport-
         | 
| 8 8 | 
             
            events fired by the tws.
         | 
| 9 9 | 
             
            The order is identified by local_id and perm_id
         | 
| 10 10 |  | 
| 11 11 | 
             
            Everything is carried out in a mutex-synchonized environment
         | 
| 12 12 | 
             
            =end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 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 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
            # | 
| 92 | 
            -
            #  | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
            		tws.unsubscribe subscription
         | 
| 107 | 
            -
            	end
         | 
| 108 | 
            -
             | 
| 109 | 
            -
            	alias update_orders request_open_orders 
         | 
| 13 | 
            +
              def update_order_dependent_object order_dependent_object  # :nodoc:
         | 
| 14 | 
            +
                account_data  do  | a | 
         | 
| 15 | 
            +
                  order = if order_dependent_object.local_id.present?
         | 
| 16 | 
            +
                            a.locate_order( :local_id => order_dependent_object.local_id)
         | 
| 17 | 
            +
                          else
         | 
| 18 | 
            +
                            a.locate_order( :perm_id => order_dependent_object.perm_id)
         | 
| 19 | 
            +
                          end
         | 
| 20 | 
            +
                  yield order if order.present?
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
              def initialize_order_handling
         | 
| 24 | 
            +
                tws.subscribe( :CommissionReport, :ExecutionData, :OrderStatus, :OpenOrder, :OpenOrderEnd, :NextValidId ) do |msg| 
         | 
| 25 | 
            +
                  case msg
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  when IB::Messages::Incoming::CommissionReport
         | 
| 28 | 
            +
                    # Commission-Reports are not assigned to a order -
         | 
| 29 | 
            +
                    logger.info "CommissionReport -------#{msg.exec_id} :...:C: #{msg.commission} :...:P/L: #{msg.realized_pnl}-"
         | 
| 30 | 
            +
                  when IB::Messages::Incoming::OrderStatus
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    # The order-state only links via local_id and perm_id to orders.
         | 
| 33 | 
            +
                    # There is no reference to a contract or an account
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    success = update_order_dependent_object( msg.order_state) do |o|
         | 
| 36 | 
            +
                      o.order_states.update_or_create msg.order_state, :status
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    logger.info {  "Order State not assigned-- #{msg.order_state.to_human} ----------" } if success.nil?
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  when IB::Messages::Incoming::OpenOrder
         | 
| 42 | 
            +
                    ## todo --> handling of bags --> no con_id
         | 
| 43 | 
            +
                    account_data(msg.order.account) do | this_account |
         | 
| 44 | 
            +
                      # first update the contracts
         | 
| 45 | 
            +
                      # make open order equal to IB::Spreads (include negativ con_id)
         | 
| 46 | 
            +
                      msg.contract[:con_id] = -msg.contract.combo_legs.map{|y| y.con_id}.sum  if msg.contract.is_a? IB::Bag
         | 
| 47 | 
            +
                      msg.contract.orders.update_or_create msg.order, :local_id
         | 
| 48 | 
            +
                      this_account.contracts.first_or_create msg.contract, :con_id
         | 
| 49 | 
            +
                      # now save the order-record
         | 
| 50 | 
            +
                      msg.order.contract = msg.contract
         | 
| 51 | 
            +
                      this_account.orders.update_or_create msg.order, :local_id
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    #     update_ib_order msg  ## aus support
         | 
| 55 | 
            +
                  when IB::Messages::Incoming::OpenOrderEnd
         | 
| 56 | 
            +
                    #             exitcondition=true
         | 
| 57 | 
            +
                    logger.debug { "OpenOrderEnd" }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  when IB::Messages::Incoming::ExecutionData
         | 
| 60 | 
            +
                    # Excution-Data are fired independly from order-states.
         | 
| 61 | 
            +
                    # The Objects are stored at the associated order
         | 
| 62 | 
            +
                    success = update_order_dependent_object( msg.execution) do |o|
         | 
| 63 | 
            +
                      o.executions << msg.execution
         | 
| 64 | 
            +
                      if msg.execution.cumulative_quantity.to_i == o.total_quantity.abs
         | 
| 65 | 
            +
                        logger.info{ "#{o.account} --> #{o.contract.symbol}: Execution completed" }
         | 
| 66 | 
            +
                        o.order_states.first_or_create( IB::OrderState.new( perm_id: o.perm_id, local_id: o.local_id,
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                                                                            status: 'Filled' ), :status )
         | 
| 69 | 
            +
                        # update portfoliovalue
         | 
| 70 | 
            +
                        a = @accounts.detect{ | x | x.account == o.account } #  we are in a mutex controlled environment
         | 
| 71 | 
            +
                        pv = a.portfolio_values.detect{ | y | y.contract.con_id == o.contract.con_id}
         | 
| 72 | 
            +
                        change = o.action == :sell ? -o.total_quantity : o.total_quantity
         | 
| 73 | 
            +
                        if pv.present?
         | 
| 74 | 
            +
                          pv.update_attribute :position, pv.position + change
         | 
| 75 | 
            +
                        else
         | 
| 76 | 
            +
                          a.portfolio_values << IB::PortfolioValue.new( position: change, contract: o.contract )
         | 
| 77 | 
            +
                        end
         | 
| 78 | 
            +
                      else
         | 
| 79 | 
            +
                        logger.debug{ "#{o.account} --> #{o.contract.symbol}: Execution not completed (#{msg.execution.cumulative_quantity.to_i}/#{o.total_quantity.abs})" }
         | 
| 80 | 
            +
                      end  # branch
         | 
| 81 | 
            +
                    end # block
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    logger.error { "Execution-Record not assigned-- #{msg.execution.to_human} ----------" } if success.nil?
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  end  # case msg.code
         | 
| 86 | 
            +
                end # do
         | 
| 87 | 
            +
              end # def subscribe
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              # Resets the order-array for each account first.
         | 
| 90 | 
            +
              # Requests all open (eg. pending)  orders from the tws
         | 
| 91 | 
            +
              #
         | 
| 92 | 
            +
              # Waits until the OpenOrderEnd-Message is recieved
         | 
| 93 | 
            +
             | 
| 94 | 
            +
             | 
| 95 | 
            +
              def request_open_orders
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                exit_condition = false
         | 
| 98 | 
            +
                subscription = tws.subscribe( :OpenOrderEnd ) { exit_condition = true }
         | 
| 99 | 
            +
                account_data{| account | account.orders = [] }
         | 
| 100 | 
            +
                send_message :RequestAllOpenOrders
         | 
| 101 | 
            +
                i=0; loop{ i+=1; sleep 0.01;  break if i > 1000 || exit_condition }
         | 
| 102 | 
            +
                tws.unsubscribe subscription
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              alias update_orders request_open_orders 
         | 
| 110 106 |  | 
| 111 107 |  | 
| 112 108 |  | 
| @@ -119,32 +115,31 @@ end # module | |
| 119 115 |  | 
| 120 116 | 
             
            module IB
         | 
| 121 117 |  | 
| 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 | 
            -
            end  # class Order
         | 
| 118 | 
            +
              class Order
         | 
| 119 | 
            +
                def auto_adjust
         | 
| 120 | 
            +
                  # lambda to perform the calculation
         | 
| 121 | 
            +
                  adjust_price = ->(a,b) do
         | 
| 122 | 
            +
                    a = BigDecimal( a, 5 ) 
         | 
| 123 | 
            +
                    b = BigDecimal( b, 5 ) 
         | 
| 124 | 
            +
                    _,o = a.divmod(b)
         | 
| 125 | 
            +
                    a - o
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                  # adjust_price[2.6896, 0.1].to_f     => 2.6
         | 
| 128 | 
            +
                  # adjust_price[2.0896, 0.05].to_f    => 2.05
         | 
| 129 | 
            +
                  # adjust_price[2.0896, 0.002].to_f   => 2.088
         | 
| 130 | 
            +
             | 
| 131 | 
            +
             | 
| 132 | 
            +
                  error "No Contract provided to Auto adjust" unless contract.is_a? IB::Contract
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  unless contract.is_a? IB::Bag
         | 
| 135 | 
            +
                    # ensure that contract_details are present
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    the_details = contract.contract_detail.present? ? contract.contract_detail : contract.verify.first.contract_detail
         | 
| 138 | 
            +
                      # there are two attributes to consider: limit_price and aux_price
         | 
| 139 | 
            +
                      # limit_price +  aux_price may be nil or an empty string. Then ".to_f.zero?" becomes true 
         | 
| 140 | 
            +
                      self.limit_price= adjust_price.call(limit_price.to_f, the_details.min_tick) unless limit_price.to_f.zero?
         | 
| 141 | 
            +
                      self.aux_price= adjust_price.call(aux_price.to_f, the_details.min_tick) unless aux_price.to_f.zero?
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
              end  # class Order
         | 
| 150 145 | 
             
            end  # module
         | 
    
        data/lib/ib/market-price.rb
    CHANGED
    
    | @@ -65,7 +65,7 @@ module IB | |
| 65 65 | 
             
            				#and cancel subscriptions to the message handler
         | 
| 66 66 | 
             
            				# method returns the (running) thread
         | 
| 67 67 | 
             
            				th = Thread.new do
         | 
| 68 | 
            -
             | 
| 68 | 
            +
             					finalize, raise_delay_alert = false, false
         | 
| 69 69 | 
             
            					s_id = tws.subscribe(:TickSnapshotEnd){|x| finalize = true if x.ticker_id == the_id }
         | 
| 70 70 |  | 
| 71 71 | 
             
            				e_id = tws.subscribe(:Alert){|x| raise_delay_alert = true if x.code == 354 && x.error_id == the_id } 
         | 
| @@ -77,7 +77,7 @@ module IB | |
| 77 77 | 
             
            						[last,close,bid,ask].each do |x| 
         | 
| 78 78 | 
             
            							tickdata[x] = msg.the_data[:price] if x.include?( IB::TICK_TYPES[ msg.the_data[:tick_type]]) 
         | 
| 79 79 | 
             
            							#  fast exit condition
         | 
| 80 | 
            -
            							finalize = true if tickdata.size  | 
| 80 | 
            +
            							finalize = true if tickdata.size >= 4  || ( tickdata[bid].present? && tickdata[ask].present? )  
         | 
| 81 81 | 
             
            						end if  msg.ticker_id == the_id 
         | 
| 82 82 | 
             
            					end
         | 
| 83 83 | 
             
            					# initialize »the_id« that is used to identify the received tick messages
         | 
    
        data/lib/ib/models/account.rb
    CHANGED
    
    | @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            module IB
         | 
| 2 2 |  | 
| 3 | 
            -
            	class Account | 
| 3 | 
            +
            	class Account
         | 
| 4 4 |  | 
| 5 5 |  | 
| 6 6 | 
             
            		def account_data_scan search_key, search_currency=nil
         | 
| 7 7 | 
             
            			if account_values.is_a? Array
         | 
| 8 | 
            -
            				if search_currency.present? | 
| 8 | 
            +
            				if search_currency.present?
         | 
| 9 9 | 
             
            					account_values.find_all{|x| x.key.match( search_key )  && x.currency == search_currency.upcase }
         | 
| 10 10 | 
             
            				else
         | 
| 11 11 | 
             
            					account_values.find_all{|x| x.key.match( search_key ) }
         | 
| @@ -24,8 +24,8 @@ module IB | |
| 24 24 |  | 
| 25 25 | 
             
            =begin rdoc
         | 
| 26 26 | 
             
            given any key of local_id, perm_id or order_ref
         | 
| 27 | 
            -
            and an optional status, which can be a string or a | 
| 28 | 
            -
            regexp ( status: /mitted/ matches Submitted and Presubmitted) | 
| 27 | 
            +
            and an optional status, which can be a string or a
         | 
| 28 | 
            +
            regexp ( status: /mitted/ matches Submitted and Presubmitted)
         | 
| 29 29 | 
             
            the last associated Orderrecord is returned.
         | 
| 30 30 |  | 
| 31 31 | 
             
            Thus if several Orders are placed with the same order_ref, the active one is returned
         | 
| @@ -33,16 +33,23 @@ Thus if several Orders are placed with the same order_ref, the active one is ret | |
| 33 33 | 
             
            (If multible keys are specified, local_id preceeds perm_id)
         | 
| 34 34 |  | 
| 35 35 | 
             
            =end
         | 
| 36 | 
            -
            		def locate_order local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, con_id: nil
         | 
| 36 | 
            +
            		def locate_order local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil
         | 
| 37 37 | 
             
            			search_option= [ local_id.present? ? [:local_id , local_id] : nil ,
         | 
| 38 38 | 
             
            										perm_id.present? ? [:perm_id, perm_id] : nil,
         | 
| 39 39 | 
             
            										order_ref.present? ? [:order_ref , order_ref ] : nil ].compact.first
         | 
| 40 | 
            -
            			matched_items = if search_option.nil? | 
| 41 | 
            -
            												orders | 
| 42 | 
            -
            											else | 
| 40 | 
            +
            			matched_items = if search_option.nil?
         | 
| 41 | 
            +
            												orders
         | 
| 42 | 
            +
            											else
         | 
| 43 43 | 
             
            												orders.find_all{|x| x[search_option.first].to_i == search_option.last.to_i }
         | 
| 44 44 | 
             
            											end
         | 
| 45 | 
            -
             | 
| 45 | 
            +
                  if contract.present?
         | 
| 46 | 
            +
                    if contract.con_id.nil? || contract.con_id =="" || contract.con_id.zero? 
         | 
| 47 | 
            +
                      contract =  contract.verify.first unless contract.is_a? IB::Bag
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                    matched_items = matched_items.find_all{|o| o.contract.essential == contract.essential } 
         | 
| 50 | 
            +
                  elsif con_id.present?
         | 
| 51 | 
            +
                    matched_items = matched_items.find_all{|o| o.contract.con_id == con_id } 
         | 
| 52 | 
            +
                  end
         | 
| 46 53 |  | 
| 47 54 | 
             
            			if status.present?
         | 
| 48 55 | 
             
            				status = Regexp.new(status) unless status.is_a? Regexp
         | 
| @@ -53,8 +60,8 @@ Thus if several Orders are placed with the same order_ref, the active one is ret | |
| 53 60 | 
             
            		end
         | 
| 54 61 |  | 
| 55 62 |  | 
| 56 | 
            -
            =begin rdoc | 
| 57 | 
            -
            requires an IB::Order as parameter. | 
| 63 | 
            +
            =begin rdoc
         | 
| 64 | 
            +
            requires an IB::Order as parameter.
         | 
| 58 65 |  | 
| 59 66 | 
             
            If attached, the associated IB::Contract is used to specify the tws-command
         | 
| 60 67 |  | 
| @@ -65,9 +72,9 @@ auto_adjust: Limit- and Aux-Prices are adjusted to Min-Tick | |
| 65 72 | 
             
            convert_size: The action-attribute (:buy  :sell) is associated according the content of :total_quantity.
         | 
| 66 73 |  | 
| 67 74 |  | 
| 68 | 
            -
            The parameter «order» is modified! | 
| 75 | 
            +
            The parameter «order» is modified!
         | 
| 69 76 |  | 
| 70 | 
            -
            It can be used to modify and eventually cancel | 
| 77 | 
            +
            It can be used to modify and eventually cancel
         | 
| 71 78 |  | 
| 72 79 | 
             
            The method raises an  IB::TransmissionError if the transmitted order ist not acknowledged by the tws after
         | 
| 73 80 | 
             
            one second.
         | 
| @@ -79,23 +86,23 @@ Example | |
| 79 86 | 
             
               g =  IB::Gateway.current.clients.last
         | 
| 80 87 |  | 
| 81 88 | 
             
            	 g.preview contract: j36, order: order
         | 
| 82 | 
            -
                  => {:init_margin=>0.10864874e6, | 
| 83 | 
            -
            			    :maint_margin=>0.9704137e5, | 
| 84 | 
            -
            					:equity_with_loan=>0.97877973e6, | 
| 85 | 
            -
            					:commission=>0.524e1, | 
| 86 | 
            -
            					:commission_currency=>"USD", | 
| 89 | 
            +
                  => {:init_margin=>0.10864874e6,
         | 
| 90 | 
            +
            			    :maint_margin=>0.9704137e5,
         | 
| 91 | 
            +
            					:equity_with_loan=>0.97877973e6,
         | 
| 92 | 
            +
            					:commission=>0.524e1,
         | 
| 93 | 
            +
            					:commission_currency=>"USD",
         | 
| 87 94 | 
             
            					:warning=>""}
         | 
| 88 95 |  | 
| 89 96 | 
             
               the_local_id = g.place order: order
         | 
| 90 97 | 
             
            		  => 67						# returns local_id
         | 
| 91 | 
            -
            	 order.contract			# updated contract-record | 
| 92 | 
            -
                  => #<IB::Contract:0x00000000013c94b0 @attributes={:con_id=>95346693, | 
| 93 | 
            -
            			                                                   :exchange=>"SGX", | 
| 94 | 
            -
            																												 :right=>"", | 
| 95 | 
            -
            																												 :include_expired=>false}> | 
| 98 | 
            +
            	 order.contract			# updated contract-record
         | 
| 99 | 
            +
                  => #<IB::Contract:0x00000000013c94b0 @attributes={:con_id=>95346693,
         | 
| 100 | 
            +
            			                                                   :exchange=>"SGX",
         | 
| 101 | 
            +
            																												 :right=>"",
         | 
| 102 | 
            +
            																												 :include_expired=>false}>
         | 
| 96 103 |  | 
| 97 104 | 
             
            		order.limit_price = 65   # set new price
         | 
| 98 | 
            -
            	  g.modify order: order    # and transmit | 
| 105 | 
            +
            	  g.modify order: order    # and transmit
         | 
| 99 106 | 
             
            		  => 67 # returns local_id
         | 
| 100 107 |  | 
| 101 108 | 
             
            		g.locate_order( local_id: the_local_id  )
         | 
| @@ -103,18 +110,24 @@ Example | |
| 103 110 |  | 
| 104 111 | 
             
            		g.cancel order: order
         | 
| 105 112 | 
             
            				# logger output: 05:17:11 Cancelling 65 New #250/ from 3000/DU167349>
         | 
| 106 | 
            -
            	 
         | 
| 107 113 | 
             
            =end
         | 
| 108 114 |  | 
| 109 | 
            -
            		def place_order  order:, contract: nil, auto_adjust: true, convert_size:  false
         | 
| 115 | 
            +
            		def place_order  order:, contract: nil, auto_adjust: true, convert_size:  false,  enable_error: false
         | 
| 110 116 | 
             
            			# adjust the orderprice to  min-tick
         | 
| 111 | 
            -
            			logger.progname =  'Account#PlaceOrder' 
         | 
| 112 117 | 
             
            			result = ->(l){ orders.detect{|x| x.local_id == l  && x.submitted? } }
         | 
| 113 118 | 
             
            			#·IB::Symbols are always qualified. They carry a description-field
         | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
            			order.contract  | 
| 117 | 
            -
             | 
| 119 | 
            +
                  qualified_contract = ->(c) { c.is_a?(IB::Contract) && ( c.description.present? || (c.con_id.present?  &&  !c.con_id.to_i.zero?) || (c.con_id.to_i <0  && c.sec_type == :bag )) }
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            			order.contract ||=  if qualified_contract[ contract ]
         | 
| 122 | 
            +
                                    contract
         | 
| 123 | 
            +
                                     else
         | 
| 124 | 
            +
                                       contract.verify.first
         | 
| 125 | 
            +
                                     end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  if order.contract.nil?
         | 
| 128 | 
            +
                   error "No valid contract given" if enable_error
         | 
| 129 | 
            +
                   return 0
         | 
| 130 | 
            +
                  end 
         | 
| 118 131 | 
             
            			## sending of plain vanilla IB::Bags will fail using account.place, unless a (negative) con-id is provided!
         | 
| 119 132 | 
             
            #			error "place order: ContractVerification failed. No con_id assigned"  unless qualified_contract[order.contract]
         | 
| 120 133 | 
             
            			ib = IB::Connection.current
         | 
| @@ -122,46 +135,41 @@ Example | |
| 122 135 | 
             
            			the_local_id =  nil
         | 
| 123 136 |  | 
| 124 137 | 
             
            			### Handle Error messages
         | 
| 125 | 
            -
            			### Default action:   | 
| 138 | 
            +
            			### Default action:  raise IB::Transmission Error
         | 
| 126 139 | 
             
            			sa = ib.subscribe( :Alert ) do | msg |
         | 
| 140 | 
            +
              #      puts "local_id: #{the_local_id}"
         | 
| 127 141 | 
             
            				if msg.error_id == the_local_id
         | 
| 128 | 
            -
            				 if [ 110, #  The price does not  | 
| 142 | 
            +
            				 if [ 110, #  The price does not confirm to the minimum price variation for this contract
         | 
| 129 143 | 
             
            					   388,  # Order size x is smaller than the minimum required size of yy.
         | 
| 130 144 | 
             
            					  ].include? msg.code
         | 
| 131 | 
            -
             | 
| 145 | 
            +
                       error msg.message if enable_error
         | 
| 146 | 
            +
                       wrong_order =  msg.error_id.to_i
         | 
| 132 147 | 
             
            					 ib.logger.error msg.message
         | 
| 133 148 | 
             
            				 end
         | 
| 134 149 | 
             
            				end
         | 
| 135 150 | 
             
            			end
         | 
| 136 151 | 
             
            			order.account =  account  # assign the account_id to the account-field of IB::Order
         | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
            			 	order.action = order.total_quantity.to_i > 0  ? 	:buy : :sell 
         | 
| 152 | 
            +
                  self.orders.update_or_create order, :order_ref
         | 
| 153 | 
            +
                  order.auto_adjust # if auto_adjust  /defined in lib/order_handling
         | 
| 154 | 
            +
            			if convert_size
         | 
| 155 | 
            +
            				order.action = order.total_quantity.to_i > 0  ?	:buy : :sell
         | 
| 156 | 
            +
                    logger.info{ "Converted ordesize to #{order.total_quantity} and triggered a #{order.action}  order"} if  order.total_quantity.to_i < 0
         | 
| 143 157 | 
             
            				order.total_quantity  = order.total_quantity.to_i.abs
         | 
| 144 158 | 
             
            			end
         | 
| 145 159 | 
             
            				# apply non_guarenteed and other stuff bound to the contract to order.
         | 
| 146 160 | 
             
            			order.attributes.merge! order.contract.order_requirements unless order.contract.order_requirements.blank?
         | 
| 147 161 | 
             
            				#  con_id and exchange fully qualify a contract, no need to transmit other data
         | 
| 148 | 
            -
            			the_contract = order.contract.con_id >0 ? Contract.new( con_id: order.contract.con_id, exchange: order.contract.exchange) : nil | 
| 162 | 
            +
            			the_contract = order.contract.con_id >0 ? Contract.new( con_id: order.contract.con_id, exchange: order.contract.exchange) : nil
         | 
| 149 163 | 
             
            			the_local_id = order.place the_contract # return the local_id
         | 
| 150 | 
            -
             | 
| 151 | 
            -
            				loop{ sleep(0.001); break if locate_order( local_id: the_local_id, status: nil ).present? }
         | 
| 152 | 
            -
            			end
         | 
| 164 | 
            +
                  i=0;	loop{i+=1; sleep(0.01); break if locate_order( local_id: the_local_id, status: nil ).present? || i> 1000  }
         | 
| 153 165 |  | 
| 154 166 | 
             
            			ib.unsubscribe sa
         | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
            			else
         | 
| 159 | 
            -
            				nil
         | 
| 160 | 
            -
            			end
         | 
| 161 | 
            -
            		end # place 
         | 
| 167 | 
            +
                  raise IB::TransmissionError," #{order.to_human} is not transmitted properly" if i >=1000
         | 
| 168 | 
            +
                  the_local_id  # return_value
         | 
| 169 | 
            +
            		end # place
         | 
| 162 170 |  | 
| 163 171 | 
             
            		# shortcut to enable
         | 
| 164 | 
            -
            		#  account.place order: {} , contract: {} | 
| 172 | 
            +
            		#  account.place order: {} , contract: {}
         | 
| 165 173 | 
             
            		#  account.preview order: {} , contract: {}
         | 
| 166 174 | 
             
            		#  account.modify order: {}
         | 
| 167 175 | 
             
            		alias place place_order
         | 
| @@ -171,7 +179,7 @@ Account#ModifyOrder operates in two modi: | |
| 171 179 |  | 
| 172 180 | 
             
            First: The order is specified  via local_id, perm_id or order_ref.
         | 
| 173 181 | 
             
            	It is checked, whether the order is still modificable.
         | 
| 174 | 
            -
            	Then the Order ist provided through  the block. Any modification is done there. | 
| 182 | 
            +
            	Then the Order ist provided through  the block. Any modification is done there.
         | 
| 175 183 | 
             
            	Important: The Block has to return the modified IB::Order
         | 
| 176 184 |  | 
| 177 185 | 
             
            Second: The order can be provided as parameter as well. This will be used
         | 
| @@ -185,7 +193,6 @@ This has to be done manualy in the provided block | |
| 185 193 | 
             
            		def modify_order  local_id: nil, order_ref: nil, order:nil
         | 
| 186 194 |  | 
| 187 195 | 
             
            			result = ->(l){ orders.detect{|x| x.local_id == l  && x.submitted? } }
         | 
| 188 | 
            -
            			logger.tap{ |l| l.progname = "Account #{account}#modify_order"}
         | 
| 189 196 | 
             
            			order ||= locate_order( local_id: local_id, 
         | 
| 190 197 | 
             
            														 status: /ubmitted/ ,
         | 
| 191 198 | 
             
            														 order_ref: order_ref ) 
         | 
| @@ -214,9 +221,9 @@ This has to be done manualy in the provided block | |
| 214 221 | 
             
            		order.what_if = true
         | 
| 215 222 | 
             
            		the_local_id = place_order order: order, contract: contract
         | 
| 216 223 | 
             
            		i=0; loop{ i= i+1;  break if result[the_local_id] || i > 1000; sleep 0.01 }
         | 
| 217 | 
            -
             | 
| 224 | 
            +
                raise IB::TransmissionError,"(Preview-) #{order.to_human} is not transmitted properly" if i >=1000
         | 
| 218 225 | 
             
            		order.what_if = false # reset what_if flag
         | 
| 219 | 
            -
            		order.local_id = nil  # reset local_id to enable  | 
| 226 | 
            +
            		order.local_id = nil  # reset local_id to enable re-using the order-object for placing
         | 
| 220 227 | 
             
            		result[the_local_id].order_state.forcast  #  return_value
         | 
| 221 228 | 
             
            	end 
         | 
| 222 229 |  |