betfair 0.0.12 → 0.0.13
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/.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
|