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 CHANGED
@@ -3,4 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  .rvmrc
6
- coverage/
6
+ examples/bar.rb
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 [Rubygems](http://rubygems.org/gems/betfair) and can be installed via:
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
- gem install betfair OR with bundler: gem 'betfair' and `bundle install`
13
+ OR with bundler: `gem 'betfair'` and `bundle install`
12
14
 
13
15
  ------------
14
16
  Introduction
15
17
  ------------
16
- irb
17
18
 
18
- require 'betfair'
19
+ irb
19
20
 
20
- or from a Gemfile
21
+ require 'betfair'
21
22
 
22
- gem 'betfair'
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
- If you want to use a proxy or turn on Savon's logging then just pass in like so:
25
+ gem 'betfair'
28
26
 
29
- proxy = ' http://localhost:8888' # This is a local squid proxy I tunnel to from my local machine to access the host server in UK for dev purposes.
30
- logging = true
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
- Proxy's can be useful if you want to host on a cloud service such as Heroku, as you will be denied access to the Betfair API from the USA.
30
+ If you want to use a proxy or turn on Savon's logging then just pass
31
+ in like so:
34
32
 
35
- Just proxy via a server from a country that Betfair allow's, such as the UK.
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
- At the heart of the Betfair API is the session_token.
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
- In order to get one of these simply call:
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 on the level of Betfair API access that you have.
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 Software Id, Location Id, Ip Address as required by the Betfair API.
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 = bf.get_all_markets(session_token, 1, [1,3], nil, nil, nil, nil)
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
- details = bf.get_market(session_token, 2, 100386338)
56
- prices = bf.get_market_prices_compressed(session_token, 2, 100386338)
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 string, this helper provides them all in handy hashes with market id as key.
64
- helpers.all_markets(markets)
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
- bf.place_bet(session_token, 1, 104184109, 58805, 'B', 10.0, 5.0)
70
- bf.cancel_bet(session_token, 1, 16939730542)
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
- savon
107
+
108
+ savon
76
109
 
77
110
  ------------------------
78
111
  Requirements for testing
79
112
  ------------------------
80
- savon_spec
81
- rspec
82
- rake
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 runner info
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 RSpec tests and send me a pull request.
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 a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
107
-
108
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
109
-
110
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
- return error_code == 'OK' ? response.to_hash[:place_bets_response][:result][:bet_results][:place_bets_result] : error_code
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
- return error_code == 'OK' ? response.to_hash[:cancel_bets_response][:result][:bet_results][:cancel_bets_result] : error_code
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
- soap.body = { 'bf:request' => { :header => api_request_header(session_token), :marketId => market_id, :currencyCode => currency_code } }
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
- return error_code == 'OK' ? response.to_hash[:get_market_prices_compressed_response][:result][:market_prices] : error_code
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
- :locale => locale
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
- return error_code == 'OK' ? response.to_hash[:get_active_event_types_response][:result][:event_type_items][:event_type] : error_code
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
- :eventTypeIds => { 'int' => event_type_ids },
53
- :locale => locale, :countries => { 'country' => countries },
54
- :fromDate => from_date,
55
- :toDate => to_date
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
- return error_code == 'OK' ? response.to_hash[:get_all_markets_response][:result][:market_data] : error_code
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
- :password => password,
67
- :productId => product_id,
68
- :vendorSoftwareId => vendor_software_id,
69
- :locationId => location_id,
70
- :ipAddress => ip_address
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
- end
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
- @global_service = Savon::Client.new do |wsdl, http|
94
- wsdl.endpoint = 'https://api.betfair.com/global/v3/BFGlobalService'
95
- wsdl.namespace = 'https://www.betfair.com/global/v3/BFGlobalService'
96
- http.proxy = proxy if !proxy.nil?
97
- end
98
-
99
- @uk_service = Savon::Client.new do |wsdl, http|
100
- wsdl.endpoint = 'https://api.betfair.com/exchange/v5/BFExchangeService'
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
- end
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
- @aus_service = Savon::Client.new do |wsdl, http|
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
- def all_markets(markets)
118
- market_hash = {}
119
- markets.gsub! '\:', "\0"
120
- markets = markets.split ":"
121
- markets.each do |piece|
122
- piece.gsub! "\0", '\:'
123
- market_hash[piece.split('~')[0].to_i] = { :market_id => piece.split('~')[0].to_i,
124
- :market_name => piece.split('~')[1].to_s,
125
- :market_type => piece.split('~')[2].to_s,
126
- :market_status => piece.split('~')[3].to_s,
127
- # bf returns in this case time in Epoch, but in milliseconds
128
- :event_date => Time.at(piece.split('~')[4].to_i/1000),
129
- :menu_path => piece.split('~')[5].to_s,
130
- :event_hierarchy => piece.split('~')[6].to_s,
131
- :bet_delay => piece.split('~')[7].to_s,
132
- :exchange_id => piece.split('~')[8].to_i,
133
- :iso3_country_code => piece.split('~')[9].to_s,
134
- # bf returns in this case time in Epoch, but in milliseconds
135
- :last_refresh => Time.at(piece.split('~')[10].to_i/1000),
136
- :number_of_runners => piece.split('~')[11].to_i,
137
- :number_of_winners => piece.split('~')[12].to_i,
138
- :total_amount_matched => piece.split('~')[13].to_f,
139
- :bsp_market => piece.split('~')[14] == 'Y' ? true : false,
140
- :turning_in_play => piece.split('~')[15] == 'Y' ? true : false
141
- }
142
- end
143
- return market_hash
144
- end
145
-
146
- def market_info(details)
147
- { :exchange_id => nil,
148
- :market_type_id => nil,
149
- :market_matched => nil,
150
- :menu_path => details[:menu_path],
151
- :market_id => details[:market_id],
152
- :market_name => details[:name],
153
- :market_type_name => details[:menu_path].to_s.split('\\')[1]
154
- }
155
- end
156
-
157
- def combine(market, prices)
158
- market = details(market)
159
- prices = prices(prices)
160
- market[:runners].each do |runner|
161
- runner.merge!( { :market_id => market[:market_id] } )
162
- runner.merge!( { :market_type_id => market[:market_type_id] } )
163
- runner.merge!(price_string(prices[runner[:runner_id]]))
164
- end
165
- end
166
-
167
- def details(market)
168
- runners = []
169
- market[:runners][:runner].each { |runner| runners << { :runner_id => runner[:selection_id].to_i, :runner_name => runner[:name] } }
170
- return { :market_id => market[:market_id].to_i, :market_type_id => market[:event_type_id].to_i, :runners => runners }
171
- end
172
-
173
- def prices(prices)
174
- price_hash = {}
175
- prices.gsub! '\:', "\0"
176
- pieces = prices.split ":"
177
- pieces.each do |piece|
178
- piece.gsub! "\0", '\:'
179
- price_hash[piece.split('~')[0].to_i] = piece
180
- end
181
- return price_hash
182
- end
183
-
184
- ##
185
- #
186
- # Complete representation of market price data response,
187
- # except "removed runners" which is returned as raw string.
188
- #
189
- ##
190
- def prices_complete(prices)
191
- aux_hash = {}
192
- price_hash = {}
193
-
194
- prices.gsub! '\:', "\0"
195
- pieces = prices.split ":"
196
-
197
- # parsing first the auxiliary price info
198
- aux = pieces.first
199
- aux.gsub! "\0", '\:'
200
- aux_hash = { :market_id => aux.split('~')[0].to_i,
201
- :currency => aux.split('~')[1].to_s,
202
- :market_status => aux.split('~')[2].to_s,
203
- :in_play_delay => aux.split('~')[3].to_i,
204
- :number_of_winners => aux.split('~')[4].to_i,
205
- :market_information => aux.split('~')[5].to_s,
206
- :discount_allowed => aux.split('~')[6] == 'true' ? true : false,
207
- :market_base_rate => aux.split('~')[7].to_s,
208
- :refresh_time_in_milliseconds => aux.split('~')[8].to_i,
209
- :removed_runners => aux.split('~')[9].to_s,
210
- :bsp_market => aux.split('~')[10] == 'Y' ? true : false
211
- }
212
-
213
- # now iterating over the prices excluding the first piece that we already parsed above
214
- pieces[1..-1].each do |piece|
215
- piece.gsub! "\0", '\:'
216
-
217
- # using the selection_id as hash key
218
- price_hash_key = piece.split('~')[0].to_i
219
-
220
- price_hash[price_hash_key] = { :selection_id => piece.split('~')[0].to_i,
221
- :order_index => piece.split('~')[1].to_i,
222
- :total_amount_matched => piece.split('~')[2].to_f,
223
- :last_price_matched => piece.split('~')[3].to_f,
224
- :handicap => piece.split('~')[4].to_f,
225
- :reduction_factor => piece.split('~')[5].to_f,
226
- :vacant => piece.split('~')[6] == 'true' ? true : false,
227
- :far_sp_price => piece.split('~')[7].to_f,
228
- :near_sp_price => piece.split('~')[8].to_f,
229
- :actual_sp_price => piece.split('~')[9].to_f
230
- }
231
-
232
- # merge lay and back prices into price_hash
233
- price_hash[price_hash_key].merge!(price_string(piece, true))
234
- end
235
-
236
- price_hash.merge!(aux_hash)
237
-
238
- return price_hash
239
- end
240
-
241
- def price_string(string, prices_only = false)
242
- string_raw = string
243
- string = string.split('|')
244
-
245
- price = { :prices_string => nil, :runner_matched => 0, :last_back_price => 0, :wom => 0,
246
- :b1 => 0, :b1_available => 0, :b2 => 0, :b2_available => 0, :b3 => 0, :b3_available => 0,
247
- :l1 => 0, :l1_available => 0, :l2 => 0, :l2_available => 0, :l3 => 0, :l3_available => 0
248
- }
249
-
250
- if !string[0].nil? and !prices_only
251
- str = string[0].split('~')
252
- price[:prices_string] = string_raw
253
- price[:runner_matched] = str[2].to_f
254
- price[:last_back_price] = str[3].to_f
255
- end
256
-
257
- # Get the b prices (which are actually the l prices)
258
- if !string[1].nil?
259
- b = string[1].split('~')
260
- price[:b1] = b[0].to_f if !b[0].nil?
261
- price[:b1_available] = b[1].to_f if !b[1].nil?
262
- price[:b2] = b[4].to_f if !b[5].nil?
263
- price[:b2_available] = b[5].to_f if !b[6].nil?
264
- price[:b3] = b[8].to_f if !b[8].nil?
265
- price[:b3_available] = b[9].to_f if !b[9].nil?
266
- combined_b = price[:b1_available] + price[:b2_available] + price[:b3_available]
267
- end
268
-
269
- # Get the l prices (which are actually the l prices)
270
- if !string[2].nil?
271
- l = string[2].split('~')
272
- price[:l1] = l[0].to_f if !l[0].nil?
273
- price[:l1_available] = l[1].to_f if !l[1].nil?
274
- price[:l2] = l[4].to_f if !l[4].nil?
275
- price[:l2_available] = l[5].to_f if !l[5].nil?
276
- price[:l3] = l[8].to_f if !l[8].nil?
277
- price[:l3_available] = l[9].to_f if !l[9].nil?
278
- combined_l = price[:l1_available] + price[:l2_available] + price[:l3_available]
279
- end
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
- return price
284
- end
298
+
299
+ return price
300
+ end
285
301
 
286
302
  end
287
-
288
- end
303
+
304
+ end
@@ -1,3 +1,3 @@
1
1
  module Betfair
2
- VERSION = "0.0.12"
2
+ VERSION = "0.0.13"
3
3
  end
@@ -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
- session_token = Betfair::API.new(proxy, logging).login('username', 'password', 82, 0, 0, nil).to_s
208
- session_token.should be_an_instance_of(String)
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
@@ -5,6 +5,8 @@ require 'betfair'
5
5
  require 'savon_spec'
6
6
 
7
7
  RSpec.configure do |config|
8
+ HTTPI.log = false
9
+ Savon.log = false
8
10
  config.include Savon::Spec::Macros
9
11
  Savon::Spec::Fixture.path = File.expand_path("../fixtures", __FILE__)
10
12
  end
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.12
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-01-18 00:00:00.000000000Z
12
+ date: 2012-02-27 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: savon
16
- requirement: &70257646476680 !ruby/object:Gem::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: *70257646476680
24
+ version_requirements: *70291854542960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70257646476140 !ruby/object:Gem::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: *70257646476140
35
+ version_requirements: *70291854541880
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70257646475580 !ruby/object:Gem::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: *70257646475580
46
+ version_requirements: *70291854540880
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: savon_spec
49
- requirement: &70257646474980 !ruby/object:Gem::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: *70257646474980
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: -3602817033406674363
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: -3602817033406674363
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