oanda_api 0.8.1 → 0.8.3

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.
Files changed (73) hide show
  1. data/.gitignore +38 -0
  2. data/.rspec_non_jruby +2 -0
  3. data/.yardopts +6 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE +22 -0
  7. data/README.md +218 -0
  8. data/Rakefile +23 -0
  9. data/lib/oanda_api.rb +25 -0
  10. data/lib/oanda_api/client/client.rb +175 -0
  11. data/lib/oanda_api/client/namespace_proxy.rb +112 -0
  12. data/lib/oanda_api/client/resource_descriptor.rb +52 -0
  13. data/lib/oanda_api/client/token_client.rb +69 -0
  14. data/lib/oanda_api/client/username_client.rb +53 -0
  15. data/lib/oanda_api/configuration.rb +167 -0
  16. data/lib/oanda_api/errors.rb +4 -0
  17. data/lib/oanda_api/resource/account.rb +37 -0
  18. data/lib/oanda_api/resource/candle.rb +29 -0
  19. data/lib/oanda_api/resource/instrument.rb +21 -0
  20. data/lib/oanda_api/resource/order.rb +74 -0
  21. data/lib/oanda_api/resource/position.rb +18 -0
  22. data/lib/oanda_api/resource/price.rb +16 -0
  23. data/lib/oanda_api/resource/trade.rb +23 -0
  24. data/lib/oanda_api/resource/transaction.rb +67 -0
  25. data/lib/oanda_api/resource_base.rb +35 -0
  26. data/lib/oanda_api/resource_collection.rb +77 -0
  27. data/lib/oanda_api/utils/utils.rb +101 -0
  28. data/lib/oanda_api/version.rb +3 -0
  29. data/oanda_api.gemspec +32 -0
  30. data/spec/fixtures/vcr_cassettes/account_id_order_id_close.yml +264 -0
  31. data/spec/fixtures/vcr_cassettes/account_id_order_id_get.yml +114 -0
  32. data/spec/fixtures/vcr_cassettes/account_id_order_options_create.yml +74 -0
  33. data/spec/fixtures/vcr_cassettes/account_id_order_options_update.yml +112 -0
  34. data/spec/fixtures/vcr_cassettes/account_id_orders_get.yml +118 -0
  35. data/spec/fixtures/vcr_cassettes/account_id_orders_options_get.yml +123 -0
  36. data/spec/fixtures/vcr_cassettes/account_id_positions_get.yml +112 -0
  37. data/spec/fixtures/vcr_cassettes/account_id_positions_instrument_close.yml +214 -0
  38. data/spec/fixtures/vcr_cassettes/account_id_positions_instrument_get.yml +110 -0
  39. data/spec/fixtures/vcr_cassettes/account_id_trade_id_close.yml +252 -0
  40. data/spec/fixtures/vcr_cassettes/account_id_trade_id_get.yml +112 -0
  41. data/spec/fixtures/vcr_cassettes/account_id_trade_options_modify.yml +110 -0
  42. data/spec/fixtures/vcr_cassettes/account_id_trades_filter_get.yml +118 -0
  43. data/spec/fixtures/vcr_cassettes/account_id_trades_get.yml +118 -0
  44. data/spec/fixtures/vcr_cassettes/account_id_transaction_id_get.yml +283 -0
  45. data/spec/fixtures/vcr_cassettes/account_id_transactions_options_get.yml +205 -0
  46. data/spec/fixtures/vcr_cassettes/accounts_create.yml +75 -0
  47. data/spec/fixtures/vcr_cassettes/accounts_get.yml +111 -0
  48. data/spec/fixtures/vcr_cassettes/accounts_id_get.yml +187 -0
  49. data/spec/fixtures/vcr_cassettes/candles_options_get.yml +79 -0
  50. data/spec/fixtures/vcr_cassettes/instruments_get.yml +501 -0
  51. data/spec/fixtures/vcr_cassettes/instruments_options_get.yml +81 -0
  52. data/spec/fixtures/vcr_cassettes/prices_options_get.yml +81 -0
  53. data/spec/fixtures/vcr_cassettes/sandbox_client.yml +116 -0
  54. data/spec/fixtures/vcr_cassettes/sandbox_client_account.yml +111 -0
  55. data/spec/fixtures/vcr_cassettes/sandbox_instrument_EUR_USD.yml +77 -0
  56. data/spec/oanda_api/client/client_spec.rb +107 -0
  57. data/spec/oanda_api/client/namespace_proxy_spec.rb +16 -0
  58. data/spec/oanda_api/client/resource_descriptor_spec.rb +39 -0
  59. data/spec/oanda_api/client/token_client_spec.rb +60 -0
  60. data/spec/oanda_api/client/username_client_spec.rb +31 -0
  61. data/spec/oanda_api/configuration_spec.rb +138 -0
  62. data/spec/oanda_api/examples/accounts_spec.rb +28 -0
  63. data/spec/oanda_api/examples/orders_spec.rb +68 -0
  64. data/spec/oanda_api/examples/positions_spec.rb +38 -0
  65. data/spec/oanda_api/examples/rates_spec.rb +46 -0
  66. data/spec/oanda_api/examples/trades_spec.rb +58 -0
  67. data/spec/oanda_api/examples/transactions_spec.rb +24 -0
  68. data/spec/oanda_api/resource_collection_spec.rb +109 -0
  69. data/spec/oanda_api/utils/utils_spec.rb +109 -0
  70. data/spec/spec_helper.rb +10 -0
  71. data/spec/support/client_helper.rb +60 -0
  72. data/spec/support/vcr.rb +7 -0
  73. metadata +124 -9
@@ -0,0 +1,4 @@
1
+ module OandaAPI
2
+ # Errors raised by the client.
3
+ class RequestError < RuntimeError; end
4
+ end
@@ -0,0 +1,37 @@
1
+ module OandaAPI
2
+ # Namespace for all resources.
3
+ module Resource
4
+ # Account value object.
5
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/accounts/ Accounts}.
6
+ class Account < ResourceBase
7
+ attr_accessor :account_currency,
8
+ :account_id,
9
+ :account_name,
10
+ :balance,
11
+ :margin_available,
12
+ :margin_rate,
13
+ :margin_used,
14
+ :open_orders,
15
+ :open_trades,
16
+ :password,
17
+ :realized_pl,
18
+ :unrealized_pl,
19
+ :username
20
+
21
+ alias_method :id, :account_id
22
+ alias_method :id=, :account_id=
23
+
24
+ alias_method :currency, :account_currency
25
+ alias_method :currency=, :account_currency=
26
+
27
+ alias_method :name, :account_name
28
+ alias_method :name=, :account_name=
29
+
30
+ def initialize(attributes = {})
31
+ @open_orders = []
32
+ @open_trades = []
33
+ super
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Candle value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/rates/#retrieveInstrumentHistory Candles}.
5
+ class Candle < ResourceBase
6
+ attr_accessor :close_ask,
7
+ :close_bid,
8
+ :close_mid,
9
+ :complete,
10
+ :high_ask,
11
+ :high_bid,
12
+ :high_mid,
13
+ :low_ask,
14
+ :low_bid,
15
+ :low_mid,
16
+ :open_ask,
17
+ :open_bid,
18
+ :open_mid,
19
+ :time,
20
+ :volume
21
+
22
+ alias_method :complete?, :complete
23
+
24
+ def time=(v)
25
+ @time = Time.parse v.to_s
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Instrument value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/rates/#getInstrumentList Instruments}.
5
+ class Instrument < ResourceBase
6
+ attr_accessor :display_name,
7
+ :halted,
8
+ :instrument,
9
+ :margin_rate,
10
+ :max_trade_units,
11
+ :max_trailing_stop,
12
+ :min_trailing_stop,
13
+ :pip,
14
+ :precision
15
+
16
+ alias_method :halted?, :halted
17
+ alias_method :name, :instrument
18
+ alias_method :name=, :instrument=
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,74 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Order value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/orders/ Orders}.
5
+ class Order < ResourceBase
6
+ attr_accessor :expiry,
7
+ :id,
8
+ :instrument,
9
+ :lower_bound,
10
+ :order_opened,
11
+ :price,
12
+ :side,
13
+ :stop_loss,
14
+ :take_profit,
15
+ :time,
16
+ :trades_closed,
17
+ :trade_opened,
18
+ :trade_reduced,
19
+ :trailing_stop,
20
+ :type,
21
+ :units,
22
+ :upper_bound
23
+
24
+ def initialize(attributes = {})
25
+ attribs = attributes.dup
26
+ self.order_opened = attribs.delete(order_opened) || {}
27
+ self.trade_opened = attribs.delete(trade_opened) || {}
28
+ self.trade_reduced = attribs.delete(trade_reduced) || {}
29
+ self.trades_closed = attribs.delete(trades_closed) || []
30
+ super attribs
31
+ end
32
+
33
+ def expiry=(v)
34
+ @expiry = Time.parse v.to_s
35
+ end
36
+
37
+ def time=(v)
38
+ @time = Time.parse v.to_s
39
+ end
40
+
41
+ def order_opened=(v)
42
+ @order_opened = OrderOpened.new v
43
+ end
44
+
45
+ def trade_opened=(v)
46
+ @trade_opened = TradeOpened.new v
47
+ end
48
+
49
+ def trade_reduced=(v)
50
+ @trade_reduced = TradeReduced.new v
51
+ end
52
+
53
+ # See the Oanda Developer Guide for {http://developer.oanda.com/rest-live/orders/ Order} details.
54
+ class OrderOpened < ResourceBase
55
+ attr_accessor :expiry, :id, :lower_bound, :side, :stop_loss,
56
+ :take_profit, :trailing_stop, :units, :upper_bound
57
+
58
+ def expiry=(v)
59
+ @expiry = Time.parse v.to_s
60
+ end
61
+ end
62
+
63
+ # See the Oanda Developer Guide for {http://developer.oanda.com/rest-live/orders/ Order} details.
64
+ class TradeOpened < ResourceBase
65
+ attr_accessor :id, :side, :stop_loss, :take_profit, :trailing_stop, :units
66
+ end
67
+
68
+ # See the Oanda Developer Guide for {http://developer.oanda.com/rest-live/orders/ Order} details.
69
+ class TradeReduced < ResourceBase
70
+ attr_accessor :id, :interest, :pl, :units
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,18 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Position value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/positions/ Positions}.
5
+ class Position < ResourceBase
6
+ attr_accessor :avg_price,
7
+ :ids,
8
+ :instrument,
9
+ :side,
10
+ :total_units
11
+
12
+ alias_method :price, :avg_price
13
+ alias_method :price=, :avg_price=
14
+ alias_method :units, :total_units
15
+ alias_method :units=, :total_units=
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Price value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/rates/#getCurrentPrices Prices}.
5
+ class Price < ResourceBase
6
+ attr_accessor :ask,
7
+ :bid,
8
+ :instrument,
9
+ :time
10
+
11
+ def time=(v)
12
+ @time = Time.parse v.to_s
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Trade value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/trades/ Trades}.
5
+ class Trade < ResourceBase
6
+ attr_accessor :id,
7
+ :instrument,
8
+ :price,
9
+ :profit,
10
+ :side,
11
+ :stop_loss,
12
+ :take_profit,
13
+ :time,
14
+ :trailing_amount,
15
+ :trailing_stop,
16
+ :units
17
+
18
+ def time=(v)
19
+ @time = Time.parse v.to_s
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ module OandaAPI
2
+ module Resource
3
+ # Transaction value object.
4
+ # See the Oanda Developer Guide for information about {http://developer.oanda.com/rest-live/transaction-history/ Transactions}.
5
+ class Transaction < ResourceBase
6
+ attr_accessor :account_balance,
7
+ :account_id,
8
+ :amount,
9
+ :expiry,
10
+ :id,
11
+ :instrument,
12
+ :interest,
13
+ :lower_bound,
14
+ :margin_rate,
15
+ :order_id,
16
+ :pl,
17
+ :price,
18
+ :rate,
19
+ :reason,
20
+ :side,
21
+ :stop_loss_price,
22
+ :stop_loss_price,
23
+ :take_profit_price,
24
+ :time,
25
+ :trade_id,
26
+ :trade_opened,
27
+ :trade_reduced,
28
+ :trailing_stop_loss_distance,
29
+ :type,
30
+ :units,
31
+ :upper_bound
32
+
33
+ def initialize(attributes = {})
34
+ attribs = attributes.dup
35
+ self.trade_opened = attribs.delete(:trade_opened) || {}
36
+ self.trade_reduced = attribs.delete(:trade_reduced) || {}
37
+ super attribs
38
+ end
39
+
40
+ def expiry=(v)
41
+ @expiry = Time.parse v.to_s
42
+ end
43
+
44
+ def time=(v)
45
+ @time = Time.parse v.to_s
46
+ end
47
+
48
+ def trade_opened=(v)
49
+ @trade_opened = TradeOpened.new v
50
+ end
51
+
52
+ def trade_reduced=(v)
53
+ @trade_reduced = TradeReduced.new v
54
+ end
55
+
56
+ # See http://developer.oanda.com/rest-live/transaction-history/ for attribute details.
57
+ class TradeOpened < ResourceBase
58
+ attr_accessor :id, :units
59
+ end
60
+
61
+ # See http://developer.oanda.com/rest-live/transaction-history/ for attribute details.
62
+ class TradeReduced < ResourceBase
63
+ attr_accessor :id, :interest, :pl, :units
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,35 @@
1
+ module OandaAPI
2
+ # Base class for all Resources.
3
+ #
4
+ # @!attribute [rw] location
5
+ # @return [String] the +location+ header if one is returned in an API
6
+ # response.
7
+ # @example Using the +location+ attribute
8
+ # client = OandaAPI::Client::TokenClient.new :practice, token
9
+ # all_transactions = client.account(123).alltransactions.get
10
+ # all_transactions.location # => https://fxtrade.oanda.com/transactionhistory/d3aed6823c.json.zip
11
+ class ResourceBase
12
+ attr_accessor :location
13
+
14
+ # @param [Hash] attributes collection of resource attributes. See the
15
+ # {http://developer.oanda.com/rest-live/development-guide/ Oanda Developer Guide}
16
+ # for documentation about resource attributes.
17
+ def initialize(attributes = {})
18
+ initialize_attributes Utils.rubyize_keys(attributes.dup)
19
+ @location = attributes.location if attributes.respond_to? :location
20
+ end
21
+
22
+ private
23
+
24
+ # @private
25
+ # Initializes attributes.
26
+ #
27
+ # @param [Hash] attributes collection of resource attributes.
28
+ # @return [void]
29
+ def initialize_attributes(attributes)
30
+ attributes.each do |key, value|
31
+ send("#{key}=", value) if respond_to? key
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,77 @@
1
+ module OandaAPI
2
+ # A collection of a specific resource. Returned by API requests that return
3
+ # collections. See the {http://developer.oanda.com/rest-live/development-guide/ Oanda Development Guide}
4
+ # for documentation about resource attributes expected for specific requests.
5
+ #
6
+ # @example Getting candle information
7
+ # client = OandaAPI::Client::TokenClient.new :practice, token
8
+ # candles = client.candles( instrument: "EUR_USD",
9
+ # granularity: "M1",
10
+ # count: 1,
11
+ # candle_format: "midpoint" ).get
12
+ #
13
+ # candles # => OandaAPI::ResourceCollection
14
+ # candles.granularity # => "M1"
15
+ # candles.instrument # => "EUR_USD"
16
+ # candles.first # => OandaAPI::Resource::Candle
17
+ #
18
+ # @!attribute [r] location
19
+ # @return [String] see {ResourceBase#location}
20
+ class ResourceCollection
21
+ include Enumerable
22
+
23
+ attr_reader :location
24
+
25
+ # @param [Hash] attributes collection of resource attributes
26
+ #
27
+ # @param [OandaAPI::Client::ResourceDescriptor] resource_descriptor metadata
28
+ # about the resource collection and its elements.
29
+ def initialize(attributes, resource_descriptor)
30
+ attributes = {} if attributes.nil? || attributes.respond_to?(:empty) && attributes.empty?
31
+ fail ArgumentError, "Expecting a Hash" unless attributes.respond_to? :each_pair
32
+ @attributes = Utils.rubyize_keys attributes
33
+ @collection = @attributes.delete(resource_descriptor.collection_name) || []
34
+ @collection.map! { |resource| resource_descriptor.resource_klass.new resource }
35
+ @location = attributes.location if attributes.respond_to? :location
36
+ end
37
+
38
+ # @yield [OandaAPI::ResourceBase]
39
+ # @return [Enumerator]
40
+ def each
41
+ if block_given?
42
+ @collection.each { |el| yield el }
43
+ else
44
+ @collection.each
45
+ end
46
+ end
47
+
48
+ # @private
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+.
52
+ def method_missing(sym, *args)
53
+ case
54
+ when @attributes.keys.include?(sym)
55
+ @attributes[sym]
56
+ when
57
+ @collection.respond_to?(sym)
58
+ @collection.send sym
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ # Returns +true+ for concrete, delegated and dynamic methods.
65
+ # @return [Boolean]
66
+ def respond_to?(sym)
67
+ case
68
+ when @attributes.keys.include?(sym)
69
+ true
70
+ when @collection.respond_to?(sym)
71
+ true
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,101 @@
1
+ module OandaAPI
2
+ # A few general purpose useful methods.
3
+ # Intentionally not implemented as monkey patches.
4
+ # Some wheel-reinventing to avoid adding extra dependencies.
5
+ module Utils
6
+ # Puts camelHumps where you expect them.
7
+ # @param [String] s
8
+ # @return [String]
9
+ def self.camelize(s)
10
+ s.to_s.gsub(/(?:_)([a-z\d]*)/i) { "#{$1.capitalize}" }.sub(/^(.)/) { $1.downcase }
11
+ end
12
+
13
+ # Naively plops an "s" at the end of a string.
14
+ # If the string is "" or nil, returns "".
15
+ # @param [String] s
16
+ # @return [String]
17
+ def self.pluralize(s)
18
+ return "" if s.to_s == ""
19
+ s.to_s =~ /s$/ ? s.to_s : "#{s}s"
20
+ end
21
+
22
+ # Returns a string with its trailing "s" vaporized.
23
+ # @param [String] s
24
+ # @return [String]
25
+ def self.singularize(s)
26
+ s.to_s.chomp("s")
27
+ end
28
+
29
+ # Returns a deep copy of a hash with its keys downcased, underscored
30
+ # and symbolized into ruby sweetness.
31
+ # @param [Hash] hash
32
+ # @return [Hash]
33
+ def self.rubyize_keys(hash)
34
+ transform_hash_keys(hash) { |key| underscore(key).to_sym }
35
+ end
36
+
37
+ # Returns a deep copy of a hash with its keys camelized,
38
+ # underscored and symbolized.
39
+ # @param [Hash] hash
40
+ # @return [Hash]
41
+ def self.stringify_keys(hash)
42
+ transform_hash_keys(hash) { |key| camelize key }
43
+ end
44
+
45
+ # Yields all keys of a hash, and safely applies whatever transform
46
+ # the block provides. Supports nested hashes.
47
+ #
48
+ # @param [Object] value can be a +Hash+, an +Array+ or scalar object type.
49
+ #
50
+ # @param [Block] block transforms the yielded key.
51
+ #
52
+ # @yield [Object] key the key to be prettied up.
53
+ #
54
+ # @return [Hash] a deep copy of the hash with it's keys transformed
55
+ # according to the design of the block.
56
+ def self.transform_hash_keys(value, &block)
57
+ case
58
+ when value.is_a?(Array)
59
+ value.map { |v| transform_hash_keys(v, &block) }
60
+ when value.is_a?(Hash)
61
+ Hash[value.map { |k, v| [yield(k), transform_hash_keys(v, &block)] }]
62
+ else
63
+ value
64
+ end
65
+ end
66
+
67
+ # Yields all key/value pairs of a hash, and safely applies whatever
68
+ # transform the block provides to the values.
69
+ # Supports nested hashes and arrays.
70
+ #
71
+ # @param [Object] value can be a +Hash+, an +Array+ or scalar object type.
72
+ #
73
+ # @param [Object] key
74
+ #
75
+ # @param [Block] block transforms the yielded value.
76
+ #
77
+ # @return [Hash] a deep copy of the hash with it's values transformed
78
+ # according to the design of the block.
79
+ def self.transform_hash_values(value, key = nil, &block)
80
+ case
81
+ when value.is_a?(Array)
82
+ value.map { |v| transform_hash_values(v, key, &block) }
83
+ when value.is_a?(Hash)
84
+ Hash[value.map { |k, v| [k, transform_hash_values(v, k, &block)] }]
85
+ else
86
+ yield key, value
87
+ end
88
+ end
89
+
90
+ # Converts a string from camelCase to snake_case.
91
+ # @param [String] s
92
+ # @return [String]
93
+ def self.underscore(s)
94
+ s.to_s
95
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
96
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
97
+ .tr("-", "_")
98
+ .downcase
99
+ end
100
+ end
101
+ end