oanda_api 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2c7acc3d30cf5e19c55528d74251a43ad5e6387c
4
+ data.tar.gz: 6bb298acd99fb97dd08d82d86455e910694e56cc
5
+ SHA512:
6
+ metadata.gz: f44a232f8c05ee18ec10b8a0e98cba28d865e8533ba2b0efdf7eca8e87936a5e9fadbc2ee49c56a5916f81b7d7063d8dbf9b9371c87d920a7d7f36992bac8f8b
7
+ data.tar.gz: 63c786402973185453b85fee525936234ac690a49b5119922dfbebb6fc4613512f36cdbe5cfc0bc5d883e2fafec20cf29bd8ea3f19d20fffe6b11e0305d25b7b
File without changes
@@ -0,0 +1,11 @@
1
+ # Change Log
2
+
3
+ ## 0.90
4
+
5
+ * Add support for live [Streaming](http://developer.oanda.com/rest-live/streaming/).
6
+ * Add #to_json serialization to resource classes.
7
+ * fixed Yardoc formatting.
8
+
9
+ ###What's New?
10
+ OandaAPI now supports Oanda's streaming API for consuming realtime ticks and account transactions. See the example in the README and have a look at the specs for `OandaAPI::Streaming::Client`.
11
+
data/Gemfile CHANGED
@@ -4,6 +4,9 @@ gemspec
4
4
 
5
5
  group :development do
6
6
  gem "pry"
7
+ gem 'pry-stack_explorer'
8
+ gem 'pry-byebug'
9
+
7
10
  gem "guard"
8
11
  gem "guard-rspec"
9
12
  gem "fuubar"
@@ -11,3 +14,5 @@ group :development do
11
14
  gem "github-markup"
12
15
  gem "rubocop", require: false
13
16
  end
17
+
18
+ gem 'simplecov', :require => false, :group => :test
data/README.md CHANGED
@@ -12,7 +12,7 @@ client = OandaAPI::Client::TokenClient.new(:practice, "practice_account_token")
12
12
  account = client.account(12345).get
13
13
  ```
14
14
 
15
- returns an OandaAPI::Resource::Account, with method accessors for all of the [Account](http://developer.oanda.com/rest-live/accounts/) attributes defined by the Oanda API.
15
+ returns an `OandaAPI::Resource::Account`, with method accessors for all of the [Account](http://developer.oanda.com/rest-live/accounts/) attributes defined by the Oanda API.
16
16
 
17
17
 
18
18
  ## Features
@@ -34,7 +34,7 @@ Some Examples
34
34
  ```ruby
35
35
  require 'oanda_api'
36
36
 
37
- client = OandaAPI::Client::TokenClient.new(:practice, ENV["practice_account_token"])
37
+ client = OandaAPI::Client::TokenClient.new(:practice, ENV.fetch("OANDA_PRACTICE_TOKEN"))
38
38
 
39
39
  prices = client.prices(instruments: %w(EUR_USD USD_JPY)).get
40
40
 
@@ -50,7 +50,7 @@ end
50
50
  ```ruby
51
51
  require 'oanda_api'
52
52
 
53
- client = OandaAPI::Client::TokenClient.new(:practice, ENV["practice_account_token"])
53
+ client = OandaAPI::Client::TokenClient.new(:practice, ENV.fetch("OANDA_PRACTICE_TOKEN"))
54
54
 
55
55
  candles = client.candles( instrument: "EUR_USD",
56
56
  granularity: "M1",
@@ -78,7 +78,7 @@ end
78
78
  ```ruby
79
79
  require 'oanda_api'
80
80
 
81
- client = OandaAPI::Client::TokenClient.new(:practice, ENV["practice_account_token"])
81
+ client = OandaAPI::Client::TokenClient.new(:practice, ENV.fetch("OANDA_PRACTICE_TOKEN"))
82
82
 
83
83
  order = client.account(12345)
84
84
  .order(instrument: "USD_JPY",
@@ -96,7 +96,7 @@ order.trade_opened.id # => 175491416
96
96
  ```ruby
97
97
  require 'oanda_api'
98
98
 
99
- client = OandaAPI::Client::TokenClient.new(:practice, ENV["practice_account_token"])
99
+ client = OandaAPI::Client::TokenClient.new(:practice, ENV.fetch("OANDA_PRACTICE_TOKEN"))
100
100
 
101
101
  account = client.account(12345) # => OandaAPI::NamespaceProxy
102
102
  position = account.position("USD_JPY").get # => OandaAPI::Resource::Position
@@ -118,6 +118,24 @@ transaction.time # => 2014-12-19 03:29:48 UTC
118
118
  transaction.type # => "MARKET_ORDER_CREATE"
119
119
  ```
120
120
 
121
+ ##Streaming
122
+ OandaAPI also supports the [Oanda realtime streaming API](http://developer.oanda.com/rest-live/streaming/).
123
+
124
+ For example to stream live prices,
125
+
126
+ ```ruby
127
+ client = OandaAPI::Streaming::Client.new(:practice, ENV.fetch("OANDA_PRACTICE_TOKEN"))
128
+ prices = client.prices(account_id: 1234, instruments: %w[AUD_CAD AUD_CHF])
129
+ prices.stream do |price|
130
+ # Note: The code in this block should handle the price
131
+ # as efficently as possible, otherwise the connection could timeout.
132
+ # For example, you could publish the tick on a queue to be handled
133
+ # by some other thread or process.
134
+ price # => OandaAPI::Resource::Price
135
+ end
136
+ ```
137
+
138
+
121
139
  Documentation
122
140
  -------------
123
141
 
@@ -125,15 +143,15 @@ Please see the [Oanda Developer Wiki](http://developer.oanda.com/rest-live/intro
125
143
  for detailed documentation and API usage notes.
126
144
 
127
145
 
128
- | Ruby | Oanda REST API |
146
+ | Ruby | Oanda REST API |
129
147
  |:---------------------------|:---------------------|
130
- | client.accounts.get | GET /v1/accounts |
148
+ | client.accounts.get | GET /v1/accounts |
131
149
  | client.account(123).get | GET /v1/accounts/123 |
132
150
  | client.account.create | POST /v1/accounts |
133
151
  | client.instruments.get | GET /v1/instruments |
134
152
  | client.prices(instruments: ["EUR_USD","USD_JPY"]).get | GET /v1/prices/?instruments=EUR_USD%2CUSD_JPY |
135
- | client.account(123).orders.get | GET /v1/accounts/123/orders
136
- | client.account(123).order(123).get | GET /v1/accounts/123/orders/123
153
+ | client.account(123).orders.get | GET /v1/accounts/123/orders |
154
+ | client.account(123).order(123).get | GET /v1/accounts/123/orders/123 |
137
155
  | client.account(123).order( *options* ).create | POST /v1/accounts/123/orders |
138
156
  | client.account(123).order(id:123, *options* ).update | PATCH /v1/accounts/123/orders/123 |
139
157
  | client.account(123).order(123).close | DELETE /v1/accounts/123/orders/123 |
@@ -216,3 +234,5 @@ License
216
234
 
217
235
  Copyright (c) 2014 Dean Missikowski. Distributed under the MIT License. See
218
236
  [LICENSE](LICENSE) for further details.
237
+
238
+ 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.
@@ -10,12 +10,15 @@ require_relative 'oanda_api/client/namespace_proxy'
10
10
  require_relative 'oanda_api/client/resource_descriptor'
11
11
  require_relative 'oanda_api/client/token_client'
12
12
  require_relative 'oanda_api/client/username_client'
13
+ require_relative 'oanda_api/streaming/client'
14
+ require_relative 'oanda_api/streaming/request'
13
15
  require_relative 'oanda_api/errors'
14
16
  require_relative 'oanda_api/resource_base'
15
17
  require_relative 'oanda_api/resource_collection'
16
18
  require_relative 'oanda_api/resource/account'
17
19
  require_relative 'oanda_api/resource/instrument'
18
20
  require_relative 'oanda_api/resource/candle'
21
+ require_relative 'oanda_api/resource/heartbeat'
19
22
  require_relative 'oanda_api/resource/order'
20
23
  require_relative 'oanda_api/resource/position'
21
24
  require_relative 'oanda_api/resource/price'
@@ -8,7 +8,7 @@ module OandaAPI
8
8
  # Provides everything needed for accessing the API.
9
9
  #
10
10
  # - Uses persistant http connections.
11
- # - Uses +OpenSSL::SSL::VERIFY_PEER+ to always validate SSL certificates.
11
+ # - Uses `OpenSSL::SSL::VERIFY_PEER` to always validate SSL certificates.
12
12
  # - Uses compression if enabled (see {Configuration#use_compression}).
13
13
  # - Uses request rate limiting if enabled (see {Configuration#use_request_throttling}).
14
14
  module Client
@@ -92,7 +92,7 @@ module OandaAPI
92
92
  # Maps An API _action_ to a corresponding http verb.
93
93
  #
94
94
  # @param [Symbol] method an API action. Supported actions are:
95
- # +:create+, +:close+, +:delete+, +:get+, +:update+.
95
+ # `:create`, `:close`, `:delete`, `:get`, `:update`.
96
96
  #
97
97
  # @return [Symbol] an http verb.
98
98
  def self.map_method_to_http_verb(method)
@@ -167,7 +167,7 @@ module OandaAPI
167
167
 
168
168
  # @private
169
169
  # Enables method-chaining.
170
- # @return [Namespace]
170
+ # @return [NamespaceProxy]
171
171
  def method_missing(sym, *args)
172
172
  NamespaceProxy.new self, sym, args.first
173
173
  end
@@ -24,12 +24,13 @@ module OandaAPI
24
24
  # ordered list of segments, joined, creates a path to a resource URI.
25
25
  #
26
26
  # @param [Hash] conditions an optional list of parameters that typically
27
- # specifies conditions and filters for a resource request. A a _"key"_
27
+ # specifies conditions and filters for a resource request. A _"key"_
28
28
  # or _"id"_ is a condition that identifies a particular resource. If a
29
29
  # key condition is included, it is extracted and added as a namespace
30
30
  # segment. See {#extract_key_and_conditions}.
31
31
  def initialize(client, namespace_segment, conditions)
32
- fail ArgumentError, "expecting an OandaAPI::Client instance" unless client && client.is_a?(OandaAPI::Client)
32
+ fail ArgumentError, "expecting an OandaAPI::Client instance" unless
33
+ client && client.is_a?(OandaAPI::Client) || client.is_a?(OandaAPI::Streaming::Client)
33
34
  fail ArgumentError, "expecting a namespace value" if namespace_segment.to_s.empty?
34
35
 
35
36
  @client = client
@@ -38,7 +39,7 @@ module OandaAPI
38
39
  extract_key_and_conditions conditions
39
40
  end
40
41
 
41
- # Returns a deep clone of +self+.
42
+ # Returns a deep clone of `self`.
42
43
  # @return [NamespaceProxy]
43
44
  def clone
44
45
  ns = self.dup
@@ -86,20 +87,24 @@ module OandaAPI
86
87
  end
87
88
 
88
89
  # Executes an API request and returns a resource object, or returns a
89
- # clone of +self+ for method chaining.
90
+ # clone of `self` for method chaining.
91
+ #
92
+ # @yield [OandaAPI::ResourceBase] if the method is `:stream`.
93
+ #
94
+ # @return [void] if the method is `:stream`.
90
95
  #
91
96
  # @return [OandaAPI::Client::NamespaceProxy] if the method is used
92
97
  # for chaining.
93
98
  #
94
99
  # @return [OandaAPI::ResourceBase] if the method is one of the supported
95
- # _terminating_ methods (+:create+, +:close+, +:delete+, +:get+, +:update+).
100
+ # _terminating_ methods (`:create`, `:close`, `:delete`, `:get`, `:update`).
96
101
  #
97
- # @return [OandaAPI::ResourceCollection] if the method is +:get+ and the
102
+ # @return [OandaAPI::ResourceCollection] if the method is `:get` and the
98
103
  # API returns a collection of resources.
99
- def method_missing(sym, *args)
104
+ def method_missing(sym, *args, &block)
100
105
  # Check for terminating method
101
- if [:create, :close, :delete, :get, :update].include?(sym)
102
- @client.execute_request sym, namespace, conditions
106
+ if [:create, :close, :delete, :get, :update, :stream].include?(sym)
107
+ @client.execute_request sym, namespace, conditions, &block
103
108
  else
104
109
  ns = self.clone
105
110
  ns.namespace_segments << Utils.pluralize(sym)
@@ -8,8 +8,7 @@ module OandaAPI
8
8
  # {http://developer.oanda.com/rest-live/authentication/ obtaining a personal access token from Oanda}.
9
9
  #
10
10
  # @example Example usage
11
- # token = ENV["oanda_practice_account_token"]
12
- # client = OandaAPI::Client::TokenClient.new :practice, token
11
+ # client = OandaAPI::Client::TokenClient.new :practice, ENV.fetch("OANDA_PRACTICE_TOKEN")
13
12
  #
14
13
  # # Get information for an account.
15
14
  # # See http://developer.oanda.com/rest-live/accounts/
@@ -25,7 +24,7 @@ module OandaAPI
25
24
  # @return [String] Oanda personal access token.
26
25
  #
27
26
  # @!attribute [rw] domain
28
- # @return [Symbol] identifies the Oanda subdomain (+:practice+ or +:live+)
27
+ # @return [Symbol] identifies the Oanda subdomain (`:practice` or `:live`)
29
28
  # accessed by the client.
30
29
  #
31
30
  # @!attribute [rw] default_params
@@ -15,7 +15,7 @@ module OandaAPI
15
15
  #
16
16
  #
17
17
  # @!attribute [r] domain
18
- # @return [Symbol] identifies the Oanda subdomain (+:sandbox+) which the
18
+ # @return [Symbol] identifies the Oanda subdomain (`:sandbox`) which the
19
19
  # client accesses.
20
20
  #
21
21
  # @!attribute [r] username
@@ -11,7 +11,7 @@ module OandaAPI
11
11
  USE_COMPRESSION = false
12
12
  USE_REQUEST_THROTTLING = false
13
13
 
14
- # The format in which dates will be returned by the API (+:rfc3339+ or +:unix+).
14
+ # The format in which dates will be returned by the API (`:rfc3339` or `:unix`).
15
15
  # See the Oanda Development Guide for more details about {http://developer.oanda.com/rest-live/development-guide/#date_Time_Format DateTime formats}.
16
16
  # @return [Symbol]
17
17
  def datetime_format
@@ -27,7 +27,7 @@ module OandaAPI
27
27
  end
28
28
 
29
29
  # The maximum number of requests per second allowed to be made through the
30
- # API. Only enforced if {#use_request_throttling?} is +true+.
30
+ # API. Only enforced if {#use_request_throttling?} is `true`.
31
31
  #
32
32
  # @return [Numeric]
33
33
  def max_requests_per_second
@@ -44,7 +44,7 @@ module OandaAPI
44
44
  end
45
45
 
46
46
  # The minimum amount of time in seconds that must elapse between consecutive requests to the API.
47
- # Determined by {#max_requests_per_second}. Only enforced if {#use_request_throttling?} is +true+.
47
+ # Determined by {#max_requests_per_second}. Only enforced if {#use_request_throttling?} is `true`.
48
48
  # @return [Float]
49
49
  def min_request_interval
50
50
  @min_request_interval ||= (1.0 / max_requests_per_second)
@@ -1,4 +1,7 @@
1
1
  module OandaAPI
2
- # Errors raised by the client.
2
+ # Request errors raised by the client.
3
3
  class RequestError < RuntimeError; end
4
+
5
+ # Raised when the streaming API server disconnects the client
6
+ class StreamingDisconnect < RuntimeError; end
4
7
  end
@@ -0,0 +1,13 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Heartbeat returned by the Streaming API.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/streaming/ Heartbeats}.
5
+ class Heartbeat < ResourceBase
6
+ attr_accessor :time
7
+
8
+ def time=(v)
9
+ @time = Time.parse v.to_s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,10 +1,11 @@
1
1
  module OandaAPI
2
+ require 'json'
2
3
  # Base class for all Resources.
3
4
  #
4
5
  # @!attribute [rw] location
5
- # @return [String] the +location+ header if one is returned in an API
6
+ # @return [String] the `location` header if one is returned in an API
6
7
  # response.
7
- # @example Using the +location+ attribute
8
+ # @example Using the `location` attribute
8
9
  # client = OandaAPI::Client::TokenClient.new :practice, token
9
10
  # all_transactions = client.account(123).alltransactions.get
10
11
  # all_transactions.location # => https://fxtrade.oanda.com/transactionhistory/d3aed6823c.json.zip
@@ -15,10 +16,16 @@ module OandaAPI
15
16
  # {http://developer.oanda.com/rest-live/development-guide/ Oanda Developer Guide}
16
17
  # for documentation about resource attributes.
17
18
  def initialize(attributes = {})
18
- initialize_attributes Utils.rubyize_keys(attributes.dup)
19
+ initialize_attributes Utils.rubyize_keys(attributes)
19
20
  @location = attributes.location if attributes.respond_to? :location
20
21
  end
21
22
 
23
+ # Serializes an instance as JSON
24
+ # @return [String] a stringified JSON representation of an instance
25
+ def to_json
26
+ JSON.generate @_attributes.merge(custom_attributes)
27
+ end
28
+
22
29
  private
23
30
 
24
31
  # @private
@@ -27,9 +34,16 @@ module OandaAPI
27
34
  # @param [Hash] attributes collection of resource attributes.
28
35
  # @return [void]
29
36
  def initialize_attributes(attributes)
37
+ @_attributes = attributes
30
38
  attributes.each do |key, value|
31
39
  send("#{key}=", value) if respond_to? key
32
40
  end
33
41
  end
42
+
43
+ # Provides additional attributes used in serialization.
44
+ # @return [Hash] returns a hash of customized attributes for serialization
45
+ def custom_attributes
46
+ {}.tap { |hash| hash[:location] = location if location }
47
+ end
34
48
  end
35
49
  end
@@ -32,7 +32,7 @@ module OandaAPI
32
32
  @attributes = Utils.rubyize_keys attributes
33
33
  @collection = @attributes.delete(resource_descriptor.collection_name) || []
34
34
  @collection.map! { |resource| resource_descriptor.resource_klass.new resource }
35
- @location = attributes.location if attributes.respond_to? :location
35
+ @location = attributes.location if attributes.respond_to? :location
36
36
  end
37
37
 
38
38
  # @yield [OandaAPI::ResourceBase]
@@ -47,21 +47,20 @@ module OandaAPI
47
47
 
48
48
  # @private
49
49
  # Responds to collection-scoped accessor methods that are specific to the
50
- # type of resource collection. For example, a +Candle+ collection includes
51
- # the collection-scoped methods +granularity+ and +instrument+.
50
+ # type of resource collection. For example, a `Candle` collection includes
51
+ # the collection-scoped methods `#granularity` and `#instrument`.
52
52
  def method_missing(sym, *args)
53
53
  case
54
- when @attributes.keys.include?(sym)
54
+ when @attributes.keys.include?(sym)
55
55
  @attributes[sym]
56
- when
57
- @collection.respond_to?(sym)
58
- @collection.send sym
56
+ when @collection.respond_to?(sym)
57
+ @collection.send sym
59
58
  else
60
59
  super
61
60
  end
62
61
  end
63
62
 
64
- # Returns +true+ for concrete, delegated and dynamic methods.
63
+ # Returns `true` for concrete, delegated and dynamic methods.
65
64
  # @return [Boolean]
66
65
  def respond_to?(sym)
67
66
  case
@@ -0,0 +1,153 @@
1
+ module OandaAPI
2
+ module Streaming
3
+ # Resource URI templates
4
+ BASE_URI = {
5
+ live: "https://stream-fxtrade.oanda.com/[API_VERSION]",
6
+ practice: "https://stream-fxpractice.oanda.com/[API_VERSION]"
7
+ }
8
+
9
+ # @example Example Usage
10
+ # client = OandaAPI::Streaming::Client.new :practice, ENV.fetch("ONADA_PRACTICE_TOKEN")
11
+ #
12
+ # # IMPORTANT
13
+ # # This code will block indefinitely because streaming executes as an infinite loop.
14
+ # prices = client.prices(account_id: 1234, instruments: %w[AUD_CAD AUD_CHF])
15
+ # prices.stream do |price|
16
+ #
17
+ # # The code in this block should handle the price as efficently
18
+ # # as possible, otherwise the connection could timeout.
19
+ # # For example, you could publish the tick on a queue to be handled
20
+ # # by some other thread or process.
21
+ # price # => OandaAPI::Resource::Price
22
+ # end
23
+ #
24
+ # client.events.stream do |transaction|
25
+ # transaction # => OandaAPI::Resource::Transaction
26
+ # end
27
+ #
28
+ # # -- Stopping the stream --
29
+ # # You may add a second argument to the block to yield the client itself.
30
+ # # You can use the client's `#stop!` method to terminate streaming.
31
+ # @prices = []
32
+ # prices = client.prices(account_id: 1234, instruments: %w[AUD_CAD AUD_CHF])
33
+ # prices.stream do |price, client|
34
+ # @prices << price
35
+ # client.stop! if @prices.size == 10
36
+ # end
37
+ #
38
+ # @!attribute [r] auth_token
39
+ # @return [String] Oanda personal access token.
40
+ #
41
+ # @!attribute [r] streaming_request
42
+ # @return [OandaAPI::Streaming::Request]
43
+ #
44
+ # @!attribute [rw] domain
45
+ # @return [Symbol] identifies the Oanda subdomain (`:practice` or `:live`)
46
+ # accessed by the client.
47
+ #
48
+ # @!attribute [rw] headers
49
+ # @return [Hash] parameters that are included with every API request
50
+ # as HTTP headers.
51
+ class Client
52
+ attr_reader :auth_token, :streaming_request
53
+ attr_accessor :domain, :headers
54
+
55
+ # @param [Symbol] domain see {#domain}
56
+ # @param [String] auth_token see {#auth_token}
57
+ def initialize(domain, auth_token)
58
+ super()
59
+ @auth_token = auth_token
60
+ self.domain = domain
61
+ @headers = auth
62
+ @streaming_request = nil
63
+ end
64
+
65
+ # Returns an absolute URI for a resource request.
66
+ #
67
+ # @param [String] path the path portion of the URI.
68
+ #
69
+ # @return [String] a URI.
70
+ def api_uri(path)
71
+ uri = "#{BASE_URI[domain]}#{path}"
72
+ uri.sub "[API_VERSION]", OandaAPI.configuration.rest_api_version
73
+ end
74
+
75
+ # Parameters used for authentication.
76
+ # @return [Hash]
77
+ def auth
78
+ { "Authorization" => "Bearer #{auth_token}" }
79
+ end
80
+
81
+ # @private
82
+ # Sets the domain the client can access.
83
+ # @return [void]
84
+ def domain=(value)
85
+ fail ArgumentError, "Invalid domain" unless OandaAPI::DOMAINS.include? value
86
+ @domain = value
87
+ end
88
+
89
+ # Determines whether emitted resources should include heartbeats.
90
+ # @param [boolean] value
91
+ def emit_heartbeats=(value)
92
+ @emit_heartbeats = value
93
+ streaming_request.emit_heartbeats = value if streaming_request
94
+ end
95
+
96
+ # Returns `true` if emitted resources include heartbeats. Defaults to false.
97
+ # @return [boolean]
98
+ def emit_heartbeats?
99
+ !!@emit_heartbeats
100
+ end
101
+
102
+ # Signals the streaming request to disconnect and terminates streaming.
103
+ # @return [void]
104
+ def stop!
105
+ streaming_request.stop! if running?
106
+ end
107
+
108
+ # Returns `true` if the client is currently streaming.
109
+ # @return [boolean]
110
+ def running?
111
+ !!(streaming_request && streaming_request.running?)
112
+ end
113
+
114
+ # @private
115
+ # Executes a streaming http request.
116
+ #
117
+ # @param [Symbol] _method Ignored.
118
+ #
119
+ # @param [String] path the path of an Oanda resource request.
120
+ #
121
+ # @param [Hash] conditions optional parameters that are converted into query parameters.
122
+ #
123
+ # @yield [OandaAPI:ResourceBase, OandaAPI::Streaming::Client] See {OandaAPI::Streaming::Request.stream}
124
+ #
125
+ # @return [void]
126
+ #
127
+ # @raise [OandaAPI::RequestError] if the API return code is not 2xx.
128
+ # @raise [OandaAPI::StreamingDisconnect] if the API disconnects.
129
+ def execute_request(_method, path, conditions = {}, &block)
130
+ Http::Exceptions.wrap_and_check do
131
+ @streaming_request = OandaAPI::Streaming::Request.new client: self,
132
+ uri: api_uri(path),
133
+ query: Utils.stringify_keys(conditions),
134
+ headers: OandaAPI.configuration.headers.merge(headers)
135
+ @streaming_request.stream(&block)
136
+ return nil
137
+ end
138
+
139
+ rescue Http::Exceptions::HttpException => e
140
+ raise OandaAPI::RequestError, e.message
141
+ end
142
+
143
+ private
144
+
145
+ # @private
146
+ # Enables method-chaining.
147
+ # @return [OandaAPI::Client::NamespaceProxy]
148
+ def method_missing(sym, *args)
149
+ OandaAPI::Client::NamespaceProxy.new self, sym, args.first
150
+ end
151
+ end
152
+ end
153
+ end