wdevauld-ib-ruby 0.2
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.
- data/History.txt +5 -0
- data/LICENSE +504 -0
- data/Manifest.txt +20 -0
- data/README.txt +50 -0
- data/Rakefile +33 -0
- data/bin/AccountInfo +71 -0
- data/bin/HistoricToCSV +129 -0
- data/bin/RequestHistoricData +312 -0
- data/bin/RequestMarketData +80 -0
- data/bin/SimpleTimeAndSales +101 -0
- data/bin/ib-ruby +8 -0
- data/lib/ib-ruby.rb +49 -0
- data/lib/ib-ruby/datatypes.rb +402 -0
- data/lib/ib-ruby/ib.rb +242 -0
- data/lib/ib-ruby/messages.rb +1449 -0
- data/lib/ib-ruby/symbols/forex.rb +62 -0
- data/lib/ib-ruby/symbols/futures.rb +114 -0
- data/spec/ib-ruby_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/test/test_ib-ruby.rb +162 -0
- metadata +96 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/AccountInfo
|
7
|
+
bin/HistoricToCSV
|
8
|
+
bin/RequestHistoricData
|
9
|
+
bin/RequestMarketData
|
10
|
+
bin/SimpleTimeAndSales
|
11
|
+
bin/ib-ruby
|
12
|
+
lib/ib-ruby.rb
|
13
|
+
lib/ib-ruby/datatypes.rb
|
14
|
+
lib/ib-ruby/ib.rb
|
15
|
+
lib/ib-ruby/messages.rb
|
16
|
+
lib/ib-ruby/symbols/forex.rb
|
17
|
+
lib/ib-ruby/symbols/futures.rb
|
18
|
+
spec/ib-ruby_spec.rb
|
19
|
+
spec/spec_helper.rb
|
20
|
+
test/test_ib-ruby.rb
|
data/README.txt
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
ib-ruby
|
2
|
+
By Wes Devauld (wes at devauld dot ca)
|
3
|
+
http://github.com/wdevauld/ib-ruby
|
4
|
+
|
5
|
+
This is a fork of Paul Legato's (pjlegato at gmail dot com) work found at:
|
6
|
+
http://github.com/pjlegato/ib-ruby
|
7
|
+
|
8
|
+
Copyright (C) 2009 Wes Devauld
|
9
|
+
|
10
|
+
== DESCRIPTION:
|
11
|
+
|
12
|
+
* Ruby Implementation of the Interactive Broker' TWS API
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
* This is a ALPHA release, and should not be used for live trading. Any features contained with are AS-IS and may not work in all conditions
|
17
|
+
* This code is not sanctioned by Interactive Brokers
|
18
|
+
* TODO Deal with Logging properly
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
First, start up Interactive Broker's Trader Work Station. Ensure it is configured to allow API connections on localhost
|
22
|
+
|
23
|
+
>> require 'ib-ruby'
|
24
|
+
>> ib_connection = IB:IB.new()
|
25
|
+
|
26
|
+
== REQUIREMENTS:
|
27
|
+
|
28
|
+
* FIXME List all the requirements
|
29
|
+
|
30
|
+
== INSTALL:
|
31
|
+
|
32
|
+
* Ensure that http://gems.github.com is in your gem sources
|
33
|
+
* sudo gem install wdevauld-ib-ruby
|
34
|
+
|
35
|
+
== LICENSE:
|
36
|
+
|
37
|
+
This library is free software; you can redistribute it and/or modify
|
38
|
+
it under the terms of the GNU Lesser General Public License as
|
39
|
+
published by the Free Software Foundation; either version 2.1 of the
|
40
|
+
License, or (at your option) any later version.
|
41
|
+
|
42
|
+
This library is distributed in the hope that it will be useful, but
|
43
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
44
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
45
|
+
Lesser General Public License for more details.
|
46
|
+
|
47
|
+
You should have received a copy of the GNU Lesser General Public
|
48
|
+
License along with this library; if not, write to the Free Software
|
49
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
50
|
+
02110-1301 USA
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# This library is free software; you can redistribute it and/or
|
2
|
+
# modify it under the terms of the GNU Lesser General Public
|
3
|
+
# License see README.txt for more details
|
4
|
+
|
5
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
6
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
7
|
+
# are where the options are used.
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'bones'
|
11
|
+
Bones.setup
|
12
|
+
rescue LoadError
|
13
|
+
begin
|
14
|
+
load 'tasks/setup.rb'
|
15
|
+
rescue LoadError
|
16
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ensure_in_path 'lib'
|
21
|
+
require 'ib-ruby'
|
22
|
+
|
23
|
+
task :default => 'spec:run'
|
24
|
+
|
25
|
+
PROJ.name = 'ib-ruby'
|
26
|
+
PROJ.authors = 'Wes Devauld'
|
27
|
+
PROJ.email = 'wes@devauld.ca'
|
28
|
+
PROJ.url = 'http://github.com/wdevauld/ib-ruby/tree/master'
|
29
|
+
PROJ.version = IbRuby::VERSION
|
30
|
+
|
31
|
+
PROJ.spec.opts << '--color'
|
32
|
+
|
33
|
+
# EOF
|
data/bin/AccountInfo
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Paul Legato.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2.1 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful, but
|
11
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18
|
+
# 02110-1301 USA
|
19
|
+
#
|
20
|
+
|
21
|
+
$:.push(File.dirname(__FILE__) + "/../")
|
22
|
+
|
23
|
+
require 'ib'
|
24
|
+
require 'datatypes'
|
25
|
+
require 'symbols/futures'
|
26
|
+
|
27
|
+
# First, connect to IB TWS.
|
28
|
+
ib = IB::IB.new
|
29
|
+
|
30
|
+
# Uncomment this for verbose debug messages:
|
31
|
+
# IB::IBLogger.level = Logger::Severity::DEBUG
|
32
|
+
|
33
|
+
## Subscribe to the messages that TWS sends in response to a request
|
34
|
+
## for account data.
|
35
|
+
|
36
|
+
ib.subscribe(IB::IncomingMessages::AccountValue, lambda {|msg|
|
37
|
+
puts msg.to_human
|
38
|
+
})
|
39
|
+
|
40
|
+
ib.subscribe(IB::IncomingMessages::PortfolioValue, lambda {|msg|
|
41
|
+
puts msg.to_human
|
42
|
+
})
|
43
|
+
|
44
|
+
ib.subscribe(IB::IncomingMessages::AccountUpdateTime, lambda {|msg|
|
45
|
+
puts msg.to_human
|
46
|
+
})
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
msg = IB::OutgoingMessages::RequestAccountData.new({
|
51
|
+
:subscribe => true,
|
52
|
+
:account_code => ''
|
53
|
+
})
|
54
|
+
ib.dispatch(msg)
|
55
|
+
|
56
|
+
|
57
|
+
puts "\n\n\t******** Press <Enter> to quit.. *********\n\n"
|
58
|
+
|
59
|
+
gets
|
60
|
+
|
61
|
+
puts "Cancelling account data subscription.."
|
62
|
+
|
63
|
+
msg = IB::OutgoingMessages::RequestAccountData.new({
|
64
|
+
:subscribe => false,
|
65
|
+
:account_code => ''
|
66
|
+
})
|
67
|
+
ib.dispatch(msg)
|
68
|
+
|
69
|
+
|
70
|
+
puts "Done."
|
71
|
+
|
data/bin/HistoricToCSV
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Paul Legato.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2.1 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful, but
|
11
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18
|
+
# 02110-1301 USA
|
19
|
+
#
|
20
|
+
#####################################################################
|
21
|
+
#
|
22
|
+
# This program demonstrates how to download historic data and write it
|
23
|
+
# to a CSV file.
|
24
|
+
#
|
25
|
+
# To use, set CSV_FILE to the file you want to write (which will be
|
26
|
+
# overwritten automatically if it already exists), set the contract
|
27
|
+
# data, duration, data type (trades, bid, ask, midpoint), etc. as you
|
28
|
+
# like, and run the program.
|
29
|
+
#
|
30
|
+
# Note that it does not detect when the historic data from the server
|
31
|
+
# has stopped arriving automatically. This limitation will be
|
32
|
+
# addressed soon. For now, just press <Enter> when the data stream on
|
33
|
+
# the console stops, and the output file will be closed and the
|
34
|
+
# program terminated.
|
35
|
+
#
|
36
|
+
|
37
|
+
$:.push(File.dirname(__FILE__) + "/../")
|
38
|
+
|
39
|
+
require 'ib'
|
40
|
+
require 'datatypes'
|
41
|
+
require 'symbols/futures'
|
42
|
+
|
43
|
+
|
44
|
+
### Configurable Options
|
45
|
+
|
46
|
+
# if Quiet == false, status data will be printed to STDERR
|
47
|
+
Quiet = false
|
48
|
+
|
49
|
+
# How long to wait when no messages are received from TWS before
|
50
|
+
# exiting, in seconds
|
51
|
+
Timeout = 2
|
52
|
+
|
53
|
+
SymbolToRequest = IB::Symbols::Futures[:eur]
|
54
|
+
|
55
|
+
### end options
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# Definition of what we want market data for. We have to keep track
|
60
|
+
# of what ticker id corresponds to what symbol ourselves, because the
|
61
|
+
# ticks don't include any other identifying information.
|
62
|
+
#
|
63
|
+
# The choice of ticker ids is, as far as I can tell, arbitrary.
|
64
|
+
#
|
65
|
+
# Note that as of 4/07 there is no historical data available for forex spot.
|
66
|
+
#
|
67
|
+
@market =
|
68
|
+
{
|
69
|
+
123 => SymbolToRequest
|
70
|
+
}
|
71
|
+
|
72
|
+
# To determine when the timeout has passed.
|
73
|
+
@last_msg_time = Time.now.to_i + 2
|
74
|
+
|
75
|
+
# Connect to IB TWS.
|
76
|
+
ib = IB::IB.new
|
77
|
+
|
78
|
+
# Uncomment this for verbose debug messages:
|
79
|
+
# IB::IBLogger.level = Logger::Severity::DEBUG
|
80
|
+
|
81
|
+
#
|
82
|
+
# Now, subscribe to HistoricalData incoming events. The code
|
83
|
+
# passed in the block will be executed when a message of that type is
|
84
|
+
# received, with the received message as its argument. In this case,
|
85
|
+
# we just print out the data.
|
86
|
+
#
|
87
|
+
# Note that we have to look the ticker id of each incoming message
|
88
|
+
# up in local memory to figure out what it's for.
|
89
|
+
#
|
90
|
+
# (N.B. The description field is not from IB TWS. It is defined
|
91
|
+
# locally in forex.rb, and is just arbitrary text.)
|
92
|
+
|
93
|
+
|
94
|
+
ib.subscribe(IB::IncomingMessages::HistoricalData, lambda {|msg|
|
95
|
+
|
96
|
+
STDERR.puts @market[msg.data[:req_id]].description + ": " + msg.data[:item_count].to_s + " items:" unless Quiet
|
97
|
+
|
98
|
+
msg.data[:history].each { |datum|
|
99
|
+
|
100
|
+
@last_msg_time = Time.now.to_i
|
101
|
+
|
102
|
+
STDERR.puts " " + datum.to_s unless Quiet
|
103
|
+
STDOUT.puts "#{datum.date},#{datum.open.to_digits},#{datum.high.to_digits},#{datum.low.to_digits},#{datum.close.to_digits},#{datum.volume}"
|
104
|
+
}
|
105
|
+
})
|
106
|
+
|
107
|
+
# Now we actually request historical data for the symbols we're
|
108
|
+
# interested in. TWS will respond with a HistoricalData message,
|
109
|
+
# which will be received by the code above.
|
110
|
+
|
111
|
+
@market.each_pair {|id, contract|
|
112
|
+
msg = IB::OutgoingMessages::RequestHistoricalData.new({
|
113
|
+
:ticker_id => id,
|
114
|
+
:contract => contract,
|
115
|
+
:end_date_time => Time.now.to_ib,
|
116
|
+
:duration => (60 * 60 * 24).to_s, # how long before end_date_time to request in seconds - this means 1 day
|
117
|
+
:bar_size => IB::OutgoingMessages::RequestHistoricalData::BarSizes.index(:five_minutes),
|
118
|
+
:what_to_show => :trades,
|
119
|
+
:use_RTH => 0,
|
120
|
+
:format_date => 2
|
121
|
+
})
|
122
|
+
ib.dispatch(msg)
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
while true
|
127
|
+
exit(0) if Time.now.to_i > @last_msg_time + Timeout
|
128
|
+
sleep 1
|
129
|
+
end
|
@@ -0,0 +1,312 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007-8 Paul Legato. pjlegato at gmail dot com.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2.1 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful, but
|
11
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18
|
+
# 02110-1301 USA
|
19
|
+
#
|
20
|
+
# >> YOUR USE OF THIS PROGRAM IS ENTIRELY AT YOUR OWN RISK. <<
|
21
|
+
# >> IT MAY CONTAIN POTENTIALLY COSTLY BUGS, ERRORS, ETC., BOTH KNOWN AND UNKNOWN. <<
|
22
|
+
#
|
23
|
+
|
24
|
+
$:.push(File.dirname(__FILE__) + "/../")
|
25
|
+
|
26
|
+
# IB-Ruby libraries
|
27
|
+
require 'ib'
|
28
|
+
require 'datatypes'
|
29
|
+
require 'symbols/futures'
|
30
|
+
|
31
|
+
# Stdlib
|
32
|
+
require 'time' # for extended time parsing
|
33
|
+
|
34
|
+
# Gems - requires duration and getopt.
|
35
|
+
require 'rubygems'
|
36
|
+
require 'duration'
|
37
|
+
require 'getopt/long'
|
38
|
+
|
39
|
+
|
40
|
+
require "getopt/long"
|
41
|
+
include Getopt
|
42
|
+
opt = Getopt::Long.getopts(
|
43
|
+
["--help", BOOLEAN],
|
44
|
+
["--end", REQUIRED],
|
45
|
+
["--security", REQUIRED],
|
46
|
+
["--duration", REQUIRED],
|
47
|
+
["--barsize", REQUIRED],
|
48
|
+
["--header",BOOLEAN],
|
49
|
+
["--dateformat", REQUIRED],
|
50
|
+
["--nonregularhours", BOOLEAN],
|
51
|
+
["--verbose", BOOLEAN],
|
52
|
+
["--veryverbose", BOOLEAN]
|
53
|
+
)
|
54
|
+
|
55
|
+
if opt["help"] || opt["security"].nil? || opt["security"].empty?
|
56
|
+
puts <<ENDHELP
|
57
|
+
|
58
|
+
** RequestHistoricData.rb - Copyright (C) 2007-8 Paul Legato.
|
59
|
+
|
60
|
+
This library is free software; you can redistribute it and/or modify
|
61
|
+
it under the terms of the GNU Lesser General Public License as
|
62
|
+
published by the Free Software Foundation; either version 2.1 of the
|
63
|
+
License, or (at your option) any later version.
|
64
|
+
|
65
|
+
This library is distributed in the hope that it will be useful, but
|
66
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
67
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
68
|
+
Lesser General Public License for more details.
|
69
|
+
|
70
|
+
You should have received a copy of the GNU Lesser General Public
|
71
|
+
License along with this library; if not, write to the Free Software
|
72
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
73
|
+
02110-1301 USA
|
74
|
+
|
75
|
+
The author and this software are not connected with Interactive
|
76
|
+
Brokers in any way, nor do they endorse us.
|
77
|
+
|
78
|
+
************************************************************************************
|
79
|
+
|
80
|
+
>> YOUR USE OF THIS PROGRAM IS ENTIRELY AT YOUR OWN RISK. <<
|
81
|
+
>> IT MAY CONTAIN POTENTIALLY COSTLY BUGS, ERRORS, ETC., BOTH KNOWN AND UNKNOWN. <<
|
82
|
+
>> DO NOT USE THIS SOFTWARE IF YOU ARE UNWILLING TO ACCEPT ALL RISK IN DOING SO. <<
|
83
|
+
|
84
|
+
************************************************************************************
|
85
|
+
|
86
|
+
|
87
|
+
This program requires a TWS running on localhost on the standard port
|
88
|
+
that uses API protocol version 15 or higher. Any modern TWS should
|
89
|
+
work. (Patches to make it work on an arbitrary host/port are welcome.)
|
90
|
+
|
91
|
+
----------
|
92
|
+
|
93
|
+
One argument is required: --security, the security specification you want, in
|
94
|
+
"long serialized IB-Ruby" format. This is a colon-separated string of the format:
|
95
|
+
|
96
|
+
symbol:security_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
|
97
|
+
|
98
|
+
Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
|
99
|
+
|
100
|
+
For example, to query the British pound futures contract trading on Globex expiring in September, 2008,
|
101
|
+
the correct command line is:
|
102
|
+
|
103
|
+
./RequestHistoricData.rb --security GBP:FUT:200809:::62500:GLOBEX::USD:
|
104
|
+
|
105
|
+
Consult datatypes.rb for allowed values, and see also the examples in the symbols/ directory (load them in
|
106
|
+
irb and run security#serialize_ib_ruby(ib_version) to see the appropriate string.)
|
107
|
+
|
108
|
+
***
|
109
|
+
|
110
|
+
Options:
|
111
|
+
|
112
|
+
--end is is the last time we want data for. The default is now.
|
113
|
+
This is eval'ed by Ruby, so you can use a Ruby expression, which must return a Time object.
|
114
|
+
|
115
|
+
|
116
|
+
--duration is how much historic data we want, in seconds, before --end's time.
|
117
|
+
The default is 86400 (seconds, which is 1 day.)
|
118
|
+
The TWS-imposed limit is 86400 (1 day per request.) Requests for more than 86400 seconds worth of historic data will fail.
|
119
|
+
|
120
|
+
--what determines what the data will be comprised of. This can be "trades", "midpoint", "bid", or "asked".
|
121
|
+
The default is "trades".
|
122
|
+
|
123
|
+
--barsize determines how long each bar will be.
|
124
|
+
|
125
|
+
Possible values (from the IB documentation):
|
126
|
+
|
127
|
+
1 = 1 sec
|
128
|
+
2 = 5 sec
|
129
|
+
3 = 15 sec
|
130
|
+
4 = 30 sec
|
131
|
+
5 = 1 minute
|
132
|
+
6 = 2 minutes
|
133
|
+
7 = 5 minutes
|
134
|
+
8 = 15 minutes
|
135
|
+
9 = 30 minutes
|
136
|
+
10 = 1 hour
|
137
|
+
11 = 1 day
|
138
|
+
|
139
|
+
Values less than 4 do not appear to work for some securities.
|
140
|
+
The default is 8, 15 minutes.
|
141
|
+
|
142
|
+
--nonregularhours :
|
143
|
+
Normally, only data from the instrument's regular trading hours is returned.
|
144
|
+
If --nonregularhours is given, all data available during the time
|
145
|
+
span requested is returned, even data bars covering time
|
146
|
+
intervals where the market in question was illiquid. If
|
147
|
+
|
148
|
+
|
149
|
+
--dateformat : a --dateformat of 1 will cause the dates in the returned
|
150
|
+
messages with the historic data to be in a text format, like
|
151
|
+
"20050307 11:32:16". If you set it to 2 instead, you
|
152
|
+
will get an offset in seconds from the beginning of 1970, which
|
153
|
+
is the same format as the UNIX epoch time.
|
154
|
+
|
155
|
+
The default is 1 (human-readable time.)
|
156
|
+
|
157
|
+
--header : if present, prints a 1 line CSV header describing the fields in the CSV.
|
158
|
+
|
159
|
+
--veryverbose : if present, prints very verbose debugging info.
|
160
|
+
--verbose : if present, prints all messages received from IB, and print the data in human-readable
|
161
|
+
format.
|
162
|
+
|
163
|
+
Otherwise, in the default mode, prints only the historic data (and any errors), and prints the
|
164
|
+
data in CSV format.
|
165
|
+
|
166
|
+
ENDHELP
|
167
|
+
#' <- fix broken syntax highlighting in Aquamacs
|
168
|
+
exit
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
### Parameters
|
173
|
+
|
174
|
+
# DURATION is how much historic data we want, in seconds, before END_DATE_TIME.
|
175
|
+
# (The 'duration' gem gives us methods like #hour on integers.)
|
176
|
+
DURATION = (opt["duration"] && opt["duration"].to_i) || 1.day
|
177
|
+
|
178
|
+
if DURATION > 86400
|
179
|
+
STDERR.puts("\nTWS does not accept a --duration longer than 86400 seconds (1 day.) Please try again with a smaller duration.\n\n")
|
180
|
+
exit(1)
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
# This is the last time we want data for.
|
185
|
+
END_DATE_TIME = (opt["end"] && eval(opt["end"]).to_ib) || Time.now.to_ib
|
186
|
+
|
187
|
+
|
188
|
+
# This can be :trades, :midpoint, :bid, or :asked
|
189
|
+
WHAT = (opt["what"] && opt["what"].to_sym) || :trades
|
190
|
+
|
191
|
+
# Possible bar size values:
|
192
|
+
# 1 = 1 sec
|
193
|
+
# 2 = 5 sec
|
194
|
+
# 3 = 15 sec
|
195
|
+
# 4 = 30 sec
|
196
|
+
# 5 = 1 minute
|
197
|
+
# 6 = 2 minutes
|
198
|
+
# 7 = 5 minutes
|
199
|
+
# 8 = 15 minutes
|
200
|
+
# 9 = 30 minutes
|
201
|
+
# 10 = 1 hour
|
202
|
+
# 11 = 1 day
|
203
|
+
#
|
204
|
+
# Values less than 4 do not appear to actually work; they are rejected by the server.
|
205
|
+
#
|
206
|
+
BAR_SIZE = (opt["barsize"] && opt["barsize"].to_i) || 8
|
207
|
+
|
208
|
+
# If REGULAR_HOURS_ONLY is set to 0, all data available during the time
|
209
|
+
# span requested is returned, even data bars covering time
|
210
|
+
# intervals where the market in question was illiquid. If useRTH
|
211
|
+
# has a non-zero value, only data within the "Regular Trading
|
212
|
+
# Hours" of the product in question is returned, even if the time
|
213
|
+
# span requested falls partially or completely outside of them.
|
214
|
+
|
215
|
+
REGULAR_HOURS_ONLY = opt["nonregularhours"] ? 0 : 1
|
216
|
+
|
217
|
+
# Using a DATE_FORMAT of 1 will cause the dates in the returned
|
218
|
+
# messages with the historic data to be in a text format, like
|
219
|
+
# "20050307 11:32:16". If you set :format_date to 2 instead, you
|
220
|
+
# will get an offset in seconds from the beginning of 1970, which
|
221
|
+
# is the same format as the UNIX epoch time.
|
222
|
+
|
223
|
+
DATE_FORMAT = (opt["dateformat"] && opt["dateformat"].to_i) || 1
|
224
|
+
|
225
|
+
VERYVERBOSE = !opt["veryverbose"].nil?
|
226
|
+
VERBOSE = !opt["verbose"].nil?
|
227
|
+
|
228
|
+
#
|
229
|
+
# Definition of what we want market data for. We have to keep track
|
230
|
+
# of what ticker id corresponds to what symbol ourselves, because the
|
231
|
+
# ticks don't include any other identifying information.
|
232
|
+
#
|
233
|
+
# The choice of ticker ids is, as far as I can tell, arbitrary.
|
234
|
+
#
|
235
|
+
# Note that as of 4/07 there is no historical data available for forex spot.
|
236
|
+
#
|
237
|
+
@market =
|
238
|
+
{
|
239
|
+
123 => opt["security"]
|
240
|
+
}
|
241
|
+
|
242
|
+
|
243
|
+
# First, connect to IB TWS.
|
244
|
+
ib = IB::IB.new
|
245
|
+
|
246
|
+
|
247
|
+
# Default level is quiet, only warnings printed.
|
248
|
+
IB::IBLogger.level = Logger::Severity::ERROR
|
249
|
+
|
250
|
+
# For verbose printing of each message:
|
251
|
+
IB::IBLogger.level = Logger::Severity::INFO if VERBOSE
|
252
|
+
|
253
|
+
# For very verbose debug messages:
|
254
|
+
IB::IBLogger.level = Logger::Severity::DEBUG if VERYVERBOSE
|
255
|
+
|
256
|
+
puts "datetime,open,high,low,close,volume,wap,has_gaps" if !opt["header"].nil?
|
257
|
+
|
258
|
+
lastMessageTime = Queue.new # for communicating with the reader thread.
|
259
|
+
|
260
|
+
#
|
261
|
+
# Subscribe to incoming HistoricalData events. The code passed in the
|
262
|
+
# block will be executed when a message of the subscribed type is
|
263
|
+
# received, with the received message as its argument. In this case,
|
264
|
+
# we just print out the data.
|
265
|
+
#
|
266
|
+
# Note that we have to look the ticker id of each incoming message
|
267
|
+
# up in local memory to figure out what security it relates to.
|
268
|
+
# The incoming message packet from TWS just identifies it by ticker id.
|
269
|
+
#
|
270
|
+
ib.subscribe(IB::IncomingMessages::HistoricalData, lambda {|msg|
|
271
|
+
STDERR.puts @market[msg.data[:req_id]].description + ": " + msg.data[:item_count].to_s + " items:" if VERBOSE
|
272
|
+
|
273
|
+
msg.data[:history].each { |datum|
|
274
|
+
puts(if VERBOSE
|
275
|
+
datum.to_s
|
276
|
+
else
|
277
|
+
"#{datum.date},#{datum.open.to_digits},#{datum.high.to_digits},#{datum.low.to_digits}," +
|
278
|
+
"#{datum.close.to_digits},#{datum.volume},#{datum.wap.to_digits},#{datum.has_gaps}"
|
279
|
+
end
|
280
|
+
)
|
281
|
+
}
|
282
|
+
lastMessageTime.push(Time.now)
|
283
|
+
})
|
284
|
+
|
285
|
+
# Now we actually request historical data for the symbols we're
|
286
|
+
# interested in. TWS will respond with a HistoricalData message,
|
287
|
+
# which will be received by the code above.
|
288
|
+
|
289
|
+
@market.each_pair {|id, contract|
|
290
|
+
msg = IB::OutgoingMessages::RequestHistoricalData.new({
|
291
|
+
:ticker_id => id,
|
292
|
+
:contract => contract,
|
293
|
+
:end_date_time => END_DATE_TIME,
|
294
|
+
:duration => DURATION, # seconds == 1 hour
|
295
|
+
:bar_size => BAR_SIZE, # 1 minute bars
|
296
|
+
:what_to_show => WHAT,
|
297
|
+
:use_RTH => REGULAR_HOURS_ONLY,
|
298
|
+
:format_date => DATE_FORMAT
|
299
|
+
})
|
300
|
+
ib.dispatch(msg)
|
301
|
+
}
|
302
|
+
|
303
|
+
|
304
|
+
# A complication here is that IB does not send any indication when all historic data is done being delivered.
|
305
|
+
# So we have to guess - when there is no more new data for some period, we interpret that as "end of data" and exit.
|
306
|
+
|
307
|
+
while true
|
308
|
+
lastTime = lastMessageTime.pop # blocks until a message is ready on the queue
|
309
|
+
sleep 2 # .. wait ..
|
310
|
+
exit if lastMessageTime.empty? # if still no more messages after 2 more seconds, exit.
|
311
|
+
end
|
312
|
+
|