betfair 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/README +94 -42
- data/lib/betfair/api.rb +244 -228
- data/lib/betfair/version.rb +1 -1
- data/spec/betfair/api_spec.rb +49 -7
- data/spec/fixtures/get_account_funds/success.xml +28 -0
- data/spec/spec_helper.rb +2 -0
- metadata +14 -12
data/.gitignore
CHANGED
data/README
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
===========
|
2
1
|
BETFAIR API
|
3
2
|
===========
|
4
3
|
|
@@ -6,94 +5,134 @@ BETFAIR API
|
|
6
5
|
Installation
|
7
6
|
------------
|
8
7
|
|
9
|
-
Betfair is available through
|
8
|
+
Betfair is available through
|
9
|
+
[Rubygems](http://rubygems.org/gems/betfair) and can be installed via:
|
10
|
+
|
11
|
+
gem install betfair
|
10
12
|
|
11
|
-
|
13
|
+
OR with bundler: `gem 'betfair'` and `bundle install`
|
12
14
|
|
13
15
|
------------
|
14
16
|
Introduction
|
15
17
|
------------
|
16
|
-
irb
|
17
18
|
|
18
|
-
|
19
|
+
irb
|
19
20
|
|
20
|
-
|
21
|
+
require 'betfair'
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
# create a client for the General API so that you can log in.
|
25
|
-
bf = Betfair::API.new
|
23
|
+
or from a Gemfile
|
26
24
|
|
27
|
-
|
25
|
+
gem 'betfair'
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
bf = Betfair::API.new(proxy, logging)
|
27
|
+
# create a client for the General API so that you can log in.
|
28
|
+
bf = Betfair::API.new
|
32
29
|
|
33
|
-
|
30
|
+
If you want to use a proxy or turn on Savon's logging then just pass
|
31
|
+
in like so:
|
34
32
|
|
35
|
-
|
33
|
+
# This is a local squid proxy I tunnel to from my local
|
34
|
+
# machine to access the host server in UK for dev purposes.
|
35
|
+
proxy = 'http://localhost:8888'
|
36
|
+
logging = true
|
37
|
+
bf = Betfair::API.new(proxy, logging)
|
36
38
|
|
37
|
-
|
39
|
+
Proxies can be useful if you want to host on a cloud service such as
|
40
|
+
Heroku, as you will be denied access to the Betfair API from the
|
41
|
+
USA. Just proxy via a server from a country that Betfair allows, such
|
42
|
+
as the UK.
|
38
43
|
|
39
|
-
|
44
|
+
At the heart of the Betfair API is the `session_token`. In order to
|
45
|
+
get one of these simply call:
|
40
46
|
|
41
|
-
session_token = bf.login('username', 'password', 82, 0, 0, nil)
|
47
|
+
session_token = bf.login('username', 'password', 82, 0, 0, nil)
|
42
48
|
|
43
49
|
Username and Password are fairly obvious.
|
44
50
|
|
45
|
-
82 is the standard Product Id, you may have a different one depending
|
51
|
+
82 is the standard Product Id, you may have a different one depending
|
52
|
+
on the level of Betfair API access that you have.
|
46
53
|
|
47
|
-
You can ignore the rest as leave as is, but they refer to Vendor
|
54
|
+
You can ignore the rest as leave as is, but they refer to Vendor
|
55
|
+
Software Id, Location Id, Ip Address as required by the Betfair API.
|
48
56
|
|
49
57
|
-----
|
50
58
|
Read
|
51
59
|
-----
|
52
60
|
|
53
|
-
markets =
|
61
|
+
markets =
|
62
|
+
bf.get_all_markets(session_token, 1, [1,3], nil, nil, nil, nil)
|
63
|
+
|
64
|
+
# We can either pull the market ID out of `markets`, or use a
|
65
|
+
# stored value:
|
66
|
+
|
67
|
+
market_id = 100386338
|
68
|
+
exchange_id = 1 # or 2 for the Australian exchange
|
69
|
+
|
70
|
+
# get_market returns a Hash with the details of the identified
|
71
|
+
# market, including the commission as
|
72
|
+
# `details[:market_base_rate]`, and information on the runners
|
73
|
+
# (including their ids, which you need to place a bet) as
|
74
|
+
# `details[:runners]`.
|
75
|
+
|
76
|
+
details =
|
77
|
+
bf.get_market(session_token, exchange_id, market_id)
|
78
|
+
|
79
|
+
# This returns an encoded string with some back prices and depths
|
80
|
+
# for the given market.
|
54
81
|
|
55
|
-
|
56
|
-
|
82
|
+
prices = bf.get_market_prices_compressed(session_token,
|
83
|
+
exchange_id,
|
84
|
+
market_id)
|
57
85
|
|
58
|
-
helpers = Betfair::Helpers.new
|
86
|
+
helpers = Betfair::Helpers.new
|
59
87
|
|
60
|
-
helpers.market_info(details)
|
61
|
-
helpers.combine(details, prices)
|
88
|
+
helpers.market_info(details)
|
89
|
+
helpers.combine(details, prices)
|
62
90
|
|
63
|
-
# The get_all_markets api call returns all markets in one huge
|
64
|
-
|
91
|
+
# The get_all_markets api call returns all markets in one huge
|
92
|
+
# string. This helper provides them all in handy hashes with
|
93
|
+
# market id as key.
|
94
|
+
|
95
|
+
helpers.all_markets(markets)
|
65
96
|
|
66
97
|
---
|
67
98
|
Bet
|
68
99
|
---
|
69
|
-
|
70
|
-
bf.
|
100
|
+
|
101
|
+
bf.place_bet(session_token, 1, 104184109, 58805, 'B', 10.0, 5.0)
|
102
|
+
bf.cancel_bet(session_token, 1, 16939730542)
|
71
103
|
|
72
104
|
------------
|
73
105
|
Requirements
|
74
106
|
------------
|
75
|
-
|
107
|
+
|
108
|
+
savon
|
76
109
|
|
77
110
|
------------------------
|
78
111
|
Requirements for testing
|
79
112
|
------------------------
|
80
|
-
|
81
|
-
|
82
|
-
|
113
|
+
|
114
|
+
savon_spec
|
115
|
+
rspec
|
116
|
+
rake
|
83
117
|
|
84
118
|
----------
|
85
119
|
To Do
|
86
120
|
----------
|
121
|
+
|
122
|
+
- The WOM of money in Helpers#price_string returns 0 if either all b1,b2,b3 or l1,l2,l3 are all 0
|
87
123
|
- Add some error checking to the Betfair::Helper methods
|
88
|
-
- Finish of the mash method, to return a nice hash of all market and
|
124
|
+
- Finish of the mash method, to return a nice hash of all market and
|
125
|
+
runner info
|
89
126
|
- Write a spec for the mashed method
|
90
127
|
|
91
128
|
----------
|
92
129
|
Contribute
|
93
130
|
----------
|
131
|
+
|
94
132
|
I have only added the Betfair API method calls that I need.
|
95
133
|
|
96
|
-
Feel free to fork this repo, add what you need to with the relevant
|
134
|
+
Feel free to fork this repo, add what you need to with the relevant
|
135
|
+
RSpec tests and send me a pull request.
|
97
136
|
|
98
137
|
-------
|
99
138
|
License
|
@@ -103,8 +142,21 @@ License
|
|
103
142
|
|
104
143
|
Copyright (c) 2011 Luke Byrne
|
105
144
|
|
106
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
145
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
146
|
+
a copy of this software and associated documentation files (the
|
147
|
+
'Software'), to deal in the Software without restriction, including
|
148
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
149
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
150
|
+
permit persons to whom the Software is furnished to do so, subject to
|
151
|
+
the following conditions:
|
152
|
+
|
153
|
+
The above copyright notice and this permission notice shall be
|
154
|
+
included in all copies or substantial portions of the Software.
|
155
|
+
|
156
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
157
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
158
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
159
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
160
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
161
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
162
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/betfair/api.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Betfair
|
2
|
-
|
2
|
+
|
3
3
|
class API
|
4
|
-
|
4
|
+
|
5
5
|
def place_bet(session_token, exchange_id, market_id, runner_id, bet_type, price, size)
|
6
6
|
bf_bet = { :marketId => market_id, :selectionId => runner_id, :betType => bet_type, :price => price, :size => size, :asianLineId => 0, :betCategoryType => 'E', :betPersistenceType => 'NONE', :bspLiability => 0 }
|
7
7
|
response = exchange(exchange_id).request :bf, :placeBets do
|
8
8
|
soap.body = { 'bf:request' => { :header => api_request_header(session_token), :bets => { 'PlaceBets' => [bf_bet] } } }
|
9
9
|
end
|
10
10
|
error_code = response.to_hash[:place_bets_response][:result][:error_code]
|
11
|
-
|
11
|
+
error_code2 = response.to_hash[:place_bets_response][:result][:header][:error_code]
|
12
|
+
return error_code == 'OK' ? response.to_hash[:place_bets_response][:result][:bet_results][:place_bets_result] : "#{error_code} - #{error_code2}"
|
12
13
|
end
|
13
14
|
|
14
15
|
def cancel_bet(session_token, exchange_id, bet_id)
|
@@ -16,64 +17,79 @@ module Betfair
|
|
16
17
|
soap.body = { 'bf:request' => { :header => api_request_header(session_token), :bets => { 'CancelBets' => [ { :betId => bet_id } ] } } } # "CancelBets" has to be a string, not a symbol!
|
17
18
|
end
|
18
19
|
error_code = response.to_hash[:cancel_bets_response][:result][:error_code]
|
19
|
-
|
20
|
+
error_code2 = response.to_hash[:cancel_bets_response][:result][:header][:error_code]
|
21
|
+
return error_code == 'OK' ? response.to_hash[:cancel_bets_response][:result][:bet_results][:cancel_bets_result] : "#{error_code} - #{error_code2}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_market(session_token, exchange_id, market_id, locale = nil)
|
25
|
+
response = exchange(exchange_id).request :bf, :getMarket do
|
26
|
+
soap.body = { 'bf:request' => { :header => api_request_header(session_token), :marketId => market_id, :locale => locale } }
|
27
|
+
end
|
28
|
+
error_code = response.to_hash[:get_market_response][:result][:error_code]
|
29
|
+
error_code2 = response.to_hash[:get_market_response][:result][:header][:error_code]
|
30
|
+
return error_code == 'OK' ? response.to_hash[:get_market_response][:result][:market] : "#{error_code} - #{error_code2}"
|
20
31
|
end
|
21
|
-
|
22
|
-
def get_market(session_token, exchange_id, market_id, locale = nil)
|
23
|
-
response = exchange(exchange_id).request :bf, :getMarket do
|
24
|
-
soap.body = { 'bf:request' => { :header => api_request_header(session_token), :marketId => market_id, :locale => locale } }
|
25
|
-
end
|
26
|
-
error_code = response.to_hash[:get_market_response][:result][:error_code]
|
27
|
-
return error_code == 'OK' ? response.to_hash[:get_market_response][:result][:market] : error_code
|
28
|
-
end
|
29
|
-
|
32
|
+
|
30
33
|
def get_market_prices_compressed(session_token, exchange_id, market_id, currency_code = nil)
|
31
34
|
response = exchange(exchange_id).request :bf, :getMarketPricesCompressed do
|
32
|
-
|
35
|
+
soap.body = { 'bf:request' => { :header => api_request_header(session_token), :marketId => market_id, :currencyCode => currency_code } }
|
33
36
|
end
|
34
37
|
error_code = response.to_hash[:get_market_prices_compressed_response][:result][:error_code]
|
35
|
-
|
38
|
+
error_code2 = response.to_hash[:get_market_prices_compressed_response][:result][:header][:error_code]
|
39
|
+
return error_code == 'OK' ? response.to_hash[:get_market_prices_compressed_response][:result][:market_prices] : "#{error_code} - #{error_code2}"
|
36
40
|
end
|
37
41
|
|
38
42
|
def get_active_event_types(session_token, locale = nil)
|
39
43
|
response = @global_service.request :bf, :getActiveEventTypes do
|
40
44
|
soap.body = { 'bf:request' => { :header => api_request_header(session_token),
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
:locale => locale
|
46
|
+
}
|
47
|
+
}
|
44
48
|
end
|
45
|
-
error_code = response.to_hash[:get_active_event_types_response][:result][:error_code]
|
46
|
-
|
49
|
+
error_code = response.to_hash[:get_active_event_types_response][:result][:error_code]
|
50
|
+
error_code2 = response.to_hash[:get_active_event_types_response][:result][:header][:error_code]
|
51
|
+
return error_code == 'OK' ? response.to_hash[:get_active_event_types_response][:result][:event_type_items][:event_type] : "#{error_code} - #{error_code2}"
|
47
52
|
end
|
48
|
-
|
53
|
+
|
49
54
|
def get_all_markets(session_token, exchange_id, event_type_ids = nil, locale = nil, countries = nil, from_date = nil, to_date = nil)
|
50
55
|
response = exchange(exchange_id).request :bf, :getAllMarkets do
|
51
56
|
soap.body = { 'bf:request' => { :header => api_request_header(session_token),
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
:eventTypeIds => { 'int' => event_type_ids },
|
58
|
+
:locale => locale, :countries => { 'country' => countries },
|
59
|
+
:fromDate => from_date,
|
60
|
+
:toDate => to_date
|
61
|
+
}
|
62
|
+
}
|
58
63
|
end
|
59
|
-
error_code = response.to_hash[:get_all_markets_response][:result][:error_code]
|
60
|
-
|
64
|
+
error_code = response.to_hash[:get_all_markets_response][:result][:error_code]
|
65
|
+
error_code2 = response.to_hash[:get_all_markets_response][:result][:header][:error_code]
|
66
|
+
return error_code == 'OK' ? response.to_hash[:get_all_markets_response][:result][:market_data] : "#{error_code} - #{error_code2}"
|
61
67
|
end
|
62
|
-
|
68
|
+
|
69
|
+
|
70
|
+
def get_account_funds( session_token, exchange_id )
|
71
|
+
response = exchange( exchange_id ).request :bf, :getAccountFunds do
|
72
|
+
soap.body = { 'bf:request' => { :header => api_request_header(session_token) } }
|
73
|
+
end
|
74
|
+
error_code = response.to_hash[:get_account_funds_response][:result][:error_code]
|
75
|
+
error_code2 = response.to_hash[:get_account_funds_response][:result][:header][:error_code]
|
76
|
+
return error_code == 'OK' ? response.to_hash[:get_account_funds_response][:result] : "#{error_code} - #{error_code2}"
|
77
|
+
end
|
78
|
+
|
63
79
|
def login(username, password, product_id, vendor_software_id, location_id, ip_address)
|
64
80
|
response = @global_service.request :bf, :login do
|
65
81
|
soap.body = { 'bf:request' => { :username => username,
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
82
|
+
:password => password,
|
83
|
+
:productId => product_id,
|
84
|
+
:vendorSoftwareId => vendor_software_id,
|
85
|
+
:locationId => location_id,
|
86
|
+
:ipAddress => ip_address
|
87
|
+
}
|
88
|
+
}
|
73
89
|
end
|
74
90
|
session_token(response.to_hash[:login_response][:result][:header])
|
75
91
|
end
|
76
|
-
|
92
|
+
|
77
93
|
def exchange(exchange_id)
|
78
94
|
exchange_id == 2 ? @aus_service : @uk_service
|
79
95
|
end
|
@@ -81,208 +97,208 @@ module Betfair
|
|
81
97
|
def api_request_header(session_token)
|
82
98
|
{ :client_stamp => 0, :session_token => session_token }
|
83
99
|
end
|
84
|
-
|
100
|
+
|
85
101
|
def session_token(response_header)
|
86
102
|
response_header[:error_code] == 'OK' ? response_header[:session_token] : response_header[:error_code]
|
87
|
-
|
103
|
+
end
|
104
|
+
|
105
|
+
def initialize(proxy = nil, logging = nil)
|
88
106
|
|
89
|
-
def initialize(proxy = nil, logging = nil)
|
90
|
-
|
91
107
|
logging == true ? Savon.log = true : Savon.log = false
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
108
|
+
|
109
|
+
@global_service = Savon::Client.new do |wsdl, http|
|
110
|
+
wsdl.endpoint = 'https://api.betfair.com/global/v3/BFGlobalService'
|
111
|
+
wsdl.namespace = 'https://www.betfair.com/global/v3/BFGlobalService'
|
112
|
+
http.proxy = proxy if !proxy.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
@uk_service = Savon::Client.new do |wsdl, http|
|
116
|
+
wsdl.endpoint = 'https://api.betfair.com/exchange/v5/BFExchangeService'
|
101
117
|
wsdl.namespace = 'http://www.betfair.com/exchange/v3/BFExchangeService/UK'
|
102
118
|
http.proxy = proxy if !proxy.nil?
|
103
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
@aus_service = Savon::Client.new do |wsdl, http|
|
122
|
+
wsdl.endpoint = 'https://api-au.betfair.com/exchange/v5/BFExchangeService'
|
123
|
+
wsdl.namespace = 'http://www.betfair.com/exchange/v3/BFExchangeService/AUS'
|
124
|
+
http.proxy = proxy if !proxy.nil?
|
125
|
+
end
|
104
126
|
|
105
|
-
|
106
|
-
wsdl.endpoint = 'https://api-au.betfair.com/exchange/v5/BFExchangeService'
|
107
|
-
wsdl.namespace = 'http://www.betfair.com/exchange/v3/BFExchangeService/AUS'
|
108
|
-
http.proxy = proxy if !proxy.nil?
|
109
|
-
end
|
127
|
+
end
|
110
128
|
|
111
|
-
end
|
112
|
-
|
113
129
|
end
|
114
|
-
|
130
|
+
|
115
131
|
class Helpers
|
116
132
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
133
|
+
def all_markets(markets)
|
134
|
+
market_hash = {}
|
135
|
+
markets.gsub! '\:', "\0"
|
136
|
+
markets = markets.split ":"
|
137
|
+
markets.each do |piece|
|
138
|
+
piece.gsub! "\0", '\:'
|
139
|
+
market_hash[piece.split('~')[0].to_i] = { :market_id => piece.split('~')[0].to_i,
|
140
|
+
:market_name => piece.split('~')[1].to_s,
|
141
|
+
:market_type => piece.split('~')[2].to_s,
|
142
|
+
:market_status => piece.split('~')[3].to_s,
|
143
|
+
# bf returns in this case time in Epoch, but in milliseconds
|
144
|
+
:event_date => Time.at(piece.split('~')[4].to_i/1000),
|
145
|
+
:menu_path => piece.split('~')[5].to_s,
|
146
|
+
:event_hierarchy => piece.split('~')[6].to_s,
|
147
|
+
:bet_delay => piece.split('~')[7].to_s,
|
148
|
+
:exchange_id => piece.split('~')[8].to_i,
|
149
|
+
:iso3_country_code => piece.split('~')[9].to_s,
|
150
|
+
# bf returns in this case time in Epoch, but in milliseconds
|
151
|
+
:last_refresh => Time.at(piece.split('~')[10].to_i/1000),
|
152
|
+
:number_of_runners => piece.split('~')[11].to_i,
|
153
|
+
:number_of_winners => piece.split('~')[12].to_i,
|
154
|
+
:total_amount_matched => piece.split('~')[13].to_f,
|
155
|
+
:bsp_market => piece.split('~')[14] == 'Y' ? true : false,
|
156
|
+
:turning_in_play => piece.split('~')[15] == 'Y' ? true : false
|
157
|
+
}
|
158
|
+
end
|
159
|
+
return market_hash
|
160
|
+
end
|
161
|
+
|
162
|
+
def market_info(details)
|
163
|
+
{ :exchange_id => nil,
|
164
|
+
:market_type_id => nil,
|
165
|
+
:market_matched => nil,
|
166
|
+
:menu_path => details[:menu_path],
|
167
|
+
:market_id => details[:market_id],
|
168
|
+
:market_name => details[:name],
|
169
|
+
:market_type_name => details[:menu_path].to_s.split('\\')[1]
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def combine(market, prices)
|
174
|
+
market = details(market)
|
175
|
+
prices = prices(prices)
|
176
|
+
market[:runners].each do |runner|
|
177
|
+
runner.merge!( { :market_id => market[:market_id] } )
|
178
|
+
runner.merge!( { :market_type_id => market[:market_type_id] } )
|
179
|
+
runner.merge!(price_string(prices[runner[:runner_id]]))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def details(market)
|
184
|
+
runners = []
|
185
|
+
market[:runners][:runner].each { |runner| runners << { :runner_id => runner[:selection_id].to_i, :runner_name => runner[:name] } }
|
186
|
+
return { :market_id => market[:market_id].to_i, :market_type_id => market[:event_type_id].to_i, :runners => runners }
|
187
|
+
end
|
188
|
+
|
189
|
+
def prices(prices)
|
190
|
+
price_hash = {}
|
191
|
+
prices.gsub! '\:', "\0"
|
192
|
+
pieces = prices.split ":"
|
193
|
+
pieces.each do |piece|
|
194
|
+
piece.gsub! "\0", '\:'
|
195
|
+
price_hash[piece.split('~')[0].to_i] = piece
|
196
|
+
end
|
197
|
+
return price_hash
|
198
|
+
end
|
199
|
+
|
200
|
+
##
|
201
|
+
#
|
202
|
+
# Complete representation of market price data response,
|
203
|
+
# except "removed runners" which is returned as raw string.
|
204
|
+
#
|
205
|
+
##
|
206
|
+
def prices_complete(prices)
|
207
|
+
aux_hash = {}
|
208
|
+
price_hash = {}
|
209
|
+
|
210
|
+
prices.gsub! '\:', "\0"
|
211
|
+
pieces = prices.split ":"
|
212
|
+
|
213
|
+
# parsing first the auxiliary price info
|
214
|
+
aux = pieces.first
|
215
|
+
aux.gsub! "\0", '\:'
|
216
|
+
aux_hash = { :market_id => aux.split('~')[0].to_i,
|
217
|
+
:currency => aux.split('~')[1].to_s,
|
218
|
+
:market_status => aux.split('~')[2].to_s,
|
219
|
+
:in_play_delay => aux.split('~')[3].to_i,
|
220
|
+
:number_of_winners => aux.split('~')[4].to_i,
|
221
|
+
:market_information => aux.split('~')[5].to_s,
|
222
|
+
:discount_allowed => aux.split('~')[6] == 'true' ? true : false,
|
223
|
+
:market_base_rate => aux.split('~')[7].to_s,
|
224
|
+
:refresh_time_in_milliseconds => aux.split('~')[8].to_i,
|
225
|
+
:removed_runners => aux.split('~')[9].to_s,
|
226
|
+
:bsp_market => aux.split('~')[10] == 'Y' ? true : false
|
227
|
+
}
|
228
|
+
|
229
|
+
# now iterating over the prices excluding the first piece that we already parsed above
|
230
|
+
pieces[1..-1].each do |piece|
|
231
|
+
piece.gsub! "\0", '\:'
|
232
|
+
|
233
|
+
# using the selection_id as hash key
|
234
|
+
price_hash_key = piece.split('~')[0].to_i
|
235
|
+
|
236
|
+
price_hash[price_hash_key] = { :selection_id => piece.split('~')[0].to_i,
|
237
|
+
:order_index => piece.split('~')[1].to_i,
|
238
|
+
:total_amount_matched => piece.split('~')[2].to_f,
|
239
|
+
:last_price_matched => piece.split('~')[3].to_f,
|
240
|
+
:handicap => piece.split('~')[4].to_f,
|
241
|
+
:reduction_factor => piece.split('~')[5].to_f,
|
242
|
+
:vacant => piece.split('~')[6] == 'true' ? true : false,
|
243
|
+
:far_sp_price => piece.split('~')[7].to_f,
|
244
|
+
:near_sp_price => piece.split('~')[8].to_f,
|
245
|
+
:actual_sp_price => piece.split('~')[9].to_f
|
246
|
+
}
|
247
|
+
|
248
|
+
# merge lay and back prices into price_hash
|
249
|
+
price_hash[price_hash_key].merge!(price_string(piece, true))
|
250
|
+
end
|
251
|
+
|
252
|
+
price_hash.merge!(aux_hash)
|
253
|
+
|
254
|
+
return price_hash
|
255
|
+
end
|
256
|
+
|
257
|
+
def price_string(string, prices_only = false)
|
258
|
+
string_raw = string
|
259
|
+
string = string.split('|')
|
260
|
+
|
261
|
+
price = { :prices_string => nil, :runner_matched => 0, :last_back_price => 0, :wom => 0,
|
262
|
+
:b1 => 0, :b1_available => 0, :b2 => 0, :b2_available => 0, :b3 => 0, :b3_available => 0,
|
263
|
+
:l1 => 0, :l1_available => 0, :l2 => 0, :l2_available => 0, :l3 => 0, :l3_available => 0
|
264
|
+
}
|
265
|
+
|
266
|
+
if !string[0].nil? and !prices_only
|
267
|
+
str = string[0].split('~')
|
268
|
+
price[:prices_string] = string_raw
|
269
|
+
price[:runner_matched] = str[2].to_f
|
270
|
+
price[:last_back_price] = str[3].to_f
|
271
|
+
end
|
272
|
+
|
273
|
+
# Get the b prices (which are actually the l prices)
|
274
|
+
if !string[1].nil?
|
275
|
+
b = string[1].split('~')
|
276
|
+
price[:b1] = b[0].to_f if !b[0].nil?
|
277
|
+
price[:b1_available] = b[1].to_f if !b[1].nil?
|
278
|
+
price[:b2] = b[4].to_f if !b[5].nil?
|
279
|
+
price[:b2_available] = b[5].to_f if !b[6].nil?
|
280
|
+
price[:b3] = b[8].to_f if !b[8].nil?
|
281
|
+
price[:b3_available] = b[9].to_f if !b[9].nil?
|
282
|
+
combined_b = price[:b1_available] + price[:b2_available] + price[:b3_available]
|
283
|
+
end
|
284
|
+
|
285
|
+
# Get the l prices (which are actually the l prices)
|
286
|
+
if !string[2].nil?
|
287
|
+
l = string[2].split('~')
|
288
|
+
price[:l1] = l[0].to_f if !l[0].nil?
|
289
|
+
price[:l1_available] = l[1].to_f if !l[1].nil?
|
290
|
+
price[:l2] = l[4].to_f if !l[4].nil?
|
291
|
+
price[:l2_available] = l[5].to_f if !l[5].nil?
|
292
|
+
price[:l3] = l[8].to_f if !l[8].nil?
|
293
|
+
price[:l3_available] = l[9].to_f if !l[9].nil?
|
294
|
+
combined_l = price[:l1_available] + price[:l2_available] + price[:l3_available]
|
295
|
+
end
|
280
296
|
|
281
297
|
price[:wom] = combined_b / ( combined_b + combined_l ) unless combined_b.nil? or combined_l.nil?
|
282
|
-
|
283
|
-
|
284
|
-
|
298
|
+
|
299
|
+
return price
|
300
|
+
end
|
285
301
|
|
286
302
|
end
|
287
|
-
|
288
|
-
end
|
303
|
+
|
304
|
+
end
|
data/lib/betfair/version.rb
CHANGED
data/spec/betfair/api_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'tempfile'
|
1
2
|
require 'spec_helper'
|
2
3
|
|
4
|
+
|
3
5
|
module Betfair
|
4
6
|
|
5
7
|
describe "Helper methods for mashing the data from the API" do
|
@@ -88,11 +90,28 @@ module Betfair
|
|
88
90
|
it "should fail to cancel a bet on the exchange via the api" do
|
89
91
|
savon.expects(:cancel_bets).returns(:fail)
|
90
92
|
error_code = @bf.cancel_bet(@session_token, 3, 16939689578)
|
91
|
-
error_code.should eq('API_ERROR')
|
93
|
+
error_code.should eq('API_ERROR - NO_SESSION')
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
95
97
|
end
|
98
|
+
|
99
|
+
|
100
|
+
describe "Reading account details" do
|
101
|
+
before(:all) do
|
102
|
+
@bf = Betfair::API.new
|
103
|
+
@session_token = @bf.login('username', 'password', 82, 0, 0, nil)
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "reading wallet contents" do
|
107
|
+
it "reads the contents of the user's wallet" do
|
108
|
+
savon.expects( :get_account_funds ).returns( :success )
|
109
|
+
funds = @bf.get_account_funds( @session_token, 1 )
|
110
|
+
funds.should_not be_nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
96
115
|
|
97
116
|
describe "Basic read methods from the API" do
|
98
117
|
|
@@ -113,7 +132,7 @@ module Betfair
|
|
113
132
|
it "should return an error message given the exchange id and and array of market type ids and no session id" do
|
114
133
|
savon.expects(:get_all_markets).returns(:fail)
|
115
134
|
error_code = @bf.get_all_markets(@session_token, 1, [1,3], nil, nil, nil, nil)
|
116
|
-
error_code.should eq('API_ERROR')
|
135
|
+
error_code.should eq('API_ERROR - NO_SESSION')
|
117
136
|
end
|
118
137
|
end
|
119
138
|
|
@@ -129,7 +148,7 @@ module Betfair
|
|
129
148
|
it "should return an error message given the wrong exchange id or market id" do
|
130
149
|
savon.expects(:get_market).returns(:fail)
|
131
150
|
error_code = @bf.get_market(@session_token, 2, 10038633)
|
132
|
-
error_code.should eq('INVALID_MARKET')
|
151
|
+
error_code.should eq('INVALID_MARKET - OK')
|
133
152
|
end
|
134
153
|
end
|
135
154
|
|
@@ -145,7 +164,7 @@ module Betfair
|
|
145
164
|
it "should return an error message given the wrong exchange id or market id" do
|
146
165
|
savon.expects(:get_market_prices_compressed).returns(:fail)
|
147
166
|
error_code = @bf.get_market_prices_compressed(@session_token, 2, 10038633)
|
148
|
-
error_code.should eq('INVALID_MARKET')
|
167
|
+
error_code.should eq('INVALID_MARKET - OK')
|
149
168
|
end
|
150
169
|
end
|
151
170
|
|
@@ -204,11 +223,34 @@ module Betfair
|
|
204
223
|
savon.expects(:login).returns(:success)
|
205
224
|
proxy = nil
|
206
225
|
logging = true
|
207
|
-
|
208
|
-
|
226
|
+
output = capturing_stdout do
|
227
|
+
session_token = Betfair::API.new(proxy, logging).login('username', 'password', 82, 0, 0, nil).to_s
|
228
|
+
session_token.should be_an_instance_of(String)
|
229
|
+
end
|
230
|
+
output.should_not be_empty
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def capturing_stdout
|
236
|
+
result = nil
|
237
|
+
Tempfile.open("betfair_api") do |tempout|
|
238
|
+
replacement_stdout = File.open("/dev/stdout", "w")
|
239
|
+
$stdout.reopen( tempout )
|
240
|
+
|
241
|
+
begin
|
242
|
+
yield
|
243
|
+
ensure
|
244
|
+
$stdout.reopen( replacement_stdout )
|
245
|
+
end
|
246
|
+
|
247
|
+
tempout.close
|
248
|
+
result = File.read( tempout.path )
|
209
249
|
end
|
250
|
+
result
|
210
251
|
end
|
211
252
|
|
253
|
+
|
212
254
|
end
|
213
255
|
|
214
|
-
end
|
256
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
3
|
+
xmlns:n2="http://www.betfair.com/publicapi/types/exchange/v5/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
4
|
+
<soap:Body>
|
5
|
+
<n:getAccountFundsResponse xmlns:n="http://www.betfair.com/publicapi/v5/BFExchangeService/">
|
6
|
+
<n:Result xsi:type="n2:GetAccountFundsResp">
|
7
|
+
<header xsi:type="n2:APIResponseHeader">
|
8
|
+
<errorCode xsi:type="n2:APIErrorEnum">OK</errorCode>
|
9
|
+
<minorErrorCode xsi:nil="1"></minorErrorCode>
|
10
|
+
<sessionToken xsi:type="xsd:string">T0fSCYclMyf2iuL4FbGQyoINXuNeaxpKxtAq/7VnAjo=</sessionToken>
|
11
|
+
<timestamp xsi:type="xsd:dateTime">2012-02-26T21:35:14.957Z</timestamp>
|
12
|
+
</header>
|
13
|
+
<availBalance xsi:type="xsd:double">124.75</availBalance>
|
14
|
+
<balance xsi:type="xsd:double">124.75</balance>
|
15
|
+
<commissionRetain xsi:type="xsd:double">0.0</commissionRetain>
|
16
|
+
<creditLimit xsi:type="xsd:double">0.0</creditLimit>
|
17
|
+
<currentBetfairPoints xsi:type="xsd:int">2</currentBetfairPoints>
|
18
|
+
<expoLimit xsi:type="xsd:double">-5000.0</expoLimit>
|
19
|
+
<exposure xsi:type="xsd:double">0.0</exposure>
|
20
|
+
<holidaysAvailable xsi:type="xsd:int">4</holidaysAvailable>
|
21
|
+
<minorErrorCode xsi:nil="1"></minorErrorCode>
|
22
|
+
<nextDiscount xsi:type="xsd:double">0.0</nextDiscount>
|
23
|
+
<withdrawBalance xsi:type="xsd:double">124.75</withdrawBalance>
|
24
|
+
<errorCode xsi:type="n2:GetAccountFundsErrorEnum">OK</errorCode>
|
25
|
+
</n:Result>
|
26
|
+
</n:getAccountFundsResponse>
|
27
|
+
</soap:Body>
|
28
|
+
</soap:Envelope>
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: betfair
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-02-27 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: savon
|
16
|
-
requirement: &
|
16
|
+
requirement: &70291854542960 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70291854542960
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &70291854541880 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70291854541880
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70291854540880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70291854540880
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: savon_spec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70291854539840 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70291854539840
|
58
58
|
description: Gem for accessing the Betfair API.
|
59
59
|
email:
|
60
60
|
- lukeb@lukebyrne.com
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- spec/betfair/api_spec.rb
|
76
76
|
- spec/fixtures/cancel_bets/fail.xml
|
77
77
|
- spec/fixtures/cancel_bets/success.xml
|
78
|
+
- spec/fixtures/get_account_funds/success.xml
|
78
79
|
- spec/fixtures/get_active_event_types/success.xml
|
79
80
|
- spec/fixtures/get_all_markets/fail.xml
|
80
81
|
- spec/fixtures/get_all_markets/success.xml
|
@@ -101,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
102
|
version: '0'
|
102
103
|
segments:
|
103
104
|
- 0
|
104
|
-
hash: -
|
105
|
+
hash: -2983570207811971585
|
105
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
107
|
none: false
|
107
108
|
requirements:
|
@@ -110,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
111
|
version: '0'
|
111
112
|
segments:
|
112
113
|
- 0
|
113
|
-
hash: -
|
114
|
+
hash: -2983570207811971585
|
114
115
|
requirements: []
|
115
116
|
rubyforge_project: betfair
|
116
117
|
rubygems_version: 1.8.10
|
@@ -121,6 +122,7 @@ test_files:
|
|
121
122
|
- spec/betfair/api_spec.rb
|
122
123
|
- spec/fixtures/cancel_bets/fail.xml
|
123
124
|
- spec/fixtures/cancel_bets/success.xml
|
125
|
+
- spec/fixtures/get_account_funds/success.xml
|
124
126
|
- spec/fixtures/get_active_event_types/success.xml
|
125
127
|
- spec/fixtures/get_all_markets/fail.xml
|
126
128
|
- spec/fixtures/get_all_markets/success.xml
|