oanda_api_v20 1.5.0 → 2.2.0
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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +5 -1
- data/CHANGELOG.md +27 -0
- data/README.md +55 -9
- data/lib/oanda_api_v20/accounts.rb +1 -1
- data/lib/oanda_api_v20/api.rb +90 -1
- data/lib/oanda_api_v20/client.rb +37 -97
- data/lib/oanda_api_v20/exceptions.rb +12 -1
- data/lib/oanda_api_v20/instruments.rb +1 -1
- data/lib/oanda_api_v20/pricing.rb +12 -0
- data/lib/oanda_api_v20/transactions.rb +12 -0
- data/lib/oanda_api_v20/version.rb +1 -1
- data/oanda_api_v20.gemspec +1 -1
- data/spec/oanda_api_v20/api_spec.rb +482 -251
- data/spec/oanda_api_v20/client_spec.rb +105 -114
- data/spec/oanda_api_v20/exceptions_spec.rb +24 -0
- data/spec/oanda_api_v20/oanda_api_v20_spec.rb +2 -2
- metadata +9 -8
@@ -3,62 +3,81 @@ require 'spec_helper'
|
|
3
3
|
describe OandaApiV20::Client do
|
4
4
|
describe '#initialize' do
|
5
5
|
it 'sets the access_token attribute when supplied' do
|
6
|
-
|
7
|
-
expect(
|
6
|
+
client = OandaApiV20::Client.new(access_token: 'my_access_token')
|
7
|
+
expect(client.access_token).to eq('my_access_token')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'sets the connection_pool_size attribute when supplied' do
|
11
|
+
client = OandaApiV20::Client.new(connection_pool_size: 5)
|
12
|
+
expect(client.connection_pool_size).to eq(5)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets the connection_pool_size attribute to the default value of 2 when not supplied' do
|
16
|
+
client = OandaApiV20::Client.new
|
17
|
+
expect(client.connection_pool_size).to eq(2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sets the max_requests_per_second attribute when supplied' do
|
21
|
+
client = OandaApiV20::Client.new(max_requests_per_second: 10)
|
22
|
+
expect(client.max_requests_per_second).to eq(10)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'sets the max_requests_per_second attribute to the default value of 100 when not supplied' do
|
26
|
+
client = OandaApiV20::Client.new
|
27
|
+
expect(client.max_requests_per_second).to eq(100)
|
8
28
|
end
|
9
29
|
|
10
30
|
it 'sets the proxy_url attribute when supplied' do
|
11
|
-
|
12
|
-
expect(
|
31
|
+
client = OandaApiV20::Client.new(proxy_url: 'https://user:pass@proxy.com:80')
|
32
|
+
expect(client.proxy_url).to eq('https://user:pass@proxy.com:80')
|
13
33
|
end
|
14
34
|
|
15
35
|
it 'sets the base_uri to practice when the practice option was supplied and set to true' do
|
16
|
-
|
17
|
-
expect(
|
36
|
+
client = OandaApiV20::Client.new(practice: true)
|
37
|
+
expect(client.base_uri).to eq('https://api-fxpractice.oanda.com/v3')
|
18
38
|
end
|
19
39
|
|
20
40
|
it 'sets the base_uri to live when the practice option was supplied and set to false' do
|
21
|
-
|
22
|
-
expect(
|
41
|
+
client = OandaApiV20::Client.new(practice: false)
|
42
|
+
expect(client.base_uri).to eq('https://api-fxtrade.oanda.com/v3')
|
23
43
|
end
|
24
44
|
|
25
45
|
it 'sets the base_uri to live when the practice option was not supplied' do
|
26
|
-
|
27
|
-
expect(
|
46
|
+
client = OandaApiV20::Client.new
|
47
|
+
expect(client.base_uri).to eq('https://api-fxtrade.oanda.com/v3')
|
28
48
|
end
|
29
49
|
|
30
50
|
it 'set the headers attribute to a hash' do
|
31
|
-
|
32
|
-
expect(
|
51
|
+
client = OandaApiV20::Client.new
|
52
|
+
expect(client.send(:headers)).to be_an_instance_of(Hash)
|
33
53
|
end
|
34
54
|
end
|
35
55
|
|
36
56
|
describe '#method_missing' do
|
37
|
-
let!(:
|
57
|
+
let!(:client) { OandaApiV20::Client.new(access_token: 'my_access_token') }
|
38
58
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
expect(c.send(:last_action)).to eq(:accounts)
|
43
|
-
end
|
59
|
+
it 'returns an OandaApiV20::Api instance when an API method has been called' do
|
60
|
+
expect(client.accounts).to be_an_instance_of(OandaApiV20::Api)
|
61
|
+
end
|
44
62
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
expect(c.send(:last_arguments)).to eq(['100-100-100'])
|
49
|
-
end
|
63
|
+
it 'sets the OandaApiV20::Api account_id attribute when method account was called' do
|
64
|
+
expect(client.account('100-100-100').account_id).to eq('100-100-100')
|
65
|
+
end
|
50
66
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
67
|
+
it 'sets the OandaApiV20::Api intrument variable when method instrument was called' do
|
68
|
+
instrument = 'EUR_USD'
|
69
|
+
expect(client.instrument(instrument).instance_variable_get(:@instrument)).to eq(instrument)
|
70
|
+
end
|
55
71
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
72
|
+
it 'raises a NoMethodError exception when a method other than an OandaApiV20::Api method has been called' do
|
73
|
+
expect{ client.this_method_definitely_does_not_exist }.to raise_error(NoMethodError)
|
59
74
|
end
|
60
75
|
|
61
|
-
|
76
|
+
it 'raises a NoMethodError exception when a method other than an OandaApiV20::Api action method has been called' do
|
77
|
+
expect{ client.accounts.show_me_the_money }.to raise_error(NoMethodError)
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'network' do
|
62
81
|
let(:response_account) { '{"account":{"id":"100-100-100","NAV":"100000.0000","balance":"100000.0000","lastTransactionID":"99","orders":[],"positions":[],"trades":[],"pendingOrderCount":0},"lastTransactionID":"99"}' }
|
63
82
|
let!(:request_account) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-100').to_return(status: 200, body: response_account, headers: {}) }
|
64
83
|
|
@@ -66,121 +85,93 @@ describe OandaApiV20::Client do
|
|
66
85
|
let!(:request_accounts) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').to_return(status: 200, body: response_accounts, headers: {}) }
|
67
86
|
|
68
87
|
before(:each) do
|
69
|
-
allow(
|
88
|
+
allow(client).to receive(:sleep)
|
70
89
|
end
|
71
90
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
expect(request_accounts).to have_been_requested
|
76
|
-
expect(request_accounts).to have_been_requested.at_most_once
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'returns the response from Oanda API' do
|
80
|
-
expect(c.accounts.show).to eq(JSON.parse(response_accounts))
|
81
|
-
expect(c.account('100-100-100').show).to eq(JSON.parse(response_account))
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'attributes' do
|
86
|
-
it 'sets the equivalent HTTP verb' do
|
87
|
-
c.accounts.show
|
88
|
-
expect(c.send(:http_verb)).to eq(:get)
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'sets the current account ID' do
|
92
|
-
c.account('100-100-100').show
|
93
|
-
expect(c.send(:account_id)).to eq('100-100-100')
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'sets the last transaction ID when returned' do
|
97
|
-
c.account('100-100-100').show
|
98
|
-
expect(c.send(:last_transaction_id)).to eq('99')
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'sets the last request made at time' do
|
102
|
-
expect(c.send(:last_api_request_at).last).to be_nil
|
103
|
-
c.accounts.show
|
104
|
-
expect(c.send(:last_api_request_at).last).to_not be_nil
|
105
|
-
expect(Time.parse(c.send(:last_api_request_at).last.to_s)).to be_an_instance_of(Time)
|
106
|
-
end
|
91
|
+
it 'raises an OandaApiV20::RequestError exception when receiving anything other than a 2xx response from Oanda API' do
|
92
|
+
stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-109').to_return(status: 401, body: '', headers: {})
|
93
|
+
expect{ client.account('100-100-109').show }.to raise_error(OandaApiV20::RequestError)
|
107
94
|
end
|
108
95
|
|
109
96
|
describe 'headers' do
|
110
97
|
it 'sets authentication header' do
|
111
|
-
|
98
|
+
client.accounts.show
|
112
99
|
expect(a_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').with(headers: { 'Authorization' => 'Bearer my_access_token' })).to have_been_made.once
|
113
100
|
end
|
114
101
|
|
115
102
|
it 'sets content type header to json' do
|
116
|
-
|
103
|
+
client.accounts.show
|
117
104
|
expect(a_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').with(headers: { 'Content-Type' => 'application/json' })).to have_been_made.once
|
118
105
|
end
|
119
106
|
|
120
107
|
it 'sets date time format header to RFC3339' do
|
121
|
-
|
108
|
+
client.accounts.show
|
122
109
|
expect(a_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').with(headers: { 'X-Accept-Datetime-Format' => 'RFC3339' })).to have_been_made.once
|
123
110
|
end
|
124
111
|
|
125
112
|
it 'sets persisten connection header' do
|
126
|
-
|
113
|
+
client.accounts.show
|
127
114
|
expect(a_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').with(headers: { 'Connection' => 'keep-alive' })).to have_been_made.once
|
128
115
|
end
|
129
116
|
end
|
117
|
+
end
|
118
|
+
end
|
130
119
|
|
131
|
-
|
132
|
-
|
133
|
-
stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-109').to_return(status: 401, body: '', headers: {})
|
134
|
-
expect{ c.account('100-100-109').show }.to raise_error(OandaApiV20::RequestError)
|
135
|
-
end
|
136
|
-
end
|
120
|
+
describe 'public methods' do
|
121
|
+
let!(:client) { OandaApiV20::Client.new(access_token: 'my_access_token') }
|
137
122
|
|
138
|
-
|
139
|
-
|
140
|
-
Timecop.freeze(Time.local('2016-08-01 06:00:00'))
|
141
|
-
end
|
123
|
+
let(:response_account) { '{"account":{"id":"100-100-100","NAV":"100000.0000","balance":"100000.0000","lastTransactionID":"99","orders":[],"positions":[],"trades":[],"pendingOrderCount":0},"lastTransactionID":"99"}' }
|
124
|
+
let!(:request_account) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-100').to_return(status: 200, body: response_account, headers: {}) }
|
142
125
|
|
143
|
-
|
144
|
-
|
145
|
-
end
|
126
|
+
let(:response_accounts) { '{"accounts":[{"id":"100-100-100","tags":[]}]}' }
|
127
|
+
let!(:request_accounts) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').to_return(status: 200, body: response_accounts, headers: {}) }
|
146
128
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
Timecop.freeze('2016-08-01 06:00:01')
|
151
|
-
30.times { c.accounts.show }
|
152
|
-
end
|
129
|
+
before(:each) do
|
130
|
+
allow(client).to receive(:sleep)
|
131
|
+
end
|
153
132
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
29.times { c.accounts.show }
|
159
|
-
Timecop.freeze('2016-08-01 06:00:02')
|
160
|
-
29.times { c.accounts.show }
|
161
|
-
end
|
133
|
+
describe '#govern_api_request_rate' do
|
134
|
+
before(:each) do
|
135
|
+
Timecop.freeze(Time.local('2016-08-01 06:00:00'))
|
136
|
+
end
|
162
137
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
138
|
+
after(:each) do
|
139
|
+
Timecop.return
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'is not allowed to make 100 requests or more per second' do
|
143
|
+
expect(client).to receive(:sleep).at_least(:twice)
|
144
|
+
100.times { client.accounts.show }
|
145
|
+
Timecop.freeze('2016-08-01 06:00:01')
|
146
|
+
100.times { client.accounts.show }
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'is allowed to make less than 100 requests per second' do
|
150
|
+
expect(client).to_not receive(:sleep)
|
151
|
+
99.times { client.accounts.show }
|
152
|
+
Timecop.freeze('2016-08-01 06:00:01')
|
153
|
+
99.times { client.accounts.show }
|
154
|
+
Timecop.freeze('2016-08-01 06:00:02')
|
155
|
+
99.times { client.accounts.show }
|
172
156
|
end
|
173
|
-
end
|
174
157
|
|
175
|
-
|
176
|
-
|
177
|
-
|
158
|
+
it 'halts all API requests for the remainder of the second when 100 requests have been made in one second' do
|
159
|
+
expect(client).to receive(:sleep).with(0.7)
|
160
|
+
Timecop.freeze('2016-08-01 06:00:00.0')
|
161
|
+
30.times { client.accounts.show }
|
162
|
+
Timecop.freeze('2016-08-01 06:00:00.1')
|
163
|
+
30.times { client.accounts.show }
|
164
|
+
Timecop.freeze('2016-08-01 06:00:00.3')
|
165
|
+
40.times { client.accounts.show }
|
178
166
|
end
|
179
167
|
end
|
180
168
|
|
181
|
-
|
182
|
-
it '
|
183
|
-
expect
|
169
|
+
describe '#update_last_api_request_at' do
|
170
|
+
it 'sets the time of the last request made' do
|
171
|
+
expect(client.send(:last_api_request_at).last).to be_nil
|
172
|
+
client.accounts.show
|
173
|
+
expect(client.send(:last_api_request_at).last).to_not be_nil
|
174
|
+
expect(Time.parse(client.send(:last_api_request_at).last.to_s)).to be_an_instance_of(Time)
|
184
175
|
end
|
185
176
|
end
|
186
177
|
end
|
@@ -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
|
@@ -3,8 +3,8 @@ require 'spec_helper'
|
|
3
3
|
describe OandaApiV20 do
|
4
4
|
describe '.new' do
|
5
5
|
it 'instantiates a new OandaApiV20::Client' do
|
6
|
-
|
7
|
-
expect(
|
6
|
+
client = OandaApiV20.new
|
7
|
+
expect(client).to be_an_instance_of(OandaApiV20::Client)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
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:
|
4
|
+
version: 2.2.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: 2021-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -98,16 +98,16 @@ dependencies:
|
|
98
98
|
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
103
|
+
version: '13.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
version: '13.0'
|
111
111
|
description: Ruby client that supports the Oanda REST API V20 methods.
|
112
112
|
email: kobus@translate3d.com
|
113
113
|
executables: []
|
@@ -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
|
@@ -160,13 +161,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
161
|
- !ruby/object:Gem::Version
|
161
162
|
version: '0'
|
162
163
|
requirements: []
|
163
|
-
|
164
|
-
rubygems_version: 2.5.1
|
164
|
+
rubygems_version: 3.1.4
|
165
165
|
signing_key:
|
166
166
|
specification_version: 4
|
167
167
|
summary: Ruby Oanda REST API V20
|
168
168
|
test_files:
|
169
169
|
- spec/oanda_api_v20/api_spec.rb
|
170
170
|
- spec/oanda_api_v20/client_spec.rb
|
171
|
+
- spec/oanda_api_v20/exceptions_spec.rb
|
171
172
|
- spec/oanda_api_v20/oanda_api_v20_spec.rb
|
172
173
|
- spec/spec_helper.rb
|