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.
- data/.gitignore +38 -0
- data/.rspec_non_jruby +2 -0
- data/.yardopts +6 -0
- data/Gemfile +13 -0
- data/Guardfile +7 -0
- data/LICENSE +22 -0
- data/README.md +218 -0
- data/Rakefile +23 -0
- data/lib/oanda_api.rb +25 -0
- data/lib/oanda_api/client/client.rb +175 -0
- data/lib/oanda_api/client/namespace_proxy.rb +112 -0
- data/lib/oanda_api/client/resource_descriptor.rb +52 -0
- data/lib/oanda_api/client/token_client.rb +69 -0
- data/lib/oanda_api/client/username_client.rb +53 -0
- data/lib/oanda_api/configuration.rb +167 -0
- data/lib/oanda_api/errors.rb +4 -0
- data/lib/oanda_api/resource/account.rb +37 -0
- data/lib/oanda_api/resource/candle.rb +29 -0
- data/lib/oanda_api/resource/instrument.rb +21 -0
- data/lib/oanda_api/resource/order.rb +74 -0
- data/lib/oanda_api/resource/position.rb +18 -0
- data/lib/oanda_api/resource/price.rb +16 -0
- data/lib/oanda_api/resource/trade.rb +23 -0
- data/lib/oanda_api/resource/transaction.rb +67 -0
- data/lib/oanda_api/resource_base.rb +35 -0
- data/lib/oanda_api/resource_collection.rb +77 -0
- data/lib/oanda_api/utils/utils.rb +101 -0
- data/lib/oanda_api/version.rb +3 -0
- data/oanda_api.gemspec +32 -0
- data/spec/fixtures/vcr_cassettes/account_id_order_id_close.yml +264 -0
- data/spec/fixtures/vcr_cassettes/account_id_order_id_get.yml +114 -0
- data/spec/fixtures/vcr_cassettes/account_id_order_options_create.yml +74 -0
- data/spec/fixtures/vcr_cassettes/account_id_order_options_update.yml +112 -0
- data/spec/fixtures/vcr_cassettes/account_id_orders_get.yml +118 -0
- data/spec/fixtures/vcr_cassettes/account_id_orders_options_get.yml +123 -0
- data/spec/fixtures/vcr_cassettes/account_id_positions_get.yml +112 -0
- data/spec/fixtures/vcr_cassettes/account_id_positions_instrument_close.yml +214 -0
- data/spec/fixtures/vcr_cassettes/account_id_positions_instrument_get.yml +110 -0
- data/spec/fixtures/vcr_cassettes/account_id_trade_id_close.yml +252 -0
- data/spec/fixtures/vcr_cassettes/account_id_trade_id_get.yml +112 -0
- data/spec/fixtures/vcr_cassettes/account_id_trade_options_modify.yml +110 -0
- data/spec/fixtures/vcr_cassettes/account_id_trades_filter_get.yml +118 -0
- data/spec/fixtures/vcr_cassettes/account_id_trades_get.yml +118 -0
- data/spec/fixtures/vcr_cassettes/account_id_transaction_id_get.yml +283 -0
- data/spec/fixtures/vcr_cassettes/account_id_transactions_options_get.yml +205 -0
- data/spec/fixtures/vcr_cassettes/accounts_create.yml +75 -0
- data/spec/fixtures/vcr_cassettes/accounts_get.yml +111 -0
- data/spec/fixtures/vcr_cassettes/accounts_id_get.yml +187 -0
- data/spec/fixtures/vcr_cassettes/candles_options_get.yml +79 -0
- data/spec/fixtures/vcr_cassettes/instruments_get.yml +501 -0
- data/spec/fixtures/vcr_cassettes/instruments_options_get.yml +81 -0
- data/spec/fixtures/vcr_cassettes/prices_options_get.yml +81 -0
- data/spec/fixtures/vcr_cassettes/sandbox_client.yml +116 -0
- data/spec/fixtures/vcr_cassettes/sandbox_client_account.yml +111 -0
- data/spec/fixtures/vcr_cassettes/sandbox_instrument_EUR_USD.yml +77 -0
- data/spec/oanda_api/client/client_spec.rb +107 -0
- data/spec/oanda_api/client/namespace_proxy_spec.rb +16 -0
- data/spec/oanda_api/client/resource_descriptor_spec.rb +39 -0
- data/spec/oanda_api/client/token_client_spec.rb +60 -0
- data/spec/oanda_api/client/username_client_spec.rb +31 -0
- data/spec/oanda_api/configuration_spec.rb +138 -0
- data/spec/oanda_api/examples/accounts_spec.rb +28 -0
- data/spec/oanda_api/examples/orders_spec.rb +68 -0
- data/spec/oanda_api/examples/positions_spec.rb +38 -0
- data/spec/oanda_api/examples/rates_spec.rb +46 -0
- data/spec/oanda_api/examples/trades_spec.rb +58 -0
- data/spec/oanda_api/examples/transactions_spec.rb +24 -0
- data/spec/oanda_api/resource_collection_spec.rb +109 -0
- data/spec/oanda_api/utils/utils_spec.rb +109 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/client_helper.rb +60 -0
- data/spec/support/vcr.rb +7 -0
- metadata +124 -9
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/client_helper'
|
3
|
+
|
4
|
+
describe "OandaAPI::Resource::Position" do
|
5
|
+
let(:account) { ClientHelper.account }
|
6
|
+
|
7
|
+
it "gets all open positions", :vcr do
|
8
|
+
VCR.use_cassette("account(id).positions.get") do
|
9
|
+
ClientHelper.create_trade
|
10
|
+
positions = account.positions.get
|
11
|
+
expect(positions.first).to be_an OandaAPI::Resource::Position
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets the position for an instrument", :vcr do
|
16
|
+
VCR.use_cassette("account(id).positions(instrument).get") do
|
17
|
+
ClientHelper.create_trade(instrument: "USD_JPY")
|
18
|
+
position = account.positions("USD_JPY").get
|
19
|
+
expect(position).to be_an OandaAPI::Resource::Position
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "closes an existing position", :vcr do
|
24
|
+
VCR.use_cassette("account(id).positions(instrument).close") do
|
25
|
+
ClientHelper.create_trade(instrument: "USD_JPY")
|
26
|
+
|
27
|
+
# Verify we have an open position.
|
28
|
+
position = account.position("USD_JPY").get
|
29
|
+
expect(position).to be_an OandaAPI::Resource::Position
|
30
|
+
|
31
|
+
account.position("USD_JPY").close
|
32
|
+
|
33
|
+
# Verify that the position has been closed.
|
34
|
+
positions = account.positions.get
|
35
|
+
expect(positions.map(&:instrument)).to_not include "USD_JPY"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/client_helper'
|
3
|
+
|
4
|
+
describe "OandaAPI::Resource" do
|
5
|
+
let(:client) { ClientHelper.client }
|
6
|
+
|
7
|
+
describe "Instrument" do
|
8
|
+
it "gets all instruments", :vcr do
|
9
|
+
VCR.use_cassette("instruments.get") do
|
10
|
+
instruments = client.instruments.get
|
11
|
+
expect(instruments.first).to be_an OandaAPI::Resource::Instrument
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets a filtered list of instruments", :vcr do
|
16
|
+
VCR.use_cassette("instruments(options).get") do
|
17
|
+
instruments = client.instruments(instruments: %w(EUR_USD EUR_CAD),
|
18
|
+
fields: %w(pip precision))
|
19
|
+
.get
|
20
|
+
expect(instruments.first).to be_an OandaAPI::Resource::Instrument
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "Price" do
|
26
|
+
it "gets a filtered list of prices", :vcr do
|
27
|
+
VCR.use_cassette("prices(options).get") do
|
28
|
+
prices = client.prices(instruments: %w(EUR_USD EUR_CAD)).get
|
29
|
+
expect(prices.first).to be_an OandaAPI::Resource::Price
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "Candle" do
|
35
|
+
it "gets candles for an instrument", :vcr do
|
36
|
+
VCR.use_cassette("candles(options).get") do
|
37
|
+
candles = client.candles(instrument: "EUR_USD",
|
38
|
+
granularity: "M1",
|
39
|
+
count: 1,
|
40
|
+
candle_format: "midpoint")
|
41
|
+
.get
|
42
|
+
expect(candles.first).to be_an OandaAPI::Resource::Candle
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/client_helper'
|
3
|
+
|
4
|
+
describe "OandaAPI::Resource::Trade" do
|
5
|
+
let(:account) { ClientHelper.account }
|
6
|
+
|
7
|
+
it "gets a list of trades", :vcr do
|
8
|
+
VCR.use_cassette("account(id).trades.get") do
|
9
|
+
ClientHelper.create_trade
|
10
|
+
trades = account.trades.get
|
11
|
+
expect(trades.first).to be_an OandaAPI::Resource::Trade
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets a filtered list of trades", :vcr do
|
16
|
+
VCR.use_cassette("account(id).trades(filter).get") do
|
17
|
+
ClientHelper.create_trade(instrument: "USD_JPY")
|
18
|
+
trades = account.trades(instrument: "USD_JPY").get
|
19
|
+
expect(trades.first).to be_an OandaAPI::Resource::Trade
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "gets information on a specific trade", :vcr do
|
24
|
+
VCR.use_cassette("account(id).trade(id).get") do
|
25
|
+
trade_id = ClientHelper.create_trade.trade_opened.id
|
26
|
+
trade = account.trade(trade_id).get
|
27
|
+
expect(trade).to be_an OandaAPI::Resource::Trade
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "modifies an open trade " do
|
32
|
+
VCR.use_cassette("account(id).trade(options).modify") do
|
33
|
+
order = ClientHelper.create_trade
|
34
|
+
modified_order =
|
35
|
+
account.trade(id: order.trade_opened.id, take_profit: order.price + 2.0)
|
36
|
+
.update
|
37
|
+
expect(modified_order).to be_an OandaAPI::Resource::Trade
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "closes an open trade" do
|
42
|
+
VCR.use_cassette("account(id).trade(id).close") do
|
43
|
+
ClientHelper.create_trade(instrument: "USD_JPY").trade_opened.id
|
44
|
+
|
45
|
+
# Get the oldest open trade for USD_JPY, so we can close that trade.
|
46
|
+
oldest_trade =
|
47
|
+
account.trades(instrument: "USD_JPY")
|
48
|
+
.get
|
49
|
+
.min_by(&:id)
|
50
|
+
|
51
|
+
closed_trade = account.trade(oldest_trade.id).close
|
52
|
+
expect(closed_trade).to be_an OandaAPI::Resource::Trade
|
53
|
+
|
54
|
+
open_trades = account.trades.get
|
55
|
+
expect(open_trades.map(&:id)).to_not include oldest_trade.id
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/client_helper'
|
3
|
+
|
4
|
+
describe "OandaAPI::Transaction" do
|
5
|
+
let(:account) { ClientHelper.account }
|
6
|
+
|
7
|
+
it "gets transaction history" do
|
8
|
+
VCR.use_cassette("account(id).transactions(options).get") do
|
9
|
+
ClientHelper.create_trade(instrument: "USD_JPY")
|
10
|
+
transactions = account.transactions(instrument: "USD_JPY").get
|
11
|
+
expect(transactions.first).to be_an OandaAPI::Resource::Transaction
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets information for a specific transaction" do
|
16
|
+
VCR.use_cassette("account(id).transaction(id).get") do
|
17
|
+
ClientHelper.create_trade(instrument: "USD_JPY")
|
18
|
+
id = account.transactions(instrument: "USD_JPY").get.first.id
|
19
|
+
transaction = account.transaction(id).get
|
20
|
+
expect(transaction).to be_an OandaAPI::Resource::Transaction
|
21
|
+
expect(transaction.trade_opened).to be_a OandaAPI::Resource::Transaction::TradeOpened
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "OandaAPI::ResourceCollection" do
|
4
|
+
let(:resource_descriptor) { OandaAPI::Client::ResourceDescriptor.new "/candles", :get }
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
context "with a collection attribute matching the one expected by the resource descriptor" do
|
8
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({ candles: [{ volume: 10 }] }, resource_descriptor) }
|
9
|
+
|
10
|
+
it "returns a ResourceCollection" do
|
11
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is Enumerable" do
|
15
|
+
expect(resource_collection).to be_an Enumerable
|
16
|
+
end
|
17
|
+
|
18
|
+
it "contains elements of the expected resource" do
|
19
|
+
expect(resource_collection.first).to be_an OandaAPI::Resource::Candle
|
20
|
+
end
|
21
|
+
|
22
|
+
it "contains initialized resource elements" do
|
23
|
+
expect(resource_collection.first.volume).to eq 10
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "when the collection element is empty" do
|
27
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({ candles: [] }, resource_descriptor) }
|
28
|
+
it "returns a ResourceCollection" do
|
29
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
30
|
+
end
|
31
|
+
it "is an empty collection" do
|
32
|
+
expect(resource_collection.empty?).to be true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with extra attributes" do
|
37
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({ candles: [], extra: "I'm special" }, resource_descriptor) }
|
38
|
+
|
39
|
+
it "returns a ResourceCollection" do
|
40
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has an accessor for the extra attributes" do
|
44
|
+
expect(resource_collection.extra).to eq "I'm special"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "without the collection attribute expected by the resource descriptor " do
|
50
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({}, resource_descriptor) }
|
51
|
+
|
52
|
+
it "returns a ResourceCollection" do
|
53
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "is Enumerable" do
|
57
|
+
expect(resource_collection).to be_an Enumerable
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is an empty collection" do
|
61
|
+
expect(resource_collection.empty?).to be true
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "with extra attributes" do
|
65
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({ extra: "I'm special" }, resource_descriptor) }
|
66
|
+
|
67
|
+
it "returns a ResourceCollection" do
|
68
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "has an accessor for the extra attribute" do
|
72
|
+
expect(resource_collection.extra).to eq "I'm special"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with nil" do
|
78
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new(nil, resource_descriptor) }
|
79
|
+
|
80
|
+
it "returns a ResourceCollection" do
|
81
|
+
expect(resource_collection).to be_an(OandaAPI::ResourceCollection)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "is Enumerable" do
|
85
|
+
expect(resource_collection).to be_an Enumerable
|
86
|
+
end
|
87
|
+
|
88
|
+
it "is an empty collection" do
|
89
|
+
expect(resource_collection.empty?).to be true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#respond_to?" do
|
95
|
+
let(:resource_collection) { OandaAPI::ResourceCollection.new({ candles: [], extra: "I'm special" }, resource_descriptor) }
|
96
|
+
|
97
|
+
it "responds to collection methods" do
|
98
|
+
expect(resource_collection.respond_to?(:shuffle)).to be true
|
99
|
+
end
|
100
|
+
|
101
|
+
it "responds to dynamic accessors for extra attributes" do
|
102
|
+
expect(resource_collection.respond_to?(:extra)).to be true
|
103
|
+
end
|
104
|
+
|
105
|
+
it "responds to concrete accessors" do
|
106
|
+
expect(resource_collection.respond_to?(:location)).to be true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "OandaAPI::Utils" do
|
4
|
+
describe ".camelize" do
|
5
|
+
it "puts humps on a string starting out low" do
|
6
|
+
expect(OandaAPI::Utils.camelize("ruby_is_awesome")).to eq("rubyIsAwesome")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "works with already camelized" do
|
10
|
+
expect(OandaAPI::Utils.camelize("imACamel")).to eq("imACamel")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "works with numbers" do
|
14
|
+
expect(OandaAPI::Utils.camelize("a_number_1_concern")).to eq("aNumber1Concern")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "works with symbols" do
|
18
|
+
expect(OandaAPI::Utils.camelize(:ugly_symbol)).to eq("uglySymbol")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "turns nil to empty" do
|
22
|
+
expect(OandaAPI::Utils.camelize(nil)).to eq("")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "ignores empty" do
|
26
|
+
expect(OandaAPI::Utils.camelize("")).to eq("")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".pluralize" do
|
31
|
+
it "naively adds an s to the end of strings" do
|
32
|
+
expect(OandaAPI::Utils.pluralize("thing")).to eq("things")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "doesn't add an s if it's already there" do
|
36
|
+
expect(OandaAPI::Utils.pluralize("things")).to eq("things")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "turns nil to empty" do
|
40
|
+
expect(OandaAPI::Utils.pluralize(nil)).to eq("")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "ignores empty" do
|
44
|
+
expect(OandaAPI::Utils.pluralize("")).to eq("")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".singularize" do
|
49
|
+
it "naively removes trailing s" do
|
50
|
+
expect(OandaAPI::Utils.singularize("things")).to eq("thing")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "turns nil to empty" do
|
54
|
+
expect(OandaAPI::Utils.singularize(nil)).to eq("")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "ignores empty" do
|
58
|
+
expect(OandaAPI::Utils.singularize("")).to eq("")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe ".rubyize_keys" do
|
63
|
+
it "converts all keys in a hash into snake_cased_symbols, " do
|
64
|
+
hash = { "a" => "a", "b" => [{ "a" => "a" }], :c => "c", "camelCase" => "" }
|
65
|
+
symbolized_hash = { :a => "a", :b => [{ :a => "a" }], :c => "c", :camel_case => "" }
|
66
|
+
expect(OandaAPI::Utils.rubyize_keys(hash)).to eq(symbolized_hash)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".underscore" do
|
71
|
+
it "converts a string to snake_case" do
|
72
|
+
expect(OandaAPI::Utils.underscore("camelCase-dashedTLA")).to eq("camel_case_dashed_tla")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "turns nil to empty" do
|
76
|
+
expect(OandaAPI::Utils.underscore(nil)).to eq("")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "ignores empty" do
|
80
|
+
expect(OandaAPI::Utils.underscore("")).to eq("")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".transform_hash_keys" do
|
85
|
+
it "yields every key in a nested hash" do
|
86
|
+
hash = { "a" => "a", :b => [{ :c => "c" }], :d => "d" }
|
87
|
+
expect { |b| OandaAPI::Utils.transform_hash_keys(hash, &b) }.to yield_successive_args("a", :b, :c, :d)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "replaces hash keys with the yielded return values" do
|
91
|
+
hash = { "a" => "a", "b" => [{ "c" => "c" }], "d" => "d" }
|
92
|
+
transformed_hash = { :a => "a", :b => [{ :c => "c" }], :d => "d" }
|
93
|
+
expect(OandaAPI::Utils.transform_hash_keys(hash) { |key| key.to_sym }).to eq(transformed_hash)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe ".transform_hash_values" do
|
98
|
+
it "yields every scalar key,value pair in a nested hash" do
|
99
|
+
hash = { "a" => "a", :b => [{ :c => "c" }], :d => "d" }
|
100
|
+
expect { |b| OandaAPI::Utils.transform_hash_values(hash, &b) }.to yield_successive_args(["a", "a"], [:c, "c"], [:d, "d"])
|
101
|
+
end
|
102
|
+
|
103
|
+
it "replaces scalar hash element values with the yielded return values" do
|
104
|
+
hash = { :a => "a", :b => [{ :c => "c" }], :d => "d" }
|
105
|
+
transformed_hash = { :a => :a, :b => [{ :c => :c }], :d => :d }
|
106
|
+
expect(OandaAPI::Utils.transform_hash_values(hash) { |_key, value| value.to_sym }).to eq(transformed_hash)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
lib = File.expand_path("../../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'oanda_api'
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
c.before(:suite) do
|
7
|
+
# Disable request throttling because all examples are stubbed with VCR and webmock.
|
8
|
+
OandaAPI.configure { |config| config.use_request_throttling = false }
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'support/vcr'
|
2
|
+
module ClientHelper
|
3
|
+
# Returns a sandbox client
|
4
|
+
def self.client
|
5
|
+
VCR.use_cassette("sandbox_client") do
|
6
|
+
client = OandaAPI::Client::UsernameClient.new "_"
|
7
|
+
new_account = client.account.create
|
8
|
+
OandaAPI::Client::UsernameClient.new new_account.username
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the account namespace for the client
|
13
|
+
def self.account
|
14
|
+
VCR.use_cassette("sandbox_client_account") do
|
15
|
+
account_id = ClientHelper.client.accounts.get.first.account_id
|
16
|
+
ClientHelper.client.account(account_id)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a trade
|
21
|
+
def self.create_trade(instrument: "USD_JPY",
|
22
|
+
type: "market",
|
23
|
+
side: "buy",
|
24
|
+
units: 10_000,
|
25
|
+
price: nil,
|
26
|
+
expiry: (Time.now + 3600).utc.to_datetime.rfc3339)
|
27
|
+
opts =
|
28
|
+
case type
|
29
|
+
when "market"
|
30
|
+
{ instrument: instrument,
|
31
|
+
type: type,
|
32
|
+
side: side,
|
33
|
+
units: units }
|
34
|
+
when "limit"
|
35
|
+
{ instrument: instrument,
|
36
|
+
type: type,
|
37
|
+
side: side,
|
38
|
+
units: units,
|
39
|
+
price: price,
|
40
|
+
expiry: expiry }
|
41
|
+
else
|
42
|
+
fail ArgumentError, "invalid order type: #{type}"
|
43
|
+
end
|
44
|
+
account.order(opts).create
|
45
|
+
end
|
46
|
+
|
47
|
+
singleton_class.send(:alias_method, :create_order, :create_trade)
|
48
|
+
|
49
|
+
# Returns a lightweight instrument object
|
50
|
+
def self.instrument(instrument)
|
51
|
+
VCR.use_cassette("sandbox_instrument_#{instrument}") do
|
52
|
+
price = ClientHelper.client.prices(instruments: instrument).get.first
|
53
|
+
o = Struct.new(:instrument, :bid, :ask).new
|
54
|
+
o.instrument = instrument
|
55
|
+
o.bid = price.bid
|
56
|
+
o.ask = price.ask
|
57
|
+
o
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|