ig_markets 0.14 → 0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a9c0f861710bf880c830d2826c3e4ece9c2ef35
4
- data.tar.gz: 4c2f4dbd5d336489e2a343ac0015ff34c0910e2b
3
+ metadata.gz: 4205e8e7a404222272416344c5f21e912de43256
4
+ data.tar.gz: 55595208ec8a08d8941d68387c6c61e19c436d5d
5
5
  SHA512:
6
- metadata.gz: fa7dc0e4c01816d95e5b6dfec934c3a52e66b9b82ed27f5b777c19a89fa7bda5915b8f9bdba02ae6a3b8b42c251622ffa91c2c1a41e6c032ca4ae7300b611ca8
7
- data.tar.gz: 9ca3b4fe730001d4ed3d8900ec5483e3a10e386e9b772f4057e0696b9c3f8019889318faf6abda047735046dade967bcaf537a1a157b95e90e5eb751928feb23
6
+ metadata.gz: 230f8eb850b639d3a6fa479cd42101ecd89c901382951adaa5a69d064029623a30c042cbe9bc44ee16d11b0e1cf50c034dc99e0aa43f3081063b27cda4a797ec
7
+ data.tar.gz: 20991fd83b740be9a1bcc36ce2c112a7a63b2070adc9cf736617a130350b9810abe33663214b1b45889b3e53bda3741cc37f633068f1284cab30a09088d36d9b
data/CHANGELOG.md CHANGED
@@ -1,10 +1,21 @@
1
1
  # IG Markets Changelog
2
2
 
3
+ ### 0.15 — July 11, 2016
4
+
5
+ - Added `ig_markets self-test` command that can be run under a demo account in order to test the library against the
6
+ live IG Markets API
7
+ - If the API key or account's traffic allowance is exceeded then `IGMarkets::Session` now automatically retries the
8
+ request after a five second pause
9
+ - Added `IGMarkets::MarketHierarchyResult::HierarchyNode#markets` and
10
+ `IGMarkets::MarketHierarchyResult::HierarchyNode#nodes` methods
11
+
3
12
  ### 0.14 — July 2, 2016
4
13
 
5
14
  - Added `delete-all` subcommand to `ig_markets orders`
6
15
  - Added a `#reload` method that reloads a model's attributes in-place to `IGMarkets::Account`,
7
16
  `IGMarkets::ClientSentiment`, `IGMarkets::Market`, `IGMarkets::Position` and `IGMarkets::WorkingOrder`
17
+ - Support mass assignment of attributes on models
18
+ - Fixed a compatibility issue with recent IG API changes
8
19
 
9
20
  ### 0.13 — June 22, 2016
10
21
 
data/README.md CHANGED
@@ -85,6 +85,7 @@ commands and their subcommands is:
85
85
  - `ig_markets positions close-all [...]`
86
86
  - `ig_markets prices --epic EPIC --resolution RESOLUTION ...`
87
87
  - `ig_markets search QUERY [--type TYPE]`
88
+ - `ig_markets self-test`
88
89
  - `ig_markets sentiment MARKET`
89
90
  - `ig_markets sprints [list]`
90
91
  - `ig_markets sprints create ...`
@@ -0,0 +1,142 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets self-test` command.
4
+ class Main < Thor
5
+ desc 'self-test', 'Runs a self-test of this application against the live IG Markets API (demo accounts only)'
6
+
7
+ def self_test
8
+ self.class.begin_session(options) do |dealing_platform|
9
+ raise 'Error: self-tests must be run on a demo account' unless dealing_platform.session.platform == :demo
10
+
11
+ run_self_test dealing_platform
12
+
13
+ dealing_platform.sign_out
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def run_self_test(dealing_platform)
20
+ test_markets dealing_platform
21
+ test_positions dealing_platform
22
+ test_sprint_market_positions dealing_platform
23
+ test_working_orders dealing_platform
24
+ test_watchlists dealing_platform
25
+ test_client_sentiment dealing_platform
26
+ test_account_methods dealing_platform
27
+ test_applications dealing_platform
28
+ end
29
+
30
+ def test_markets(dealing_platform)
31
+ dealing_platform.markets.hierarchy '264134' # ID of the main 'Forex' node
32
+ dealing_platform.markets.search 'EURUSD'
33
+ @eur_usd = dealing_platform.markets['CS.D.EURUSD.CFD.IP']
34
+ @eur_usd_sprint = dealing_platform.markets['FM.D.EURUSD24.EURUSD24.IP']
35
+ @eur_usd.historical_prices resolution: :hour, number: 1
36
+ end
37
+
38
+ def test_positions(dealing_platform)
39
+ test_position_create dealing_platform
40
+ test_position_update
41
+ test_position_close dealing_platform
42
+ end
43
+
44
+ def test_position_create(dealing_platform)
45
+ deal_reference = dealing_platform.positions.create currency_code: 'USD', direction: :buy,
46
+ epic: 'CS.D.EURUSD.CFD.IP', size: 2
47
+
48
+ @position = dealing_platform.positions[dealing_platform.deal_confirmation(deal_reference).deal_id]
49
+
50
+ raise 'Error: failed creating position' unless @position
51
+ end
52
+
53
+ def test_position_update
54
+ stop_level = @position.level - 0.01
55
+
56
+ @position.update stop_level: stop_level
57
+ @position.reload
58
+
59
+ update_worked = (@position.stop_level - stop_level).abs < 0.001
60
+
61
+ raise 'Error: failed updating position' unless update_worked
62
+ end
63
+
64
+ def test_position_close(dealing_platform)
65
+ @position.close
66
+
67
+ raise 'Error: failed closing position' if dealing_platform.positions[@position.deal_id]
68
+ end
69
+
70
+ def test_sprint_market_positions(dealing_platform)
71
+ deal_reference = dealing_platform.sprint_market_positions.create direction: :buy,
72
+ epic: 'FM.D.EURUSD24.EURUSD24.IP',
73
+ expiry_period: :twenty_minutes, size: 100
74
+
75
+ deal_confirmation = dealing_platform.deal_confirmation deal_reference
76
+
77
+ sprint_market_position = dealing_platform.sprint_market_positions[deal_confirmation.deal_id]
78
+
79
+ raise 'Error: failed creating sprint market position' unless sprint_market_position
80
+ end
81
+
82
+ def test_working_orders(dealing_platform)
83
+ test_working_order_create dealing_platform
84
+ test_working_order_update dealing_platform
85
+ test_working_order_delete dealing_platform
86
+ end
87
+
88
+ def test_working_order_create(dealing_platform)
89
+ deal_reference = dealing_platform.working_orders.create currency_code: 'USD', direction: :buy,
90
+ epic: 'CS.D.EURUSD.CFD.IP', type: :limit,
91
+ level: @eur_usd.snapshot.bid - 0.1, size: 1
92
+
93
+ @working_order = dealing_platform.working_orders[dealing_platform.deal_confirmation(deal_reference).deal_id]
94
+
95
+ raise 'Error: failed creating working order' unless @working_order
96
+ end
97
+
98
+ def test_working_order_update(dealing_platform)
99
+ new_level = @eur_usd.snapshot.bid - 1
100
+
101
+ @working_order.update level: new_level
102
+ @working_order.reload
103
+
104
+ update_worked = (new_level - dealing_platform.working_orders[@working_order.deal_id].order_level).abs < 0.01
105
+
106
+ raise 'Error: failed updating working order' unless update_worked
107
+ end
108
+
109
+ def test_working_order_delete(dealing_platform)
110
+ @working_order.delete
111
+
112
+ raise 'Error: failed deleting working order' if dealing_platform.working_orders[@working_order.deal_id]
113
+ end
114
+
115
+ def test_watchlists(dealing_platform)
116
+ watchlist = dealing_platform.watchlists.create SecureRandom.hex, 'UA.D.AAPL.CASH.IP'
117
+ watchlist = dealing_platform.watchlists[watchlist.id]
118
+ watchlist.markets
119
+ watchlist.add_market 'CS.D.EURUSD.CFD.IP'
120
+ watchlist.remove_market 'CS.D.EURUSD.CFD.IP'
121
+ watchlist.delete
122
+
123
+ raise 'Error: failed deleting watchlist' if dealing_platform.watchlists[watchlist.id]
124
+ end
125
+
126
+ def test_account_methods(dealing_platform)
127
+ dealing_platform.account.all
128
+ dealing_platform.account.activities from: Date.today - 14
129
+ dealing_platform.account.transactions from: Date.today - 14
130
+ end
131
+
132
+ def test_client_sentiment(dealing_platform)
133
+ client_sentiment = dealing_platform.client_sentiment['EURUSD']
134
+ client_sentiment.related_sentiments
135
+ end
136
+
137
+ def test_applications(dealing_platform)
138
+ dealing_platform.applications
139
+ end
140
+ end
141
+ end
142
+ end
@@ -5,6 +5,20 @@ module IGMarkets
5
5
  class HierarchyNode < Model
6
6
  attribute :id
7
7
  attribute :name
8
+
9
+ # Returns an array of the markets under this node in the market hierarchy.
10
+ #
11
+ # @return [Array<MarketOverview>]
12
+ def markets
13
+ @dealing_platform.markets.hierarchy(id).markets
14
+ end
15
+
16
+ # Returns an array of the child nodes under this node in the market hierarchy.
17
+ #
18
+ # @return [Array<HierarchyNode>]
19
+ def nodes
20
+ @nodes ||= @dealing_platform.markets.hierarchy(id).nodes
21
+ end
8
22
  end
9
23
 
10
24
  attribute :markets, MarketOverview
@@ -101,10 +101,7 @@ module IGMarkets
101
101
 
102
102
  private
103
103
 
104
- HOST_URLS = {
105
- demo: 'https://demo-api.ig.com/gateway/deal/',
106
- live: 'https://api.ig.com/gateway/deal/'
107
- }.freeze
104
+ HOST_URLS = { demo: 'https://demo-api.ig.com/gateway/deal/', live: 'https://api.ig.com/gateway/deal/' }.freeze
108
105
 
109
106
  def validate_authentication
110
107
  %i(username password api_key).each do |attribute|
@@ -163,24 +160,34 @@ module IGMarkets
163
160
  rescue RestClient::Exception => exception
164
161
  raise RequestFailedError, exception.message unless exception.response
165
162
 
166
- return exception.response unless !options[:retry] && client_token_invalid?(exception.response)
163
+ return exception.response unless should_retry_request? exception.response, options
167
164
 
168
- sign_in
169
165
  execute_request options.merge(retry: true)
170
166
  rescue SocketError => socket_error
171
167
  raise RequestFailedError, socket_error
172
168
  end
173
169
 
174
- def client_token_invalid?(response)
175
- ResponseParser.parse_json(response.body)[:error_code] == 'error.security.client-token-invalid'
170
+ def should_retry_request?(response, options)
171
+ error_code = ResponseParser.parse_json(response.body)[:error_code]
172
+
173
+ if error_code == 'error.security.client-token-invalid' && !options[:retry]
174
+ sign_in
175
+ return true
176
+ end
177
+
178
+ if error_code =~ /^error\.public-api\.exceeded-(api-key|account)-allowance/
179
+ sleep 5
180
+ return true
181
+ end
182
+
183
+ false
176
184
  end
177
185
 
178
186
  def process_response(response)
179
187
  result = ResponseParser.parse_json response.body
188
+ http_code = response.code
180
189
 
181
- unless response.code >= 200 && response.code < 300
182
- raise RequestFailedError.new(result[:error_code], response.code)
183
- end
190
+ raise RequestFailedError.new(result[:error_code], http_code) unless http_code >= 200 && http_code < 300
184
191
 
185
192
  result
186
193
  end
@@ -1,4 +1,4 @@
1
1
  module IGMarkets
2
2
  # The version of this gem.
3
- VERSION = '0.14'.freeze
3
+ VERSION = '0.15'.freeze
4
4
  end
data/lib/ig_markets.rb CHANGED
@@ -4,6 +4,7 @@ require 'date'
4
4
  require 'json'
5
5
  require 'pry'
6
6
  require 'rest-client'
7
+ require 'securerandom'
7
8
  require 'terminal-table'
8
9
  require 'thor'
9
10
 
@@ -60,6 +61,7 @@ require 'ig_markets/cli/commands/markets_command'
60
61
  require 'ig_markets/cli/commands/performance_command'
61
62
  require 'ig_markets/cli/commands/prices_command'
62
63
  require 'ig_markets/cli/commands/search_command'
64
+ require 'ig_markets/cli/commands/self_test_command'
63
65
  require 'ig_markets/cli/commands/sentiment_command'
64
66
  require 'ig_markets/cli/commands/transactions_command'
65
67
  require 'ig_markets/cli/tables/table'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ig_markets
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.14'
4
+ version: '0.15'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Viney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-02 00:00:00.000000000 Z
11
+ date: 2016-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -233,6 +233,7 @@ files:
233
233
  - lib/ig_markets/cli/commands/positions_command.rb
234
234
  - lib/ig_markets/cli/commands/prices_command.rb
235
235
  - lib/ig_markets/cli/commands/search_command.rb
236
+ - lib/ig_markets/cli/commands/self_test_command.rb
236
237
  - lib/ig_markets/cli/commands/sentiment_command.rb
237
238
  - lib/ig_markets/cli/commands/sprints_command.rb
238
239
  - lib/ig_markets/cli/commands/transactions_command.rb