ig_markets 0.17 → 0.18
Sign up to get free protection for your applications and to get access to all the features.
- 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
|