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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/ig_markets/cli/commands/stream_command.rb +54 -0
- data/lib/ig_markets/cli/main.rb +3 -2
- data/lib/ig_markets/deal_confirmation.rb +9 -8
- data/lib/ig_markets/dealing_platform/position_methods.rb +2 -2
- data/lib/ig_markets/dealing_platform/sprint_market_position_methods.rb +2 -2
- data/lib/ig_markets/dealing_platform/working_order_methods.rb +2 -2
- data/lib/ig_markets/position.rb +4 -4
- data/lib/ig_markets/{payload_formatter.rb → request_body_formatter.rb} +3 -3
- data/lib/ig_markets/request_printer.rb +2 -6
- data/lib/ig_markets/session.rb +5 -3
- data/lib/ig_markets/version.rb +1 -1
- data/lib/ig_markets/working_order.rb +2 -2
- data/lib/ig_markets.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b3fab7b62a9c929a8262f9ef104c200278e03b2
|
4
|
+
data.tar.gz: 1fe0c83b896f00e493a2e7a4257fb2f43a80188f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/ig_markets/cli/main.rb
CHANGED
@@ -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
|
141
|
+
message = nil if ['', error.class.to_s].include? message
|
141
142
|
|
142
|
-
warn
|
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, :
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
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
|
-
|
73
|
+
body = RequestBodyFormatter.format model, expiry: '-'
|
74
74
|
|
75
|
-
@dealing_platform.session.post('positions/otc',
|
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
|
-
|
45
|
+
body = RequestBodyFormatter.format SprintMarketPositionCreateAttributes.new(attributes)
|
46
46
|
|
47
|
-
@dealing_platform.session.post('positions/sprintmarkets',
|
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
|
-
|
67
|
+
body = RequestBodyFormatter.format model, expiry: '-'
|
68
68
|
|
69
|
-
@dealing_platform.session.post('workingorders/otc',
|
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}.
|
data/lib/ig_markets/position.rb
CHANGED
@@ -93,9 +93,9 @@ module IGMarkets
|
|
93
93
|
model = PositionCloseAttributes.build options
|
94
94
|
model.validate
|
95
95
|
|
96
|
-
|
96
|
+
body = RequestBodyFormatter.format model
|
97
97
|
|
98
|
-
@dealing_platform.session.delete('positions/otc',
|
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
|
-
|
122
|
+
body = RequestBodyFormatter.format PositionUpdateAttributes.new(new_attributes)
|
123
123
|
|
124
|
-
@dealing_platform.session.put("positions/otc/#{deal_id}",
|
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
|
2
|
+
# Contains methods for formatting request bodies that can be passed to the IG Markets API.
|
3
3
|
#
|
4
4
|
# @private
|
5
|
-
module
|
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
|
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
|
-
|
23
|
+
puts " #{name}: #{value}"
|
24
24
|
end
|
25
25
|
|
26
|
-
print_request_body options[:
|
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
|
|
data/lib/ig_markets/session.rb
CHANGED
@@ -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.
|
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?
|
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
|
data/lib/ig_markets/version.rb
CHANGED
@@ -56,9 +56,9 @@ module IGMarkets
|
|
56
56
|
model = WorkingOrderUpdateAttributes.new existing_attributes, new_attributes
|
57
57
|
model.validate
|
58
58
|
|
59
|
-
|
59
|
+
body = RequestBodyFormatter.format model
|
60
60
|
|
61
|
-
@dealing_platform.session.put("workingorders/otc/#{deal_id}",
|
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.
|
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-
|
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
|