ig_markets 0.17 → 0.18

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c696f25e6b960d65896474cf519c7492f8ad195
4
- data.tar.gz: 54dcc2e3caa92373344c3faf7c3a1ea8f916dcf2
3
+ metadata.gz: 2b3fab7b62a9c929a8262f9ef104c200278e03b2
4
+ data.tar.gz: 1fe0c83b896f00e493a2e7a4257fb2f43a80188f
5
5
  SHA512:
6
- metadata.gz: 3b0052a80212495c796d6ab5c75188690f259d640fa96982b7c0cb7f7b636014bc43cea9e5102a4bb17e8c07cb0f671d634db469165ab83b002d66d7bdde53b6
7
- data.tar.gz: 588dd121edd94b04546afa353802f39ccdde3c972684f17d297d6d2d649baa2cce25968dfc82a6876e06ce7e0e86093236ba4a13720d9db98531a437c057cb17
6
+ metadata.gz: e3325e6ea4704dc3bc75f333d82718115a82febc8aa4f814bd10f3495bdf27a3fc43bafa59b39fe0a7c01a454c8417478ef4aa9588551cfbe5875ab4927e2ae6
7
+ data.tar.gz: e4578ffa1bb86eab2cfb2b32a0223e9792db69ce50f433e6da2936415fac0cc248d51a605b37a1a777434da01697e6e5def084e5911faad5c1ad565b9fade536
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # IG Markets Changelog
2
2
 
3
+ ### 0.18 — August 2, 2016
4
+
5
+ - Added support for the recently added `#date` attribute on `IGMarkets::DealConfirmation`
6
+ - Fixed the second call to `IGMarkets::Session#sign_in` looping indefinitely if the previously logged in session was not
7
+ explicitly signed out
8
+ - Fixed automatic re-sign-in when the client security token is invalid not working correctly
9
+ - Fixed exception when executing a `DELETE` request and verbose output is enabled
10
+
3
11
  ### 0.17 — July 27, 2016
4
12
 
5
13
  - Switched to the `excon` HTTP library
@@ -0,0 +1,54 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets stream` command.
4
+ class Main < Thor
5
+ desc 'stream', 'Displays a live stream of prices for a set of markets'
6
+
7
+ option :epics, type: :array, desc: 'The EPICs of the markets to stream live prices for'
8
+
9
+ def stream
10
+ self.class.begin_session(options) do |dealing_platform|
11
+ @data_queue = Queue.new
12
+
13
+ lightstreamer = create_lightstreamer_session dealing_platform
14
+ create_lightstreamer_subscription lightstreamer
15
+
16
+ loop do
17
+ puts @data_queue.pop
18
+
19
+ raise lightstreamer.error if lightstreamer.error
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def create_lightstreamer_session(dealing_platform)
27
+ session = dealing_platform.session
28
+
29
+ server_url = dealing_platform.client_account_summary.lightstreamer_endpoint
30
+ username = dealing_platform.client_account_summary.client_id
31
+ password = "CST-#{session.client_security_token}|XST-#{session.x_security_token}"
32
+
33
+ lightstreamer = Lightstreamer::Session.new server_url: server_url, username: username, password: password
34
+
35
+ lightstreamer.connect
36
+ puts "Session ID: #{lightstreamer.session_id}"
37
+
38
+ lightstreamer
39
+ end
40
+
41
+ def create_lightstreamer_subscription(lightstreamer)
42
+ items = Array(options[:epics]).map { |market| "MARKET:#{market}" }
43
+
44
+ subscription = lightstreamer.build_subscription items: items, fields: [:bid, :offer], mode: :merge
45
+
46
+ subscription.on_data do |_subscription, item_name, item_data, _new_values|
47
+ @data_queue.push "#{item_name[7..-1]} - #{item_data.map { |key, value| "#{key}: #{value}" }.join ', '}"
48
+ end
49
+
50
+ subscription.start snapshot: true
51
+ end
52
+ end
53
+ end
54
+ end
@@ -135,11 +135,12 @@ module IGMarkets
135
135
  # Prints the passed error to `stderr` and then exits the application.
136
136
  def warn_and_exit(error)
137
137
  class_name = error.class.name.split('::').last
138
+ class_name = nil if class_name == 'IGMarketsError'
138
139
 
139
140
  message = error.message.to_s
140
- message = nil if message.empty? || message == error.class.to_s
141
+ message = nil if ['', error.class.to_s].include? message
141
142
 
142
- warn ["IG Markets: #{class_name}", message].compact.join(', ')
143
+ warn "ig_markets: #{[class_name, message].compact.join ', '}"
143
144
 
144
145
  exit 1
145
146
  end
@@ -8,6 +8,7 @@ module IGMarkets
8
8
  end
9
9
 
10
10
  attribute :affected_deals, AffectedDeal
11
+ attribute :date, Time, format: '%FT%T.%L'
11
12
  attribute :deal_id
12
13
  attribute :deal_reference
13
14
  attribute :deal_status, Symbol, allowed_values: [:accepted, :fund_account, :rejected]
@@ -23,14 +24,14 @@ module IGMarkets
23
24
  attribute :reason, Symbol, allowed_values: [:account_not_enabled_to_trading, :attached_order_level_error,
24
25
  :attached_order_trailing_stop_error, :cannot_change_stop_type,
25
26
  :cannot_remove_stop, :closing_only_trades_accepted_on_this_market,
26
- :conflicting_order, :cr_spacing, :duplicate_order_error,
27
- :exchange_manual_override, :finance_repeat_dealing,
28
- :force_open_on_same_market_different_currency, :general_error,
29
- :good_till_date_in_the_past, :instrument_not_found, :insufficient_funds,
30
- :level_tolerance_error, :manual_order_timeout, :market_closed,
31
- :market_closed_with_edits, :market_closing, :market_not_borrowable,
32
- :market_offline, :market_phone_only, :market_rolled,
33
- :market_unavailable_to_client, :max_auto_size_exceeded,
27
+ :conflicting_order, :contact_support_instrument_error, :cr_spacing,
28
+ :duplicate_order_error, :exchange_manual_override,
29
+ :finance_repeat_dealing, :force_open_on_same_market_different_currency,
30
+ :general_error, :good_till_date_in_the_past, :instrument_not_found,
31
+ :insufficient_funds, :level_tolerance_error, :manual_order_timeout,
32
+ :market_closed, :market_closed_with_edits, :market_closing,
33
+ :market_not_borrowable, :market_offline, :market_phone_only,
34
+ :market_rolled, :market_unavailable_to_client, :max_auto_size_exceeded,
34
35
  :minimum_order_size_error, :move_away_only_limit, :move_away_only_stop,
35
36
  :move_away_only_trigger_level, :opposing_direction_orders_not_allowed,
36
37
  :opposing_positions_not_allowed, :order_locked, :order_not_found,
@@ -70,9 +70,9 @@ module IGMarkets
70
70
  def create(attributes)
71
71
  model = PositionCreateAttributes.new attributes
72
72
 
73
- payload = PayloadFormatter.format model, expiry: '-'
73
+ body = RequestBodyFormatter.format model, expiry: '-'
74
74
 
75
- @dealing_platform.session.post('positions/otc', payload, API_V2).fetch(:deal_reference)
75
+ @dealing_platform.session.post('positions/otc', body, API_V2).fetch(:deal_reference)
76
76
  end
77
77
 
78
78
  private
@@ -42,9 +42,9 @@ module IGMarkets
42
42
  # @return [String] The resulting deal reference, use {DealingPlatform#deal_confirmation} to check the result of
43
43
  # the sprint market position creation.
44
44
  def create(attributes)
45
- payload = PayloadFormatter.format SprintMarketPositionCreateAttributes.new attributes
45
+ body = RequestBodyFormatter.format SprintMarketPositionCreateAttributes.new(attributes)
46
46
 
47
- @dealing_platform.session.post('positions/sprintmarkets', payload).fetch :deal_reference
47
+ @dealing_platform.session.post('positions/sprintmarkets', body).fetch :deal_reference
48
48
  end
49
49
 
50
50
  # Internal model used by {#create}
@@ -64,9 +64,9 @@ module IGMarkets
64
64
  def create(attributes)
65
65
  model = WorkingOrderCreateAttributes.new attributes
66
66
 
67
- payload = PayloadFormatter.format model, expiry: '-'
67
+ body = RequestBodyFormatter.format model, expiry: '-'
68
68
 
69
- @dealing_platform.session.post('workingorders/otc', payload, API_V2).fetch :deal_reference
69
+ @dealing_platform.session.post('workingorders/otc', body, API_V2).fetch :deal_reference
70
70
  end
71
71
 
72
72
  # Internal model used by {#create}.
@@ -93,9 +93,9 @@ module IGMarkets
93
93
  model = PositionCloseAttributes.build options
94
94
  model.validate
95
95
 
96
- payload = PayloadFormatter.format model
96
+ body = RequestBodyFormatter.format model
97
97
 
98
- @dealing_platform.session.delete('positions/otc', payload).fetch :deal_reference
98
+ @dealing_platform.session.delete('positions/otc', body).fetch :deal_reference
99
99
  end
100
100
 
101
101
  # Updates this position. No attributes are mandatory, and any attributes not specified will be kept at their
@@ -119,9 +119,9 @@ module IGMarkets
119
119
  new_attributes[:trailing_stop_distance] = new_attributes[:trailing_stop_increment] = nil
120
120
  end
121
121
 
122
- payload = PayloadFormatter.format PositionUpdateAttributes.new new_attributes
122
+ body = RequestBodyFormatter.format PositionUpdateAttributes.new(new_attributes)
123
123
 
124
- @dealing_platform.session.put("positions/otc/#{deal_id}", payload, API_V2).fetch(:deal_reference)
124
+ @dealing_platform.session.put("positions/otc/#{deal_id}", body, API_V2).fetch(:deal_reference)
125
125
  end
126
126
 
127
127
  # Validates the internal consistency of the `:order_type`, `:quote_id` and `:level` attributes.
@@ -1,11 +1,11 @@
1
1
  module IGMarkets
2
- # Contains methods for formatting payloads that can be passed to the IG Markets API.
2
+ # Contains methods for formatting request bodies that can be passed to the IG Markets API.
3
3
  #
4
4
  # @private
5
- module PayloadFormatter
5
+ module RequestBodyFormatter
6
6
  module_function
7
7
 
8
- # Takes a {Model} and returns its attributes in a hash ready to be passed as a payload to the IG Markets API.
8
+ # Takes a {Model} and returns its attributes in a hash ready to be passed as a request body to the IG Markets API.
9
9
  # Attribute names will be converted from snake case to camel case, `Symbol` attributes will be converted to strings
10
10
  # and will be uppercased, and both `Date` and `Time` attributes will be converted to strings using their first
11
11
  # available `:format` option.
@@ -20,10 +20,10 @@ module IGMarkets
20
20
 
21
21
  puts ' Headers:'
22
22
  options[:headers].each do |name, value|
23
- print_request_header name, value
23
+ puts " #{name}: #{value}"
24
24
  end
25
25
 
26
- print_request_body options[:payload]
26
+ print_request_body options[:body]
27
27
  end
28
28
 
29
29
  # Formats and prints a JSON response body.
@@ -41,10 +41,6 @@ module IGMarkets
41
41
 
42
42
  private
43
43
 
44
- def print_request_header(name, value)
45
- puts " #{name.to_s.split('_').map { |h| h[0].upcase + h[1..-1] }.join('-')}: #{value}"
46
- end
47
-
48
44
  def print_request_body(body)
49
45
  return unless body
50
46
 
@@ -40,6 +40,8 @@ module IGMarkets
40
40
  #
41
41
  # @return [Hash] The data returned in the body of the sign in request.
42
42
  def sign_in
43
+ @client_security_token = @x_security_token = nil
44
+
43
45
  body = { identifier: username, password: password_encryptor.encrypt(password), encryptedPassword: true }
44
46
 
45
47
  sign_in_result = request method: :post, url: 'session', body: body, api_version: API_V2
@@ -139,7 +141,7 @@ module IGMarkets
139
141
 
140
142
  headers['Content-Type'] = headers['Accept'] = 'application/json; charset=UTF-8'
141
143
  headers['X-IG-API-KEY'] = api_key
142
- headers['Version'] = options.delete :api_version
144
+ headers['Version'] = options.fetch :api_version
143
145
 
144
146
  headers['CST'] = client_security_token if client_security_token
145
147
  headers['X-SECURITY-TOKEN'] = x_security_token if x_security_token
@@ -162,12 +164,12 @@ module IGMarkets
162
164
  def process_response(response, options)
163
165
  body = parse_body response
164
166
 
165
- if body.key? :error_code
167
+ if body.is_a?(Hash) && body.key?(:error_code)
166
168
  error = IGMarketsError.build body[:error_code]
167
169
 
168
170
  raise error unless should_retry_request? error, options
169
171
 
170
- execute_request options.merge(retry: true)
172
+ execute_request options.merge(retry: true, headers: request_headers(options))
171
173
  else
172
174
  { response: response, body: body }
173
175
  end
@@ -1,4 +1,4 @@
1
1
  module IGMarkets
2
2
  # The version of this gem.
3
- VERSION = '0.17'.freeze
3
+ VERSION = '0.18'.freeze
4
4
  end
@@ -56,9 +56,9 @@ module IGMarkets
56
56
  model = WorkingOrderUpdateAttributes.new existing_attributes, new_attributes
57
57
  model.validate
58
58
 
59
- payload = PayloadFormatter.format model
59
+ body = RequestBodyFormatter.format model
60
60
 
61
- @dealing_platform.session.put("workingorders/otc/#{deal_id}", payload, API_V2).fetch(:deal_reference)
61
+ @dealing_platform.session.put("workingorders/otc/#{deal_id}", body, API_V2).fetch(:deal_reference)
62
62
  end
63
63
 
64
64
  # Internal model used by {#update}.
data/lib/ig_markets.rb CHANGED
@@ -39,8 +39,8 @@ require 'ig_markets/market'
39
39
  require 'ig_markets/market_overview'
40
40
  require 'ig_markets/market_hierarchy_result'
41
41
  require 'ig_markets/password_encryptor'
42
- require 'ig_markets/payload_formatter'
43
42
  require 'ig_markets/position'
43
+ require 'ig_markets/request_body_formatter'
44
44
  require 'ig_markets/request_printer'
45
45
  require 'ig_markets/response_parser'
46
46
  require 'ig_markets/session'
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.17'
4
+ version: '0.18'
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-27 00:00:00.000000000 Z
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -236,6 +236,7 @@ files:
236
236
  - lib/ig_markets/cli/commands/self_test_command.rb
237
237
  - lib/ig_markets/cli/commands/sentiment_command.rb
238
238
  - lib/ig_markets/cli/commands/sprints_command.rb
239
+ - lib/ig_markets/cli/commands/stream_command.rb
239
240
  - lib/ig_markets/cli/commands/transactions_command.rb
240
241
  - lib/ig_markets/cli/commands/watchlists_command.rb
241
242
  - lib/ig_markets/cli/config_file.rb
@@ -273,9 +274,9 @@ files:
273
274
  - lib/ig_markets/model.rb
274
275
  - lib/ig_markets/model/typecasters.rb
275
276
  - lib/ig_markets/password_encryptor.rb
276
- - lib/ig_markets/payload_formatter.rb
277
277
  - lib/ig_markets/position.rb
278
278
  - lib/ig_markets/regex.rb
279
+ - lib/ig_markets/request_body_formatter.rb
279
280
  - lib/ig_markets/request_printer.rb
280
281
  - lib/ig_markets/response_parser.rb
281
282
  - lib/ig_markets/session.rb