oanda_api_v20 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +28 -0
- data/lib/oanda_api_v20/api.rb +2 -2
- data/lib/oanda_api_v20/client.rb +12 -5
- data/lib/oanda_api_v20/exceptions.rb +11 -1
- data/lib/oanda_api_v20/pricing.rb +24 -0
- data/lib/oanda_api_v20/version.rb +1 -1
- data/spec/oanda_api_v20/api_spec.rb +31 -2
- data/spec/oanda_api_v20/exceptions_spec.rb +24 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c705d6ed7e93994a827ab0f090c3ffb210af8abf
|
4
|
+
data.tar.gz: 9beca7942edcad3e584e29c2607ace094b38c3dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3206d93bf544c75b273a3685acb11a5485877e99127925c51df9173f50fd968ae7b335f7b40f7e67493fdc61e55ac977cef11a2d900eb6b737ce68119db203f
|
7
|
+
data.tar.gz: da31de41ee3bfd5cb0a245ce1e185e0e4ba1773d0e192efade7cd9536ffe9ce96ca8c2f5e1d507abf89cc629e62ff356d9625626b41d1490174e101146fbbc29
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 2.1.0
|
4
|
+
#### 2019-02-01
|
5
|
+
* New pricing_stream endpoint. Big thanks to @joseluis-fw.
|
6
|
+
* Raises an OandaApiV20::ParseError exception when a malformed response returned from Oanda API.
|
7
|
+
* The OandaApiV20::RequestError exception object now has an original_exception attribute which contains the original exception object raised.
|
8
|
+
|
3
9
|
## 2.0.0
|
4
10
|
#### 2018-04-24
|
5
11
|
* Fixed issues when trying to reuse a Client object to make HTTP requests to Oanda.
|
data/README.md
CHANGED
@@ -33,6 +33,10 @@ If you would like to trade with your test account:
|
|
33
33
|
|
34
34
|
client = OandaApiV20.new(access_token: 'my_access_token', practice: true)
|
35
35
|
|
36
|
+
If you would like to use the streaming endpoints:
|
37
|
+
|
38
|
+
client = OandaApiV20.new(access_token: 'my_access_token', stream: true)
|
39
|
+
|
36
40
|
If you need your requests to go through a proxy:
|
37
41
|
|
38
42
|
client = OandaApiV20.new(access_token: 'my_access_token', proxy_url: 'https://user:pass@proxy.com:80')
|
@@ -305,10 +309,34 @@ options = {
|
|
305
309
|
client.account('account_id').pricing(options).show
|
306
310
|
```
|
307
311
|
|
312
|
+
```ruby
|
313
|
+
client = OandaApiV20.new(access_token: 'my_access_token', stream: true)
|
314
|
+
|
315
|
+
options = {
|
316
|
+
'instruments' => 'EUR_USD,USD_CAD'
|
317
|
+
}
|
318
|
+
|
319
|
+
client.account('account_id').pricing_stream(options).show do |json|
|
320
|
+
puts json if json['type'] == 'PRICE'
|
321
|
+
end
|
322
|
+
```
|
323
|
+
|
308
324
|
## Exceptions
|
309
325
|
|
326
|
+
A `OandaApiV20::ParseError` will be raised when a response from the Oanda API is malformed.
|
327
|
+
|
310
328
|
A `OandaApiV20::RequestError` will be raised when a request to the Oanda API failed for any reason.
|
311
329
|
|
330
|
+
You can access the original exception in a `OandaApiV20::RequestError`:
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
begin
|
334
|
+
do_something
|
335
|
+
rescue OandaApiV20::RequestError => e
|
336
|
+
e.original_exception
|
337
|
+
end
|
338
|
+
```
|
339
|
+
|
312
340
|
## Contributing
|
313
341
|
|
314
342
|
1. Fork it
|
data/lib/oanda_api_v20/api.rb
CHANGED
@@ -33,7 +33,7 @@ module OandaApiV20
|
|
33
33
|
define_method(method_name) do |*args, &block|
|
34
34
|
# Add the block below before each of the api_methods to set the last_action and last_arguments.
|
35
35
|
# Return the OandaApiV20::Api object to allow for method chaining when any of the api_methods have been called.
|
36
|
-
# Only make an HTTP request to Oanda API
|
36
|
+
# Only make an HTTP request to Oanda API when an action method like show, update, cancel, close or create was called.
|
37
37
|
set_last_action_and_arguments(method_name, *args)
|
38
38
|
return self unless http_verb
|
39
39
|
|
@@ -56,7 +56,7 @@ module OandaApiV20
|
|
56
56
|
last_arguments.nil? || last_arguments.empty? ? send(last_action, &block) : send(last_action, *last_arguments, &block)
|
57
57
|
end
|
58
58
|
rescue Http::Exceptions::HttpException => e
|
59
|
-
raise OandaApiV20::RequestError, e.
|
59
|
+
raise OandaApiV20::RequestError.new(e.message, response: e.response, original_exception: e.original_exception)
|
60
60
|
end
|
61
61
|
|
62
62
|
if response.body && !response.body.empty?
|
data/lib/oanda_api_v20/client.rb
CHANGED
@@ -3,9 +3,15 @@ module OandaApiV20
|
|
3
3
|
include HTTParty
|
4
4
|
|
5
5
|
BASE_URI = {
|
6
|
-
live:
|
7
|
-
|
8
|
-
|
6
|
+
live: {
|
7
|
+
api: 'https://api-fxtrade.oanda.com/v3',
|
8
|
+
stream: 'https://stream-fxtrade.oanda.com/v3'
|
9
|
+
},
|
10
|
+
practice: {
|
11
|
+
api: 'https://api-fxpractice.oanda.com/v3',
|
12
|
+
stream: 'https://stream-fxpractice.oanda.com/v3'
|
13
|
+
}
|
14
|
+
}.freeze
|
9
15
|
|
10
16
|
attr_accessor :access_token, :proxy_url, :max_requests_per_second, :connection_pool_size, :debug
|
11
17
|
attr_reader :base_uri, :headers
|
@@ -20,7 +26,8 @@ module OandaApiV20
|
|
20
26
|
@connection_pool_size ||= 2
|
21
27
|
@max_requests_per_second ||= 100
|
22
28
|
@last_api_request_at = Array.new(max_requests_per_second)
|
23
|
-
|
29
|
+
uris = options[:practice] == true ? BASE_URI[:practice] : BASE_URI[:live]
|
30
|
+
@base_uri = options[:stream] == true ? uris[:stream] : uris[:api]
|
24
31
|
|
25
32
|
@headers = {}
|
26
33
|
@headers['Authorization'] = "Bearer #{access_token}"
|
@@ -39,7 +46,7 @@ module OandaApiV20
|
|
39
46
|
pool_size: connection_pool_size
|
40
47
|
}
|
41
48
|
|
42
|
-
persistent_connection_adapter_options.merge!(logger: ::Logger.new(STDOUT)) if debug
|
49
|
+
persistent_connection_adapter_options.merge!(logger: ::Logger.new(STDOUT), debug_output: ::Logger.new(STDOUT)) if debug
|
43
50
|
Client.persistent_connection_adapter(persistent_connection_adapter_options)
|
44
51
|
end
|
45
52
|
|
@@ -1,4 +1,14 @@
|
|
1
1
|
module OandaApiV20
|
2
2
|
class ApiError < RuntimeError; end
|
3
|
-
class
|
3
|
+
class ParseError < RuntimeError; end
|
4
|
+
|
5
|
+
class RequestError < RuntimeError
|
6
|
+
attr_reader :response, :original_exception
|
7
|
+
|
8
|
+
def initialize(message = nil, options = {})
|
9
|
+
@original_exception = options[:original_exception]
|
10
|
+
@response = options[:response]
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
4
14
|
end
|
@@ -5,5 +5,29 @@ module OandaApiV20
|
|
5
5
|
def pricing(options)
|
6
6
|
Client.send(http_verb, "#{base_uri}/accounts/#{account_id}/pricing", headers: headers, query: options)
|
7
7
|
end
|
8
|
+
|
9
|
+
# GET /v3/accounts/:account_id/pricing/stream
|
10
|
+
def pricing_stream(options, &block)
|
11
|
+
buffer = StringIO.new
|
12
|
+
|
13
|
+
Client.send(http_verb, "#{base_uri}/accounts/#{account_id}/pricing/stream", headers: headers, query: options, stream_body: true) do |fragment|
|
14
|
+
begin
|
15
|
+
next if fragment.empty?
|
16
|
+
|
17
|
+
buffer << fragment
|
18
|
+
next unless fragment.match(/\n\Z/)
|
19
|
+
|
20
|
+
buffer.string.split("\n").each do |message|
|
21
|
+
cleaned_message = message.strip
|
22
|
+
next if cleaned_message.empty?
|
23
|
+
yield JSON.parse(cleaned_message)
|
24
|
+
end
|
25
|
+
rescue JSON::ParseError => e
|
26
|
+
raise OandaApiV20::ParseError, "#{e.message} in '#{fragment}'"
|
27
|
+
ensure
|
28
|
+
buffer.flush
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
8
32
|
end
|
9
33
|
end
|
@@ -61,8 +61,10 @@ describe OandaApiV20::Api do
|
|
61
61
|
|
62
62
|
describe '#method_missing' do
|
63
63
|
describe 'constructs the correct API URL under' do
|
64
|
-
let!(:client)
|
65
|
-
let!(:
|
64
|
+
let!(:client) { OandaApiV20::Client.new(access_token: 'my_access_token', base_uri: 'https://api-fxtrade.oanda.com/v3', headers: {}) }
|
65
|
+
let!(:stream_client) { OandaApiV20::Client.new(access_token: 'my_access_token', base_uri: 'https://api-fxtrade.oanda.com/v3', headers: {}, stream: true) }
|
66
|
+
let!(:api) { OandaApiV20::Api.new(client: client, account_id: '100-100-100') }
|
67
|
+
let!(:stream_api) { OandaApiV20::Api.new(client: stream_client, account_id: '100-100-100') }
|
66
68
|
|
67
69
|
before(:each) do
|
68
70
|
stub_request(:get, /https:\/\/api-fxtrade\.oanda\.com\/v3.*/)
|
@@ -331,6 +333,32 @@ describe OandaApiV20::Api do
|
|
331
333
|
api.pricing(options).show
|
332
334
|
expect(a_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-100/pricing').with(query: options)).to have_been_made.once
|
333
335
|
end
|
336
|
+
|
337
|
+
it 'retrieving all pricing stream' do
|
338
|
+
options = {
|
339
|
+
'instruments' => 'EUR_USD,USD_CAD'
|
340
|
+
}
|
341
|
+
|
342
|
+
body = <<~EOF
|
343
|
+
{"type":"PRICE","time":"2019-01-31T18:16:38.818627106Z","bids":[{"price":"0.72711","liquidity":10000000}],"asks":[{"price":"0.72725","liquidity":10000000}],"closeoutBid":"0.72696","closeoutAsk":"0.72740","status":"tradeable","tradeable":true,"instrument":"USD_CAD"}\n{"type":"PRICE","time":"2019-01-31T18:16:48.270050596Z","bids":[{"price":"0.95533","liquidity":10000000}],"asks":[{"price":"0.95554","liquidity":10000000}],"closeoutBid":"0.95533","closeoutAsk":"0.95554","status":"tradeable","tradeable":true,"instrument":"EUR_USD"}\n\r\n
|
344
|
+
EOF
|
345
|
+
|
346
|
+
headers = {
|
347
|
+
'Transfer-Encoding' => 'chunked',
|
348
|
+
'Content-Type' => 'application/octet-stream'
|
349
|
+
}
|
350
|
+
|
351
|
+
stub_request(:get, 'https://stream-fxtrade.oanda.com/v3/accounts/100-100-100/pricing/stream?instruments=EUR_USD,USD_CAD').to_return(status: 200, body: body, headers: headers)
|
352
|
+
|
353
|
+
messages = []
|
354
|
+
|
355
|
+
stream_api.pricing_stream(options).show do |message|
|
356
|
+
messages << message
|
357
|
+
end
|
358
|
+
|
359
|
+
expect(a_request(:get, 'https://stream-fxtrade.oanda.com/v3/accounts/100-100-100/pricing/stream').with(query: options)).to have_been_made.once
|
360
|
+
expect(messages.count).to eq(2)
|
361
|
+
end
|
334
362
|
end
|
335
363
|
end
|
336
364
|
|
@@ -426,6 +454,7 @@ describe OandaApiV20::Api do
|
|
426
454
|
:position, :positions, :open_positions,
|
427
455
|
:transaction, :transactions, :transactions_id_range, :transactions_since_id,
|
428
456
|
:pricing,
|
457
|
+
:pricing_stream,
|
429
458
|
:candles
|
430
459
|
] }
|
431
460
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OandaApiV20::RequestError do
|
4
|
+
describe '#initialize' do
|
5
|
+
let(:response) { { code: 200, body: '' } }
|
6
|
+
|
7
|
+
it 'sets the message attribute' do
|
8
|
+
exception = OandaApiV20::RequestError.new('An error as occured while processing response.')
|
9
|
+
expect(exception.message).to eq('An error as occured while processing response.')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sets the response attribute when supplied' do
|
13
|
+
exception = OandaApiV20::RequestError.new('An error as occured while processing response.', response: response)
|
14
|
+
expect(exception.response).to eq(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets the original_exception attribue when supplied' do
|
18
|
+
original_exception = OpenSSL::SSL::SSLError.new('SSL_read: sslv3 alert handshake failure')
|
19
|
+
exception = OandaApiV20::RequestError.new('An error as occured while processing response.', original_exception: original_exception)
|
20
|
+
expect(exception.original_exception).to be_an_instance_of(OpenSSL::SSL::SSLError)
|
21
|
+
expect(exception.original_exception.message).to eq('SSL_read: sslv3 alert handshake failure')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oanda_api_v20
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kobus Joubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -139,6 +139,7 @@ files:
|
|
139
139
|
- oanda_api_v20.gemspec
|
140
140
|
- spec/oanda_api_v20/api_spec.rb
|
141
141
|
- spec/oanda_api_v20/client_spec.rb
|
142
|
+
- spec/oanda_api_v20/exceptions_spec.rb
|
142
143
|
- spec/oanda_api_v20/oanda_api_v20_spec.rb
|
143
144
|
- spec/spec_helper.rb
|
144
145
|
homepage: http://rubygems.org/gems/oanda_api_v20
|
@@ -168,5 +169,6 @@ summary: Ruby Oanda REST API V20
|
|
168
169
|
test_files:
|
169
170
|
- spec/oanda_api_v20/api_spec.rb
|
170
171
|
- spec/oanda_api_v20/client_spec.rb
|
172
|
+
- spec/oanda_api_v20/exceptions_spec.rb
|
171
173
|
- spec/oanda_api_v20/oanda_api_v20_spec.rb
|
172
174
|
- spec/spec_helper.rb
|