ig_markets 0.1 → 0.2
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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +32 -16
- data/bin/ig_markets +29 -0
- data/lib/ig_markets.rb +15 -0
- data/lib/ig_markets/account_activity.rb +1 -1
- data/lib/ig_markets/account_transaction.rb +1 -1
- data/lib/ig_markets/application.rb +2 -2
- data/lib/ig_markets/cli/account_command.rb +40 -0
- data/lib/ig_markets/cli/activities_command.rb +33 -0
- data/lib/ig_markets/cli/confirmation_command.rb +32 -0
- data/lib/ig_markets/cli/main.rb +39 -0
- data/lib/ig_markets/cli/orders_command.rb +28 -0
- data/lib/ig_markets/cli/positions_command.rb +20 -0
- data/lib/ig_markets/cli/search_command.rb +30 -0
- data/lib/ig_markets/cli/sentiment_command.rb +35 -0
- data/lib/ig_markets/cli/sprints_command.rb +28 -0
- data/lib/ig_markets/cli/transactions_command.rb +56 -0
- data/lib/ig_markets/cli/watchlists_command.rb +34 -0
- data/lib/ig_markets/deal_confirmation.rb +4 -4
- data/lib/ig_markets/dealing_platform/position_methods.rb +3 -3
- data/lib/ig_markets/dealing_platform/sprint_market_position_methods.rb +1 -1
- data/lib/ig_markets/dealing_platform/watchlist_methods.rb +1 -3
- data/lib/ig_markets/dealing_platform/working_order_methods.rb +10 -10
- data/lib/ig_markets/format.rb +36 -0
- data/lib/ig_markets/historical_price_result.rb +1 -1
- data/lib/ig_markets/instrument.rb +2 -2
- data/lib/ig_markets/market.rb +10 -10
- data/lib/ig_markets/model.rb +23 -60
- data/lib/ig_markets/model/typecasters.rb +78 -0
- data/lib/ig_markets/payload_formatter.rb +19 -6
- data/lib/ig_markets/position.rb +7 -21
- data/lib/ig_markets/request_failed_error.rb +1 -1
- data/lib/ig_markets/sprint_market_position.rb +18 -3
- data/lib/ig_markets/version.rb +1 -1
- data/lib/ig_markets/working_order.rb +11 -11
- metadata +39 -64
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.rubocop.yml +0 -2
- data/.travis.yml +0 -10
- data/.yardopts +0 -4
- data/Gemfile +0 -2
- data/ig_markets.gemspec +0 -28
- data/spec/factories/ig_markets/account.rb +0 -14
- data/spec/factories/ig_markets/account_activity.rb +0 -21
- data/spec/factories/ig_markets/account_balance.rb +0 -8
- data/spec/factories/ig_markets/account_transaction.rb +0 -15
- data/spec/factories/ig_markets/application.rb +0 -21
- data/spec/factories/ig_markets/client_sentiment.rb +0 -7
- data/spec/factories/ig_markets/deal_confirmation.rb +0 -20
- data/spec/factories/ig_markets/historical_price_result.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_data_allowance.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_price.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_snapshot.rb +0 -10
- data/spec/factories/ig_markets/instrument.rb +0 -32
- data/spec/factories/ig_markets/instrument_currency.rb +0 -9
- data/spec/factories/ig_markets/instrument_expiry_details.rb +0 -6
- data/spec/factories/ig_markets/instrument_margin_deposit_band.rb +0 -8
- data/spec/factories/ig_markets/instrument_opening_hours.rb +0 -6
- data/spec/factories/ig_markets/instrument_rollover_details.rb +0 -6
- data/spec/factories/ig_markets/instrument_slippage_factor.rb +0 -6
- data/spec/factories/ig_markets/market.rb +0 -7
- data/spec/factories/ig_markets/market_dealing_rules.rb +0 -11
- data/spec/factories/ig_markets/market_dealing_rules_rule_details.rb +0 -6
- data/spec/factories/ig_markets/market_hierarchy_result.rb +0 -6
- data/spec/factories/ig_markets/market_hierarchy_result_hierarchy_node.rb +0 -6
- data/spec/factories/ig_markets/market_overview.rb +0 -22
- data/spec/factories/ig_markets/market_snapshot.rb +0 -17
- data/spec/factories/ig_markets/position.rb +0 -19
- data/spec/factories/ig_markets/sprint_market_position.rb +0 -16
- data/spec/factories/ig_markets/watchlist.rb +0 -9
- data/spec/factories/ig_markets/working_order.rb +0 -21
- data/spec/ig_markets/account_transaction_spec.rb +0 -30
- data/spec/ig_markets/dealing_platform/account_methods_spec.rb +0 -58
- data/spec/ig_markets/dealing_platform/client_sentiment_methods_spec.rb +0 -29
- data/spec/ig_markets/dealing_platform/market_methods_spec.rb +0 -80
- data/spec/ig_markets/dealing_platform/position_methods_spec.rb +0 -137
- data/spec/ig_markets/dealing_platform/sprint_market_position_methods_spec.rb +0 -39
- data/spec/ig_markets/dealing_platform/watchlist_methods_spec.rb +0 -89
- data/spec/ig_markets/dealing_platform/working_order_methods_spec.rb +0 -120
- data/spec/ig_markets/dealing_platform_spec.rb +0 -40
- data/spec/ig_markets/model_spec.rb +0 -127
- data/spec/ig_markets/password_encryptor_spec.rb +0 -23
- data/spec/ig_markets/payload_formatter_spec.rb +0 -19
- data/spec/ig_markets/position_spec.rb +0 -37
- data/spec/ig_markets/response_parser_spec.rb +0 -13
- data/spec/ig_markets/session_spec.rb +0 -134
- data/spec/spec_helper.rb +0 -14
- data/spec/support/factory_girl.rb +0 -7
- data/spec/support/random_test_order.rb +0 -3
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::DealingPlatform::WorkingOrderMethods do
|
|
2
|
-
let(:session) { IGMarkets::Session.new }
|
|
3
|
-
let(:platform) do
|
|
4
|
-
IGMarkets::DealingPlatform.new.tap do |platform|
|
|
5
|
-
platform.instance_variable_set :@session, session
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it 'can retrieve the current working orders' do
|
|
10
|
-
orders = [build(:working_order)]
|
|
11
|
-
|
|
12
|
-
get_result = {
|
|
13
|
-
working_orders: orders.map(&:attributes).map do |a|
|
|
14
|
-
{ market_data: a[:market], working_order_data: a }
|
|
15
|
-
end
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
expect(session).to receive(:get).with('workingorders', IGMarkets::API_V2).and_return(get_result)
|
|
19
|
-
expect(platform.working_orders.all).to eq(orders)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'can retrieve a single working order' do
|
|
23
|
-
orders = [build(:working_order, deal_id: 'id')]
|
|
24
|
-
|
|
25
|
-
get_result = {
|
|
26
|
-
working_orders: orders.map(&:attributes).map do |a|
|
|
27
|
-
{ market_data: a[:market], working_order_data: a }
|
|
28
|
-
end
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
expect(session).to receive(:get).with('workingorders', IGMarkets::API_V2).and_return(get_result)
|
|
32
|
-
expect(platform.working_orders['id']).to eq(orders[0])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'can create a working order' do
|
|
36
|
-
attributes = {
|
|
37
|
-
currency_code: 'USD',
|
|
38
|
-
direction: :buy,
|
|
39
|
-
epic: 'CS.D.EURUSD.CFD.IP',
|
|
40
|
-
level: 1.0,
|
|
41
|
-
size: 2.0,
|
|
42
|
-
time_in_force: :good_till_cancelled,
|
|
43
|
-
type: :limit
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
payload = {
|
|
47
|
-
currencyCode: 'USD',
|
|
48
|
-
direction: 'BUY',
|
|
49
|
-
epic: 'CS.D.EURUSD.CFD.IP',
|
|
50
|
-
expiry: '-',
|
|
51
|
-
forceOpen: false,
|
|
52
|
-
guaranteedStop: false,
|
|
53
|
-
level: 1.0,
|
|
54
|
-
size: 2.0,
|
|
55
|
-
timeInForce: 'GOOD_TILL_CANCELLED',
|
|
56
|
-
type: 'LIMIT'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
result = { deal_reference: 'reference' }
|
|
60
|
-
|
|
61
|
-
expect(session).to receive(:post).with('workingorders/otc', payload, IGMarkets::API_V2).and_return(result)
|
|
62
|
-
expect(platform.working_orders.create(attributes)).to eq(result.fetch(:deal_reference))
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it 'requires good_till_date if time_in_force is :good_till_date' do
|
|
66
|
-
attributes = {
|
|
67
|
-
currency_code: 'USD',
|
|
68
|
-
direction: :buy,
|
|
69
|
-
epic: 'CS.D.EURUSD.CFD.IP',
|
|
70
|
-
level: 1.0,
|
|
71
|
-
size: 2.0,
|
|
72
|
-
time_in_force: :good_till_date,
|
|
73
|
-
type: :limit
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
expect { platform.working_orders.create attributes }.to raise_error(ArgumentError)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
it 'can delete a working order' do
|
|
80
|
-
orders = [build(:working_order, deal_id: '1')]
|
|
81
|
-
|
|
82
|
-
get_result = {
|
|
83
|
-
working_orders: orders.map(&:attributes).map do |a|
|
|
84
|
-
{ market_data: a[:market], working_order_data: a }
|
|
85
|
-
end
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
del_result = { deal_reference: 'reference' }
|
|
89
|
-
|
|
90
|
-
expect(session).to receive(:get).with('workingorders', IGMarkets::API_V2).and_return(get_result)
|
|
91
|
-
expect(session).to receive(:delete).with('workingorders/otc/1', {}, IGMarkets::API_V1).and_return(del_result)
|
|
92
|
-
|
|
93
|
-
expect(platform.working_orders['1'].delete).to eq('reference')
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
it 'can update a working order' do
|
|
97
|
-
orders = [build(:working_order, deal_id: '1')]
|
|
98
|
-
|
|
99
|
-
get_result = {
|
|
100
|
-
working_orders: orders.map(&:attributes).map do |a|
|
|
101
|
-
{ market_data: a[:market], working_order_data: a }
|
|
102
|
-
end
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
payload = {
|
|
106
|
-
goodTillDate: '2015/10/20 10:45',
|
|
107
|
-
level: 1.03,
|
|
108
|
-
limitDistance: 20,
|
|
109
|
-
stopDistance: 30,
|
|
110
|
-
timeInForce: 'GOOD_TILL_DATE',
|
|
111
|
-
type: 'LIMIT'
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
put_result = { deal_reference: 'reference' }
|
|
115
|
-
|
|
116
|
-
expect(session).to receive(:get).with('workingorders', IGMarkets::API_V2).and_return(get_result)
|
|
117
|
-
expect(session).to receive(:put).with('workingorders/otc/1', payload, IGMarkets::API_V1).and_return(put_result)
|
|
118
|
-
expect(platform.working_orders['1'].update(level: 1.03, limit_distance: 20, stop_distance: 30)).to eq('reference')
|
|
119
|
-
end
|
|
120
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::DealingPlatform do
|
|
2
|
-
let(:session) { IGMarkets::Session.new }
|
|
3
|
-
let(:platform) do
|
|
4
|
-
IGMarkets::DealingPlatform.new.tap do |platform|
|
|
5
|
-
platform.instance_variable_set :@session, session
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it 'has a valid session' do
|
|
10
|
-
expect(IGMarkets::DealingPlatform.new.session).to be_an_instance_of(IGMarkets::Session)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
it 'can sign in' do
|
|
14
|
-
expect(session).to receive(:sign_in).and_return({})
|
|
15
|
-
expect(platform.sign_in('username', 'password', 'api_key', :production)).to eq({})
|
|
16
|
-
expect(session.username).to eq('username')
|
|
17
|
-
expect(session.password).to eq('password')
|
|
18
|
-
expect(session.api_key).to eq('api_key')
|
|
19
|
-
expect(session.platform).to eq(:production)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'can sign out' do
|
|
23
|
-
expect(session).to receive(:sign_out).and_return(nil)
|
|
24
|
-
expect(platform.sign_out).to eq(nil)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'can retrieve a deal confirmation' do
|
|
28
|
-
deal_confirmation = build :deal_confirmation
|
|
29
|
-
|
|
30
|
-
expect(session).to receive(:get).with('confirms/deal_id', IGMarkets::API_V1).and_return(deal_confirmation)
|
|
31
|
-
expect(platform.deal_confirmation('deal_id')).to eq(deal_confirmation)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it 'can retrieve the current applications' do
|
|
35
|
-
applications = [build(:application)]
|
|
36
|
-
|
|
37
|
-
expect(session).to receive(:get).with('operations/application', IGMarkets::API_V1).and_return(applications)
|
|
38
|
-
expect(platform.applications).to eq(applications)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::Model do
|
|
2
|
-
class TestModel < IGMarkets::Model
|
|
3
|
-
attribute :id
|
|
4
|
-
attribute :bool, IGMarkets::Boolean
|
|
5
|
-
attribute :string, String, regex: /\A[A-Z]{3}\Z/, nil_if: '-'
|
|
6
|
-
attribute :date, DateTime, format: '%Y-%m-%d'
|
|
7
|
-
attribute :float, Float
|
|
8
|
-
attribute :symbol, Symbol, allowed_values: [:a, :b]
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
let(:model) { TestModel.new }
|
|
12
|
-
|
|
13
|
-
it 'initializes with specified attribute values' do
|
|
14
|
-
expect(TestModel.new(id: 'test', bool: true).attributes).to eq(
|
|
15
|
-
id: 'test', bool: true, string: nil, date: nil, float: nil, symbol: nil)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'fails when initialized with an unknown attribute' do
|
|
19
|
-
expect { TestModel.new id: 'test', unknown: '' }.to raise_error(ArgumentError)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'has the correct getter and setter methods' do
|
|
23
|
-
[:id, :id=, :bool, :bool=, :string, :string=, :date, :date=, :float, :float=, :symbol, :symbol=].each do |id|
|
|
24
|
-
expect(model.respond_to?(id)).to eq(true)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it 'has the correct attributes hash' do
|
|
29
|
-
expect(model.attributes).to eq(id: nil, bool: nil, string: nil, date: nil, float: nil, symbol: nil)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'inspects attributes' do
|
|
33
|
-
expect(model.inspect).to eq('#<TestModel id: nil, bool: nil, string: nil, date: nil, float: nil, symbol: nil>')
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it 'inspects attributes in nested models' do
|
|
37
|
-
class TestModel2 < IGMarkets::Model
|
|
38
|
-
attribute :test, TestModel
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
model = TestModel2.new test: TestModel.new
|
|
42
|
-
|
|
43
|
-
expect(model.inspect).to eq(
|
|
44
|
-
'#<TestModel2 test: #<TestModel id: nil, bool: nil, string: nil, date: nil, float: nil, symbol: nil>>')
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it '#from accepts nil' do
|
|
48
|
-
expect(TestModel.from(nil)).to eq(nil)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it '#from accepts an attributes hash' do
|
|
52
|
-
expect(TestModel.from(id: 'test').attributes).to eq(
|
|
53
|
-
id: 'test', bool: nil, string: nil, date: nil, float: nil, symbol: nil)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it '#from accepts an instance and creates a copy' do
|
|
57
|
-
instance = TestModel.new(id: 'test')
|
|
58
|
-
|
|
59
|
-
expect(TestModel.from(instance)).to eq(instance)
|
|
60
|
-
expect(TestModel.from(instance)).not_to eql(instance)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it '#from accepts an Array of attributes hashes' do
|
|
64
|
-
expect(TestModel.from([{ id: 'a' }, { id: 'b' }])).to eq([TestModel.new(id: 'a'), TestModel.new(id: 'b')])
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it '#from raises on invalid inputs' do
|
|
68
|
-
['', DateTime.new, IGMarkets::Model.new].each do |invalid_input|
|
|
69
|
-
expect { TestModel.from(invalid_input) }.to raise_error(ArgumentError)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it 'raises ArgumentError for an invalid boolean' do
|
|
74
|
-
expect { model.bool = '' }.to raise_error(ArgumentError)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
it 'raises ArgumentError when string value does not match the regex' do
|
|
78
|
-
expect { model.string = 'abc' }.to raise_error(ArgumentError)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
it 'raises ArgumentError for an invalid date' do
|
|
82
|
-
expect { model.date = '2015-29-01' }.to raise_error(ArgumentError)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it 'raises ArgumentError for an invalid float' do
|
|
86
|
-
expect { model.float = 'a' }.to raise_error(ArgumentError)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
it 'raises ArgumentError for an invalid symbol' do
|
|
90
|
-
expect { model.symbol = :invalid }.to raise_error(ArgumentError)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it 'returns the correct set of allowed values for an attribute' do
|
|
94
|
-
expect(TestModel.allowed_values(:symbol)).to eq([:a, :b])
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
it 'sets attribute to nil when value matches a nil_if' do
|
|
98
|
-
model.string = '-'
|
|
99
|
-
expect(model.string).to eq(nil)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it 'correctly parses a date in the expected format' do
|
|
103
|
-
model.date = '2015-01-10'
|
|
104
|
-
expect(model.date).to eq(DateTime.new(2015, 1, 10))
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
context 'with all attributes set' do
|
|
108
|
-
before do
|
|
109
|
-
model.id = 'id'
|
|
110
|
-
model.bool = true
|
|
111
|
-
model.string = 'ABC'
|
|
112
|
-
model.date = '2015-01-10'
|
|
113
|
-
model.float = '1.0'
|
|
114
|
-
model.symbol = 'a'
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it 'has the correct attributes hash' do
|
|
118
|
-
expect(model.attributes).to eq(
|
|
119
|
-
id: 'id', bool: true, string: 'ABC', date: DateTime.new(2015, 1, 10), float: 1.0, symbol: :a)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it 'inspects attributes' do
|
|
123
|
-
expect(model.inspect).to eq(
|
|
124
|
-
'#<TestModel id: "id", bool: true, string: "ABC", date: 2015-01-10T00:00:00+00:00, float: 1.0, symbol: :a>')
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::PasswordEncryptor do
|
|
2
|
-
let(:encryptor) { IGMarkets::PasswordEncryptor.new }
|
|
3
|
-
let(:rsa_key_pair) { OpenSSL::PKey::RSA.new 2048 }
|
|
4
|
-
|
|
5
|
-
it 'can set its public key from an encoded public key' do
|
|
6
|
-
expect { encryptor.encoded_public_key = Base64.strict_encode64 rsa_key_pair.to_pem }.not_to raise_error
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it 'can encrypt a password' do
|
|
10
|
-
password = 'test'
|
|
11
|
-
|
|
12
|
-
encryptor.time_stamp = '1000'
|
|
13
|
-
encryptor.public_key = rsa_key_pair
|
|
14
|
-
|
|
15
|
-
encoded_encrypted_password = encryptor.encrypt password
|
|
16
|
-
|
|
17
|
-
decoded_encrypted_password = Base64.strict_decode64 encoded_encrypted_password
|
|
18
|
-
encoded_decrypted_password = rsa_key_pair.private_decrypt decoded_encrypted_password
|
|
19
|
-
decoded_decrypted_password = Base64.strict_decode64 encoded_decrypted_password
|
|
20
|
-
|
|
21
|
-
expect(decoded_decrypted_password).to eq("#{password}|#{encryptor.time_stamp}")
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::PayloadFormatter do
|
|
2
|
-
class PayloadModel < IGMarkets::Model
|
|
3
|
-
attribute :the_string
|
|
4
|
-
attribute :the_symbol, Symbol, allowed_values: [:two_three]
|
|
5
|
-
attribute :the_date, DateTime, format: '%Y/%m/%d'
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
it 'formats payloads correctly' do
|
|
9
|
-
model = PayloadModel.new the_string: 'string', the_symbol: :two_three, the_date: '2010/10/20'
|
|
10
|
-
|
|
11
|
-
expect(IGMarkets::PayloadFormatter.format(model)).to eq(
|
|
12
|
-
theString: 'string', theSymbol: 'TWO_THREE', theDate: '2010/10/20')
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'camel cases snake case strings' do
|
|
16
|
-
expect(IGMarkets::PayloadFormatter.snake_case_to_camel_case('one')).to eq(:one)
|
|
17
|
-
expect(IGMarkets::PayloadFormatter.snake_case_to_camel_case('one_two_three')).to eq(:oneTwoThree)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::Position do
|
|
2
|
-
let(:profitable_position) do
|
|
3
|
-
market = build :market_overview, bid: 4.0, lot_size: 3, scaling_factor: 5
|
|
4
|
-
build :position, currency: 'USD', level: 1.0, direction: :buy, size: 2, market: market
|
|
5
|
-
end
|
|
6
|
-
let(:unprofitable_position) do
|
|
7
|
-
market = build :market_overview, offer: 4.0, lot_size: 3, scaling_factor: 5
|
|
8
|
-
build :position, currency: 'USD', level: 1.0, direction: :sell, size: 2, market: market
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'knows if it has a trailing stop' do
|
|
12
|
-
expect(build(:position).trailing_stop?).to be false
|
|
13
|
-
expect(build(:position, trailing_step: 1, trailing_stop_distance: 10).trailing_stop?).to be true
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
it 'calculates correct price deltas' do
|
|
17
|
-
expect(profitable_position.price_delta).to eq(3.0)
|
|
18
|
-
expect(unprofitable_position.price_delta).to eq(-3.0)
|
|
19
|
-
expect(profitable_position.profitable?).to be true
|
|
20
|
-
expect(unprofitable_position.profitable?).to be false
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'calculates correct profit/loss amounts' do
|
|
24
|
-
expect(profitable_position.profit_loss).to eq(90)
|
|
25
|
-
expect(unprofitable_position.profit_loss).to eq(-90)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it 'correctly formats profit/loss amounts' do
|
|
29
|
-
expect(profitable_position.formatted_profit_loss).to eq('USD 90.00')
|
|
30
|
-
expect(unprofitable_position.formatted_profit_loss).to eq('USD -90.00')
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'correctly formats position size' do
|
|
34
|
-
expect(profitable_position.formatted_size).to eq('+2')
|
|
35
|
-
expect(unprofitable_position.formatted_size).to eq('-2')
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::ResponseParser do
|
|
2
|
-
it 'snake cases camel case keys' do
|
|
3
|
-
parsed = IGMarkets::ResponseParser.parse(update: 1, updateTime: 1, updateTimeUTC: 1, updateUTCTime: 1)
|
|
4
|
-
|
|
5
|
-
expect(parsed).to eq(update: 1, update_time: 1, update_time_utc: 1, update_utc_time: 1)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
it 'parses arrays' do
|
|
9
|
-
parsed = IGMarkets::ResponseParser.parse([{ aB: 1 }, { cD: 2 }])
|
|
10
|
-
|
|
11
|
-
expect(parsed).to eq([{ a_b: 1 }, { c_d: 2 }])
|
|
12
|
-
end
|
|
13
|
-
end
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
describe IGMarkets::Session do
|
|
2
|
-
let(:response) { instance_double 'RestClient::Response' }
|
|
3
|
-
let(:rest_client) { RestClient::Request }
|
|
4
|
-
|
|
5
|
-
context 'a non-signed in session' do
|
|
6
|
-
let(:session) do
|
|
7
|
-
IGMarkets::Session.new.tap do |session|
|
|
8
|
-
session.username = 'username'
|
|
9
|
-
session.password = 'password'
|
|
10
|
-
session.api_key = 'api_key'
|
|
11
|
-
session.platform = :production
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'is not alive' do
|
|
16
|
-
expect(session.alive?).to eq(false)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'can sign in' do
|
|
20
|
-
expect(response).to receive(:code).exactly(4).times.and_return(200)
|
|
21
|
-
expect(response).to receive(:headers).and_return(cst: '1', x_security_token: '2')
|
|
22
|
-
expect(response).to receive(:body).twice.and_return(
|
|
23
|
-
{ encryptionKey: Base64.strict_encode64(OpenSSL::PKey::RSA.new(256).to_pem), timeStamp: '1000' }.to_json,
|
|
24
|
-
{}.to_json
|
|
25
|
-
)
|
|
26
|
-
expect(rest_client).to receive(:execute).twice.and_return(response)
|
|
27
|
-
|
|
28
|
-
expect(session.sign_in).to eq(nil)
|
|
29
|
-
|
|
30
|
-
expect(session.cst).to eq('1')
|
|
31
|
-
expect(session.x_security_token).to match('2')
|
|
32
|
-
expect(session.alive?).to eq(true)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'fails to sign in when required attributes are missing' do
|
|
36
|
-
%i(username password api_key platform).each do |attribute|
|
|
37
|
-
session.send "#{attribute}=", nil
|
|
38
|
-
expect { session.sign_in }.to raise_error(ArgumentError)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context 'a signed in session' do
|
|
44
|
-
let(:session) do
|
|
45
|
-
IGMarkets::Session.new.tap do |s|
|
|
46
|
-
s.instance_variable_set :@cst, 'cst'
|
|
47
|
-
s.instance_variable_set :@x_security_token, 'x_security_token'
|
|
48
|
-
s.instance_variable_set :@api_key, 'api_key'
|
|
49
|
-
s.instance_variable_set :@platform, :production
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it 'is alive' do
|
|
54
|
-
expect(session.alive?).to eq(true)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it 'passes correct details for a post request' do
|
|
58
|
-
expect(response).to receive_messages(code: 200, body: { ids: [1, 2] }.to_json)
|
|
59
|
-
expect(rest_client).to receive(:execute).with(params(:post, 'url', id: 1)).and_return(response)
|
|
60
|
-
expect(session.post('url', { id: 1 }, IGMarkets::API_V1)).to eq(ids: [1, 2])
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it 'can sign out' do
|
|
64
|
-
expect(response).to receive_messages(code: 200, body: {}.to_json)
|
|
65
|
-
expect(rest_client).to receive(:execute).with(params(:delete, 'session')).and_return(response)
|
|
66
|
-
expect(session.sign_out).to eq(nil)
|
|
67
|
-
expect(session.alive?).to eq(false)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it 'fails when the HTTP response is not 200' do
|
|
71
|
-
expect(response).to receive_messages(code: 404, body: { errorCode: '1' }.to_json)
|
|
72
|
-
expect(rest_client).to receive(:execute).with(params(:get, 'url')).and_raise(RestClient::Exception, response)
|
|
73
|
-
expect { session.get('url', IGMarkets::API_V1) }.to raise_error do |error|
|
|
74
|
-
expect(error).to be_a(IGMarkets::RequestFailedError)
|
|
75
|
-
expect(error.error).to eq('1')
|
|
76
|
-
expect(error.http_code).to eq(404)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'handles when the HTTP response is not JSON' do
|
|
81
|
-
expect(response).to receive_messages(code: 404, body: 'not_valid_json')
|
|
82
|
-
expect(rest_client).to receive(:execute).with(params(:get, 'url')).and_raise(RestClient::Exception, response)
|
|
83
|
-
expect { session.get('url', IGMarkets::API_V1) }.to raise_error(IGMarkets::RequestFailedError)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it 'converts a SocketError into a RequestFailedError' do
|
|
87
|
-
expect(rest_client).to receive(:execute).with(params(:get, 'url')).and_raise(SocketError)
|
|
88
|
-
expect { session.get('url', IGMarkets::API_V1) }.to raise_error(IGMarkets::RequestFailedError)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it 'converts a RestClient::Exception that has no response into a RequestFailedError' do
|
|
92
|
-
expect(rest_client).to receive(:execute).with(params(:get, 'url')).and_raise(RestClient::Exception)
|
|
93
|
-
expect { session.get('url', IGMarkets::API_V1) }.to raise_error(IGMarkets::RequestFailedError)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
it 'can process a PUT request' do
|
|
97
|
-
expect(response).to receive_messages(code: 200, body: '')
|
|
98
|
-
expect(rest_client).to receive(:execute).with(params(:put, 'url', id: 1)).and_return(response)
|
|
99
|
-
expect(session.put('url', { id: 1 }, IGMarkets::API_V1)).to eq({})
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it 'can process a DELETE request with a payload' do
|
|
103
|
-
execute_params = params :post, 'url', id: 1
|
|
104
|
-
execute_params[:headers]['_method'] = :delete
|
|
105
|
-
|
|
106
|
-
expect(response).to receive_messages(code: 204, body: '')
|
|
107
|
-
expect(rest_client).to receive(:execute).with(execute_params).and_return(response)
|
|
108
|
-
expect(session.delete('url', { id: 1 }, IGMarkets::API_V1)).to eq({})
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 'inspects correctly' do
|
|
112
|
-
expect(session.inspect).to eq('#<IGMarkets::Session cst, x_security_token>')
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def headers
|
|
116
|
-
hash = {}
|
|
117
|
-
hash[:accept] = hash[:content_type] = 'application/json; charset=UTF-8'
|
|
118
|
-
hash[:version] = 1
|
|
119
|
-
hash[:cst] = 'cst'
|
|
120
|
-
hash[:x_security_token] = 'x_security_token'
|
|
121
|
-
hash[:'X-IG-API-KEY'] = 'api_key'
|
|
122
|
-
hash
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def params(method, url, payload = nil)
|
|
126
|
-
hash = {}
|
|
127
|
-
hash[:method] = method
|
|
128
|
-
hash[:url] = "https://api.ig.com/gateway/deal/#{url}"
|
|
129
|
-
hash[:headers] = headers
|
|
130
|
-
hash[:payload] = payload && payload.to_json
|
|
131
|
-
hash
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|