ib-ruby 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/HISTORY +4 -0
  2. data/README.md +60 -30
  3. data/TODO +1 -0
  4. data/VERSION +1 -1
  5. data/bin/account_info +1 -4
  6. data/bin/cancel_orders +0 -3
  7. data/bin/contract_details +2 -5
  8. data/bin/depth_of_market +1 -4
  9. data/bin/fa_accounts +22 -0
  10. data/bin/historic_data +1 -4
  11. data/bin/historic_data_cli +0 -4
  12. data/bin/list_orders +2 -6
  13. data/bin/market_data +1 -4
  14. data/bin/option_data +2 -5
  15. data/bin/place_combo_order +17 -22
  16. data/bin/place_order +6 -10
  17. data/bin/tick_data +6 -9
  18. data/bin/time_and_sales +2 -5
  19. data/lib/ib-ruby/connection.rb +10 -5
  20. data/lib/ib-ruby/messages/incoming/open_order.rb +15 -13
  21. data/lib/ib-ruby/messages/incoming/ticks.rb +1 -1
  22. data/lib/ib-ruby/messages/incoming.rb +18 -18
  23. data/lib/ib-ruby/messages/outgoing.rb +2 -2
  24. data/lib/ib-ruby/messages.rb +3 -7
  25. data/lib/ib-ruby/models/{contract → contracts}/bag.rb +5 -8
  26. data/lib/ib-ruby/models/contracts/contract.rb +296 -0
  27. data/lib/ib-ruby/models/{contract → contracts}/option.rb +2 -4
  28. data/lib/ib-ruby/models/contracts.rb +27 -0
  29. data/lib/ib-ruby/models/execution.rb +1 -1
  30. data/lib/ib-ruby/models/order.rb +6 -17
  31. data/lib/ib-ruby/models.rb +9 -7
  32. data/lib/ib-ruby/symbols/forex.rb +50 -50
  33. data/lib/ib-ruby/symbols/futures.rb +47 -47
  34. data/lib/ib-ruby/symbols/options.rb +23 -23
  35. data/lib/ib-ruby/symbols/stocks.rb +14 -14
  36. data/lib/ib-ruby.rb +17 -9
  37. data/spec/README.md +6 -0
  38. data/spec/account_helper.rb +1 -1
  39. data/spec/combo_helper.rb +31 -0
  40. data/spec/ib-ruby/models/combo_leg_spec.rb +4 -4
  41. data/spec/ib-ruby/models/contract_spec.rb +37 -26
  42. data/spec/ib-ruby/models/execution_spec.rb +5 -5
  43. data/spec/ib-ruby/models/order_spec.rb +16 -16
  44. data/spec/integration/contract_info_spec.rb +8 -10
  45. data/spec/integration/historic_data_spec.rb +1 -1
  46. data/spec/integration/market_data_spec.rb +5 -5
  47. data/spec/integration/orders/attached_spec.rb +87 -0
  48. data/spec/integration/orders/combo_spec.rb +52 -65
  49. data/spec/integration/orders/placement_spec.rb +8 -8
  50. data/spec/order_helper.rb +97 -28
  51. data/spec/spec_helper.rb +2 -2
  52. metadata +12 -5
  53. data/lib/ib-ruby/models/contract.rb +0 -308
data/HISTORY CHANGED
@@ -149,3 +149,7 @@
149
149
  == 0.7.3 / 2012-03-23
150
150
 
151
151
  * Connection API extended
152
+
153
+ == 0.7.4 / 2012-04-04
154
+
155
+ * Order equality bug fixed
data/README.md CHANGED
@@ -13,59 +13,89 @@ You've been warned.
13
13
 
14
14
  This code is not sanctioned or supported by Interactive Brokers.
15
15
 
16
- ## REQUIREMENTS:
16
+ ## SUMMARY:
17
17
 
18
- Either the Interactive Brokers [TWS](http://www.interactivebrokers.com/en/p.php?f=tws) or
19
- [Gateway](http://www.interactivebrokers.com/en/p.php?f=programInterface&ib_entity=llc)
20
- software must be installed and configured to allow API connections from the computer
21
- you plan to run ib-ruby on, which is typically localhost if you're running ib-ruby on
22
- the same machine as TWS.
18
+ This is a pure Ruby implementation of Interactive Brokers API. It is NOT a wrapper
19
+ for a Java or C++ API, but rather uses socket API directly. So it does not have any
20
+ dependencies other than TWS/Gateway itself.
23
21
 
24
- As a rule of thumb, most recent version of ib-ruby gem only supports latest versions
25
- of TWS/Gateway API. Older versions of API are supported by previous gem versions:
22
+ Why Ruby? Many people are put off by the amount of boilerplate code/plumbing required
23
+ by Java, ActiveX or C++ API to do even the simplest of things, like getting account
24
+ data and placing/monitoring orders. This library intends to keep all the fluff away
25
+ and let you focus on writing your business logics, rather than useless boilerplate.
26
26
 
27
- ib-ruby gem TWS version API version
27
+ No more endless definitions of obligatory methods you'd never need, no spaghetti code
28
+ to divide your execution flow between multiple callbacks and interfaces.
28
29
 
29
- 0.5.21 918-920 965
30
- 0.6.1 921-923 966
31
- 0.7.1+ 924+ 967
30
+ Instead, a very simple paradigm is offered: your code interacts with the server
31
+ (TWS or Gateway) via exchange of messages. You subscribe to the server messages
32
+ that you're interested in, and send messages to server that request specific data
33
+ from it. You wait for specific messages being received, or other conditions you
34
+ define. The execution flow is under your control, rather than delegated somewhere.
35
+
36
+ Using this clear paradigm, you can hack together a simple automation of your
37
+ daily TWS-related routine in just a couple of minutes. Alternatively, you can
38
+ create a mechanical trading system with complex order processing logics, that
39
+ contains 1/10th of code and is 500% more maintaineable than it is possible with
40
+ other API implementations. The choice is yours.
32
41
 
33
42
  ## INSTALLATION:
34
43
 
35
44
  ### From RubyGems
36
45
 
37
- $ sudo gem install ib-ruby
46
+ $ sudo gem install ib-ruby [-v version]
38
47
 
39
48
  ### From Source
40
49
 
41
50
  $ git clone https://github.com/ib-ruby/ib-ruby
42
51
  $ cd ib-ruby; rake gem:install
43
52
 
53
+ ## PREREQUISITES:
54
+
55
+ 1. Install Interactive Brokers connectivity software: either
56
+ [TWS](http://www.interactivebrokers.com/en/p.php?f=tws) or
57
+ [Gateway](http://www.interactivebrokers.com/en/p.php?f=programInterface&ib_entity=llc)
58
+
59
+ 2. Configure the software to allow API connections from the computer you plan to run
60
+ ib-ruby on, which is typically localhost (127.0.0.1) if you're running ib-ruby on
61
+ the same machine as TWS/Gateway. [Here](http://www.youtube.com/watch?v=53tmypRq5wI)
62
+ you can see how this is done for TWS.
63
+
64
+ 3. Make sure sure your ib-ruby gem version is compatible with your software version.
65
+ As a rule of thumb, most recent ib-ruby gem only supports latest versions of
66
+ TWS/Gateway API. Older versions of API are supported by previous gem versions:
67
+
68
+ | ib-ruby gem | TWS version | API version |
69
+ |:------------|------------:|:------------:|
70
+ | 0.5.21 | 918-920 | 965 |
71
+ | 0.6.1 | 921-923 | 966 |
72
+ | 0.7.1+ | 924+ | 967 |
73
+
74
+
75
+ 4. Start Interactive Broker's Trader Work Station or Gateway before your code
76
+ attempts to connect to it. Note that TWS and Gateway listen to different ports,
77
+ this library assumes connection to Gateway on the same machine (localhost:4001)
78
+ by default, this can be changed via :host and :port options given to IB::Connection.new.
79
+
44
80
  ## SYNOPSIS:
45
81
 
46
- First, start up Interactive Broker's Trader Work Station or Gateway.
47
- Make sure it is configured to allow API connections on localhost.
48
- Note that TWS and Gateway listen to different ports, this library assumes
49
- connection to Gateway (localhost:4001) by default, this can changed via :host
50
- and :port options given to IB::Connection.new.
82
+ This is an example of your script that requests and prints out account data, then
83
+ places limit order to buy 100 lots of WFC and waits for execution. All in about ten
84
+ lines of code - and without sacrificing code readability or flexibility.
51
85
 
52
86
  require 'ib-ruby'
53
87
 
54
- ib = IB::Connection.new :port => 7496 # TWS on localhost
88
+ ib = IB::Connection.new :port => 7496
55
89
  ib.subscribe(:Alert, :AccountValue) { |msg| puts msg.to_human }
56
90
  ib.send_message :RequestAccountData
57
91
  ib.wait_for :AccountDownloadEnd
58
92
 
59
93
  ib.subscribe(:OpenOrder) { |msg| puts "Placed: #{msg.order}!" }
60
94
  ib.subscribe(:ExecutionData) { |msg| puts "Filled: #{msg.execution}!" }
61
- contract = IB::Models::Contract.new :symbol => 'WFC',
62
- :exchange => 'NYSE'
63
- :currency => 'USD',
64
- :sec_type => IB::SECURITY_TYPES[:stock]
65
- buy_order = IB::Models::Order.new :total_quantity => 100,
66
- :limit_price => 21.00,
67
- :action => 'BUY',
68
- :order_type => 'LMT'
95
+ contract = IB::Contract.new :symbol => 'WFC', :exchange => 'NYSE',
96
+ :currency => 'USD', :sec_type => 'STK'
97
+ buy_order = IB::Order.new :total_quantity => 100, :limit_price => 21.00,
98
+ :action => 'BUY', :order_type => 'LMT'
69
99
  ib.place_order buy_order, contract
70
100
  ib.wait_for :ExecutionData
71
101
 
@@ -74,8 +104,8 @@ TWS are called 'Outgoing', messages your code receives from TWS - 'Incoming'.
74
104
 
75
105
  First, you need to subscribe to incoming message types you're interested in
76
106
  using `Connection#subscribe`. The code block (or proc) given to `#subscribe`
77
- will be executed when an incoming message of the requested type is received
78
- from TWS, with the received message as its argument.
107
+ will be executed when an incoming message of the this type is received from TWS,
108
+ with the received message as its argument.
79
109
 
80
110
  Then, you request specific data from TWS using `Connection#send_message` or place
81
111
  your order using `Connection#place_order`. TWS will respond with messages that you
@@ -86,7 +116,7 @@ In order to give TWS time to respond, you either run a message processing loop o
86
116
  just wait until Connection receives the messages type you requested.
87
117
 
88
118
  See `lib/ib-ruby/messages` for a full list of supported incoming/outgoing messages
89
- and their attributes. The original TWS docs and code samples can be found
119
+ and their attributes. The original TWS docs and code samples can also be found
90
120
  in `misc` directory.
91
121
 
92
122
  The sample scripts in `bin` directory provide examples of how common tasks
data/TODO CHANGED
@@ -16,6 +16,7 @@ http://finance.groups.yahoo.com/group/TWSAPI/message/25413
16
16
 
17
17
  7. Create integration tests for more use cases (spec/README)
18
18
 
19
+ 8. Move Float values to Decimal (roundoff errors showed in spec!)
19
20
 
20
21
  Done:
21
22
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.3
1
+ 0.7.4
data/bin/account_info CHANGED
@@ -3,11 +3,8 @@
3
3
  # This script connects to IB API, subscribes to account info and prints out
4
4
  # messages received from IB (update every 3 minute or so)
5
5
 
6
- require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
9
-
10
6
  require 'rubygems'
7
+ require 'pathname'
11
8
  require 'bundler/setup'
12
9
  require 'ib-ruby'
13
10
 
data/bin/cancel_orders CHANGED
@@ -6,9 +6,6 @@
6
6
 
7
7
  require 'rubygems'
8
8
  require 'pathname'
9
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
10
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
11
-
12
9
  require 'bundler/setup'
13
10
  require 'ib-ruby'
14
11
 
data/bin/contract_details CHANGED
@@ -1,12 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script gets details for specific symbol from IB
4
-
5
- require 'pathname'
6
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
7
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
3
+ # This script gets details for specific contract from IB
8
4
 
9
5
  require 'rubygems'
6
+ require 'pathname'
10
7
  require 'bundler/setup'
11
8
  require 'ib-ruby'
12
9
 
data/bin/depth_of_market CHANGED
@@ -3,11 +3,8 @@
3
3
  # This script connects to IB API and subscribes to L2 (depth of market) data for
4
4
  # specific symbols
5
5
 
6
- require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
9
-
10
6
  require 'rubygems'
7
+ require 'pathname'
11
8
  require 'bundler/setup'
12
9
  require 'ib-ruby'
13
10
 
data/bin/fa_accounts ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This script receives Financial Adviser and Managed Accounts info
4
+
5
+ require 'rubygems'
6
+ require 'pathname'
7
+ require 'bundler/setup'
8
+ require 'ib-ruby'
9
+
10
+ # First, connect to IB TWS.
11
+ ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
12
+
13
+ # Subscribe to TWS alerts/errors and FA/managed account info
14
+ ib.subscribe(:Alert, :ManagedAccounts, :ReceiveFA) { |msg| puts msg.to_human }
15
+
16
+ ib.send_message :RequestFA
17
+ ib.send_message :RequestManagedAccounts
18
+
19
+ ib.wait_for :Alert
20
+
21
+ puts "\n******** Press <Enter> to cancel... *********\n\n"
22
+ STDIN.gets
data/bin/historic_data CHANGED
@@ -2,11 +2,8 @@
2
2
  #
3
3
  # This script downloads historic data for specific symbols from IB
4
4
 
5
- require 'pathname'
6
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
7
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
8
-
9
5
  require 'rubygems'
6
+ require 'pathname'
10
7
  require 'bundler/setup'
11
8
  require 'ib-ruby'
12
9
 
@@ -6,10 +6,6 @@ require 'time'
6
6
  require 'pathname'
7
7
  require 'getopt/long'
8
8
  require 'bundler/setup'
9
-
10
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
11
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
12
-
13
9
  require 'ib-ruby'
14
10
 
15
11
  include Getopt
data/bin/list_orders CHANGED
@@ -1,12 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script connects to IB API, subscribes to account info and prints out
4
- # messages received from IB (update every 3 minute or so)
3
+ # This script retrieves list of all Orders from TWS
5
4
 
6
5
  require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
9
-
10
6
  require 'rubygems'
11
7
  require 'bundler/setup'
12
8
  require 'pp'
@@ -23,7 +19,7 @@ ib.subscribe(:Alert, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
23
19
  ib.subscribe(:OpenOrder) do |msg|
24
20
  @counter += 1
25
21
  puts "#{@counter}: #{msg.to_human}"
26
- pp msg.order
22
+ #pp msg.order
27
23
  end
28
24
 
29
25
  ib.send_message :RequestAllOpenOrders
data/bin/market_data CHANGED
@@ -2,11 +2,8 @@
2
2
  #
3
3
  # This script connects to IB API and subscribes to market data for specific symbols
4
4
 
5
- require 'pathname'
6
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
7
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
8
-
9
5
  require 'rubygems'
6
+ require 'pathname'
10
7
  require 'bundler/setup'
11
8
  require 'ib-ruby'
12
9
 
data/bin/option_data CHANGED
@@ -1,12 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script connects to IB API and subscribes to market data for specific symbols
4
-
5
- require 'pathname'
6
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
7
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
3
+ # This script subscribes to market data for a list of Options
8
4
 
9
5
  require 'rubygems'
6
+ require 'pathname'
10
7
  require 'bundler/setup'
11
8
  require 'ib-ruby'
12
9
 
@@ -1,13 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script connects to IB API, subscribes to account info and prints out
4
- # messages received from IB (update every 3 minute or so)
5
-
6
- require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
3
+ # This script places GOOG option butterfly combo order
9
4
 
10
5
  require 'rubygems'
6
+ require 'pathname'
11
7
  require 'bundler/setup'
12
8
  require 'ib-ruby'
13
9
 
@@ -17,10 +13,10 @@ def butterfly symbol, expiry, right, *strikes
17
13
 
18
14
  legs = strikes.zip([1, -2, 1]).map do |strike, weight|
19
15
  # Create contract
20
- contract = IB::Models::Contract::Option.new :symbol => symbol,
21
- :expiry => expiry,
22
- :right => right,
23
- :strike => strike
16
+ contract = IB::Option.new :symbol => symbol,
17
+ :expiry => expiry,
18
+ :right => right,
19
+ :strike => strike
24
20
  # Find out contract's con_id
25
21
  @ib.clear_received :ContractData, :ContractDataEnd
26
22
  @ib.send_message :RequestContractData, :id => strike, :contract => contract
@@ -28,19 +24,18 @@ def butterfly symbol, expiry, right, *strikes
28
24
  con_id = @ib.received[:ContractData].last.contract.con_id
29
25
 
30
26
  # Create Comboleg from con_id and weight
31
- IB::Models::ComboLeg.new :con_id => con_id, :weight => weight
27
+ IB::ComboLeg.new :con_id => con_id, :weight => weight
32
28
  end
33
29
 
34
30
  # Create new Combo contract
35
- IB::Models::Contract::Bag.new :symbol => symbol,
36
- :currency => "USD", # Only US options in combo Contracts
37
- :exchange => "SMART",
38
- :legs => legs
31
+ IB::Bag.new :symbol => symbol,
32
+ :currency => "USD", # Only US options in combo Contracts
33
+ :exchange => "SMART",
34
+ :legs => legs
39
35
  end
40
36
 
41
-
42
37
  # First, connect to IB TWS. Arbitrary :client_id is used to identify your script
43
- ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
38
+ @ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
44
39
 
45
40
  # Subscribe to TWS alerts/errors and order-related messages
46
41
  @ib.subscribe(:Alert, :OpenOrder, :OrderStatus) { |msg| puts msg.to_human }
@@ -50,14 +45,14 @@ ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
50
45
  combo = butterfly 'GOOG', '201301', 'CALL', 500, 510, 520
51
46
 
52
47
  # Create Order stub
53
- order = IB::Models::Order.new :total_quantity => 10, # 10 butterflies
54
- :limit_price => 0.01, # at 0.01 x 100 USD per contract
55
- :action => 'BUY',
56
- :order_type => 'LMT'
48
+ order = IB::Order.new :total_quantity => 10, # 10 butterflies
49
+ :limit_price => 0.01, # at 0.01 x 100 USD per contract
50
+ :action => 'BUY',
51
+ :order_type => 'LMT'
57
52
 
58
53
  @ib.place_order order, combo
59
54
 
60
55
  @ib.wait_for [:OpenOrder, 3], [:OrderStatus, 2]
61
56
 
62
57
  puts "\n******** Press <Enter> to cancel... *********\n\n"
63
- STDIN.gets
58
+ gets
data/bin/place_order CHANGED
@@ -1,13 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script connects to IB API, subscribes to account info and prints out
4
- # messages received from IB (update every 3 minute or so)
5
-
6
- require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
3
+ # This script places WFC buy order for 100 lots
9
4
 
10
5
  require 'rubygems'
6
+ require 'pathname'
11
7
  require 'bundler/setup'
12
8
  require 'ib-ruby'
13
9
 
@@ -18,10 +14,10 @@ ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
18
14
  ib.subscribe(:Alert, :OpenOrder, :OrderStatus) { |msg| puts msg.to_human }
19
15
 
20
16
  wfc = IB::Symbols::Stocks[:wfc]
21
- buy_order = IB::Models::Order.new :total_quantity => 100,
22
- :limit_price => 1 + rand().round(2),
23
- :action => 'BUY',
24
- :order_type => 'LMT'
17
+ buy_order = IB::Order.new :total_quantity => 100,
18
+ :limit_price => 1 + rand().round(2),
19
+ :action => 'BUY',
20
+ :order_type => 'LMT'
25
21
  ib.wait_for :NextValidId
26
22
  ib.place_order buy_order, wfc
27
23
 
data/bin/tick_data CHANGED
@@ -2,19 +2,16 @@
2
2
 
3
3
  # This script reproduces https://github.com/ib-ruby/ib-ruby/issues/12
4
4
 
5
- require 'pathname'
6
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
7
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
8
-
9
5
  require 'rubygems'
6
+ require 'pathname'
10
7
  require 'bundler/setup'
11
8
  require 'ib-ruby'
12
9
 
13
- contract = IB::Models::Contract.new :symbol=> 'AAPL',
14
- :exchange=> "Smart",
15
- :currency=> "USD",
16
- :sec_type=> IB::SECURITY_TYPES[:stock],
17
- :description=> "Some stock"
10
+ contract = IB::Contract.new :symbol=> 'AAPL',
11
+ :exchange=> "Smart",
12
+ :currency=> "USD",
13
+ :sec_type=> IB::SECURITY_TYPES[:stock],
14
+ :description=> "Some stock"
18
15
 
19
16
  # First, connect to IB TWS. Arbitrary :client_id is used to identify your script
20
17
  ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
data/bin/time_and_sales CHANGED
@@ -1,13 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # This script connects to IB API and subscribes to market data for specific symbols.
3
+ # This script connects to IB API and subscribes to market data for Forex symbols.
4
4
  # It then prints out all trades that exceed certain size.
5
5
 
6
- require 'pathname'
7
- LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
8
- $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
9
-
10
6
  require 'rubygems'
7
+ require 'pathname'
11
8
  require 'bundler/setup'
12
9
  require 'ib-ruby'
13
10
 
@@ -277,20 +277,25 @@ module IB
277
277
  # Place Order (convenience wrapper for send_message :PlaceOrder).
278
278
  # Assigns client_id and order_id fields to placed order. Returns assigned order_id.
279
279
  def place_order order, contract
280
- send_message :PlaceOrder,
281
- :order => order,
282
- :contract => contract,
283
- :id => @next_order_id
284
280
  order.client_id = server[:client_id]
285
281
  order.order_id = @next_order_id
286
282
  @next_order_id += 1
283
+ modify_order order, contract
284
+ end
285
+
286
+ # Modify Order (convenience wrapper for send_message :PlaceOrder). Returns order_id.
287
+ def modify_order order, contract
288
+ send_message :PlaceOrder,
289
+ :order => order,
290
+ :contract => contract,
291
+ :order_id => order.order_id
287
292
  order.order_id
288
293
  end
289
294
 
290
295
  # Cancel Orders by their ids (convenience wrapper for send_message :CancelOrder).
291
296
  def cancel_order *order_ids
292
297
  order_ids.each do |order_id|
293
- send_message :CancelOrder, :id => order_id.to_i
298
+ send_message :CancelOrder, :order_id => order_id.to_i
294
299
  end
295
300
  end
296
301
 
@@ -106,14 +106,14 @@ module IB
106
106
  # Never happens! 28 is the max supported version currently
107
107
  # As of client v.55, we receive orderComboLegs (price) in openOrder
108
108
  [29, [:contract, :legs, :array, proc do |_|
109
- Models::ComboLeg.new :con_id => socket.read_int,
110
- :ratio => socket.read_int,
111
- :action => socket.read_string,
112
- :exchange => socket.read_string,
113
- :open_close => socket.read_int,
114
- :short_sale_slot => socket.read_int,
115
- :designated_location => socket.read_string,
116
- :exempt_code => socket.read_int
109
+ IB::ComboLeg.new :con_id => socket.read_int,
110
+ :ratio => socket.read_int,
111
+ :action => socket.read_string,
112
+ :exchange => socket.read_string,
113
+ :open_close => socket.read_int,
114
+ :short_sale_slot => socket.read_int,
115
+ :designated_location => socket.read_string,
116
+ :exempt_code => socket.read_int
117
117
  end],
118
118
 
119
119
  # Order keeps received leg prices in a separate Array for some reason ?!
@@ -167,17 +167,19 @@ module IB
167
167
 
168
168
  [:order, :what_if, :boolean],
169
169
  [:order, :status, :string],
170
- [:order, :init_margin, :string],
171
- [:order, :maint_margin, :string],
172
- [:order, :equity_with_loan, :string],
170
+
171
+ # IB uses weird String with Java Double.MAX_VALUE to indicate no value here
172
+ [:order, :init_margin, :decimal_max], # :string],
173
+ [:order, :maint_margin, :decimal_max], # :string],
174
+ [:order, :equity_with_loan, :decimal_max], # :string],
173
175
  [:order, :commission, :decimal_max], # May be nil!
174
176
  [:order, :min_commission, :decimal_max], # May be nil!
175
177
  [:order, :max_commission, :decimal_max], # May be nil!
176
178
  [:order, :commission_currency, :string],
177
179
  [:order, :warning_text, :string]
178
180
 
179
- @order = Models::Order.new @data[:order]
180
- @contract = Models::Contract.build @data[:contract]
181
+ @order = IB::Order.new @data[:order]
182
+ @contract = IB::Contract.build @data[:contract]
181
183
  end
182
184
 
183
185
  # Check if given value was set by TWS to something vaguely "positive"
@@ -91,7 +91,7 @@ module IB
91
91
  [:dividends_to_expiry, :decimal]
92
92
 
93
93
  # This message is received when the market in an option or its underlier moves.
94
- # TWS�s option model volatilities, prices, and deltas, along with the present
94
+ # TWS option model volatilities, prices, and deltas, along with the present
95
95
  # value of dividends expected on that options underlier are received.
96
96
  # TickOption message contains following @data:
97
97
  # :ticker_id - Id that was specified previously in the call to reqMktData()
@@ -149,8 +149,8 @@ module IB
149
149
  [:why_held, :string] do
150
150
  "<OrderStatus: #{status} filled: #{filled}/#{remaining + filled}" +
151
151
  " @ last/avg: #{last_fill_price}/#{average_fill_price}" +
152
- (parent_id > 0 ? "parent_id: #{parent_id}" : "") +
153
- (why_held != "" ? "why_held: #{why_held}" : "") +
152
+ (parent_id > 0 ? " parent_id: #{parent_id}" : "") +
153
+ (why_held != "" ? " why_held: #{why_held}" : "") +
154
154
  " id/perm: #{order_id}/#{perm_id}>"
155
155
  end
156
156
 
@@ -302,7 +302,7 @@ module IB
302
302
 
303
303
  def load
304
304
  super
305
- @contract = Models::Contract.build @data[:contract]
305
+ @contract = IB::Contract.build @data[:contract]
306
306
  end
307
307
 
308
308
  def to_human
@@ -346,7 +346,7 @@ module IB
346
346
  class ContractData
347
347
  def load
348
348
  super
349
- @contract = Models::Contract.build @data[:contract]
349
+ @contract = IB::Contract.build @data[:contract]
350
350
  end
351
351
  end # ContractData
352
352
 
@@ -386,8 +386,8 @@ module IB
386
386
  load_map [proc { | | @server[:client_version] >= 53 },
387
387
  [:execution, :order_ref, :string]
388
388
  ]
389
- @contract = Models::Contract.build @data[:contract]
390
- @execution = Models::Execution.new @data[:execution]
389
+ @contract = IB::Contract.build @data[:contract]
390
+ @execution = IB::Execution.new @data[:execution]
391
391
  end
392
392
 
393
393
  def to_human
@@ -429,7 +429,7 @@ module IB
429
429
  class BondContractData
430
430
  def load
431
431
  super
432
- @contract = Models::Contract.build @data[:contract]
432
+ @contract = IB::Contract.build @data[:contract]
433
433
  end
434
434
  end # BondContractData
435
435
 
@@ -443,7 +443,7 @@ module IB
443
443
  class DeltaNeutralValidation
444
444
  def load
445
445
  super
446
- @contract = Models::Contract.build @data[:contract].merge(:under_comp => true)
446
+ @contract = IB::Contract.build @data[:contract].merge(:under_comp => true)
447
447
  end
448
448
  end # DeltaNeutralValidation
449
449
 
@@ -465,7 +465,7 @@ module IB
465
465
  class RealTimeBar
466
466
  def load
467
467
  super
468
- @bar = Models::Bar.new @data[:bar]
468
+ @bar = IB::Bar.new @data[:bar]
469
469
  end
470
470
 
471
471
  def to_human
@@ -549,15 +549,15 @@ module IB
549
549
  super
550
550
 
551
551
  @results = Array.new(@data[:count]) do |_|
552
- Models::Bar.new :time => socket.read_string,
553
- :open => socket.read_decimal,
554
- :high => socket.read_decimal,
555
- :low => socket.read_decimal,
556
- :close => socket.read_decimal,
557
- :volume => socket.read_int,
558
- :wap => socket.read_decimal,
559
- :has_gaps => socket.read_string,
560
- :trades => socket.read_int
552
+ IB::Bar.new :time => socket.read_string,
553
+ :open => socket.read_decimal,
554
+ :high => socket.read_decimal,
555
+ :low => socket.read_decimal,
556
+ :close => socket.read_decimal,
557
+ :volume => socket.read_int,
558
+ :wap => socket.read_decimal,
559
+ :has_gaps => socket.read_string,
560
+ :trades => socket.read_int
561
561
  end
562
562
  end
563
563