ig_markets 0.6 → 0.7

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: 589e81bb5e11e735c8dacf82cac67d784d5d705f
4
- data.tar.gz: 2a84abcae5a0844574fdbc25cbeff2fd831624e3
3
+ metadata.gz: fe408c4f3b1493d7da7574265ceab99d931d8685
4
+ data.tar.gz: b21bbe2f50cb5a5bedbd92b718fea4afc02462b5
5
5
  SHA512:
6
- metadata.gz: f2706333bf110e2b169df6769d602bda5dae0d7e14cb98b92bb73785d81f169b35286855b72367f7182b5d41994514053768d95e1991028ee39c4a7733b3f4fb
7
- data.tar.gz: d7c015c2573702b1ac5c1be4ec83f9c5d7e4212ded0be95d80fbe15f6542d580fed02e7fa1e5cfa5fb921227e4cd6ab74b3aaa47da6a3a093776f1949b8f0d6e
6
+ metadata.gz: 8ff8f42c43576533bd8c44de769f5bb43f725857294c9edce1ab8a45716704c2551eeddb0eb8307345cbfa292a0b1aacb53e45129a3b7ce4c317534ab81dd14c
7
+ data.tar.gz: d7a8011e786e30d2cd5a75de50edf5b129ab73ddc4d037fb6f22e18ec00a783adbd7276035326752f7a42fe2f7dad93a78dd0f77c11dcd5b6f772bd146d6ece2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # IG Markets Changelog
2
2
 
3
+ ### 0.7 — 1 May, 2016
4
+
5
+ - Added `ig_markets console` command which logs in then opens a live Ruby console
6
+ - Merged `IGMarkets::DealingPlatform::AccountMethods#activities_in_date_range` and
7
+ `IGMarkets::DealingPlatform::AccountMethods#recent_activities` into
8
+ `IGMarkets::DealingPlatform::AccountMethods#activities`
9
+ - Merged `IGMarkets::DealingPlatform::AccountMethods#transactions_in_date_range` and
10
+ `IGMarkets::DealingPlatform::AccountMethods#recent_transactions` into
11
+ `IGMarkets::DealingPlatform::AccountMethods#transactions`
12
+ - Merged `IGMarkets::Market#recent_prices` and `IGMarkets::Market#prices_in_date_range` into
13
+ `IGMarkets::Market#historical_prices`
14
+ - Removed the `--account-time-zone` option and `IGMarkets::DealingPlatform#account_time_zone`
15
+ - Improved error reporting on assignment of invalid values to model attributes
16
+ - `IGMarkets::Model#inspect` now reports `Time` attributes in the local time zone
17
+ - Model attributes that are deprecated in the IG Markets API are now no longer included as part of the model classes
18
+
3
19
  ### 0.6 — April 27, 2016
4
20
 
5
21
  - Added `ig_markets prices` command to print historical market prices
data/README.md CHANGED
@@ -69,6 +69,7 @@ commands and their subcommands is:
69
69
  - `ig_markets account`
70
70
  - `ig_markets activities --days N [--start-date YYYY-MM-DD]`
71
71
  - `ig_markets confirmation DEAL-REFERENCE`
72
+ - `ig_markets console`
72
73
  - `ig_markets help [COMMAND]`
73
74
  - `ig_markets orders [list]`
74
75
  - `ig_markets orders create ...`
@@ -125,24 +126,9 @@ ig_markets orders create --direction buy --epic CS.D.EURUSD.CFD.IP --level 1.1 -
125
126
 
126
127
  # Print daily prices for EURUSD from the last two weeks
127
128
  ig_markets prices --epic CS.D.EURUSD.CFD.IP --resolution day --number 14
128
- ```
129
-
130
- #### Account Time Zone
131
-
132
- Some timestamps returned by the IG Markets API are in an unspecified time zone, which can result in some times
133
- displayed by the command-line client being incorrect. By default the unknown time zone is assumed to be UTC, but if this
134
- is incorrect then use the `--account-time-zone` argument to specify the actual time zone these times are in.
135
-
136
- For example, an IG Markets Australia account requires `--account-time-zone +1000`.
137
-
138
- The `--account-time-zone` argument should be put into an `.ig_markets` config file to avoid specifying it on every
139
- invocation.
140
-
141
- To check that the account time zone is correct run the following command and verify that the date and time reported for
142
- the price is the current date/time in your local time zone.
143
129
 
144
- ```
145
- ig_markets prices --epic CS.D.EURUSD.CFD.IP --resolution minute --number 1
130
+ # Log in and then open a live Ruby console which can be used to query the IG API
131
+ ig_markets console
146
132
  ```
147
133
 
148
134
  ## Usage — Library
@@ -162,16 +148,12 @@ ig = IGMarkets::DealingPlatform.new
162
148
  ig.sign_in 'username', 'password', 'api_key', :demo
163
149
  ig.sign_out
164
150
 
165
- # Set the time zone for the account, this is applied to time attributes returned from the IG Markets API that are not
166
- # in a known time zone such as UTC
167
- ig.account_time_zone = '+0000'
168
-
169
151
  # Account
170
152
  ig.account.all
171
- ig.account.recent_activities 365
172
- ig.account.recent_transactions 365
173
- ig.account.activities_in_date_range Date.today - 14, Date.today - 7
174
- ig.account.transactions_in_date_range Date.today - 14, Date.today - 7
153
+ ig.account.activities days: 365
154
+ ig.account.activities from: Date.today - 14, to: Date.today - 7
155
+ ig.account.transactions days: 365
156
+ ig.account.transactions from: Date.today - 14, to: Date.today - 7
175
157
 
176
158
  # Dealing
177
159
  ig.deal_confirmation 'deal_reference'
@@ -195,15 +177,15 @@ ig.working_orders.all
195
177
  ig.working_orders.create currency_code: 'USD', direction: :buy, epic: 'CS.D.EURUSD.CFD.IP', level: 0.99,
196
178
  size: 1, type: :limit
197
179
  ig.working_orders['deal_id']
198
- ig.working_orders['deal_id'].update level: 1.25, limit_distance: 50, stop_distance: 0.02
180
+ ig.working_orders['deal_id'].update level: 1.25, limit_distance: 50, stop_distance: 50
199
181
  ig.working_orders['deal_id'].delete
200
182
 
201
183
  # Markets
202
184
  ig.markets.hierarchy
203
185
  ig.markets.search 'EURUSD'
204
186
  ig.markets['CS.D.EURUSD.CFD.IP']
205
- ig.markets['CS.D.EURUSD.CFD.IP'].recent_prices :day, 10
206
- ig.markets['CS.D.EURUSD.CFD.IP'].prices_in_date_range :day, Date.today - 14, Date.today - 7
187
+ ig.markets['CS.D.EURUSD.CFD.IP'].historical_prices resolution: :hour, number: 48
188
+ ig.markets['CS.D.EURUSD.CFD.IP'].historical_prices resolution: :second, from: Time.now - 120, to: Time.now - 60
207
189
 
208
190
  # Watchlists
209
191
  ig.watchlists.all
data/lib/ig_markets.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'base64'
2
2
  require 'colorize'
3
3
  require 'date'
4
+ require 'pry'
4
5
  require 'rest-client'
5
6
  require 'terminal-table'
6
7
  require 'thor'
@@ -52,6 +53,7 @@ require 'ig_markets/cli/config_file'
52
53
  require 'ig_markets/cli/commands/account_command'
53
54
  require 'ig_markets/cli/commands/activities_command'
54
55
  require 'ig_markets/cli/commands/confirmation_command'
56
+ require 'ig_markets/cli/commands/console_command'
55
57
  require 'ig_markets/cli/commands/prices_command'
56
58
  require 'ig_markets/cli/commands/search_command'
57
59
  require 'ig_markets/cli/commands/sentiment_command'
@@ -1,14 +1,13 @@
1
1
  module IGMarkets
2
2
  # Contains details on a single activity that occurred on an IG Markets account. Returned by
3
- # {DealingPlatform::AccountMethods#activities_in_date_range} and
4
- # {DealingPlatform::AccountMethods#recent_activities}.
3
+ # {DealingPlatform::AccountMethods#activities}.
5
4
  class Activity < Model
6
5
  attribute :action_status, Symbol, allowed_values: [:accept, :reject, :manual, :not_set]
7
6
  attribute :activity
8
7
  attribute :activity_history_id
9
8
  attribute :channel
10
9
  attribute :currency
11
- attribute :date, Date, format: '%d/%m/%y'
10
+ attribute :date, Time, format: '%FT%T'
12
11
  attribute :deal_id
13
12
  attribute :epic, String, regex: Regex::EPIC
14
13
  attribute :level, Float
@@ -19,6 +18,5 @@ module IGMarkets
19
18
  attribute :size
20
19
  attribute :stop, String, nil_if: '-'
21
20
  attribute :stop_type, String, nil_if: '-', allowed_values: %w(G N T(50))
22
- attribute :time
23
21
  end
24
22
  end
@@ -19,20 +19,17 @@ module IGMarkets
19
19
 
20
20
  private
21
21
 
22
- def gather_account_history(type)
23
- days = options[:days]
24
- start_date = options[:start_date]
22
+ def gather_account_history(method_name)
23
+ history_options = if options[:start_date]
24
+ from = Date.strptime options[:start_date], '%F'
25
+ to = from + options[:days].to_i
25
26
 
26
- send_args = if start_date
27
- start_date = Date.strptime start_date, '%F'
28
- end_date = start_date + days.to_i
27
+ { from: from, to: to }
28
+ else
29
+ { days: options[:days] }
30
+ end
29
31
 
30
- ["#{type}_in_date_range", start_date, end_date]
31
- else
32
- ["recent_#{type}", days]
33
- end
34
-
35
- Main.dealing_platform.account.send(*send_args)
32
+ Main.dealing_platform.account.send method_name, history_options
36
33
  end
37
34
  end
38
35
  end
@@ -0,0 +1,16 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets console` command.
4
+ class Main < Thor
5
+ desc 'console', 'Logs in and opens a live Ruby console, the IGMarkets::DealingPlatform instance is named \'ig\''
6
+
7
+ def console
8
+ self.class.begin_session(options) do |dealing_platform|
9
+ ig = dealing_platform
10
+
11
+ pry binding
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -17,14 +17,15 @@ module IGMarkets
17
17
  def prices
18
18
  self.class.begin_session(options) do |dealing_platform|
19
19
  result = historical_price_result dealing_platform
20
+ allowance = result.metadata.allowance
20
21
 
21
22
  table = HistoricalPriceResultSnapshotsTable.new result.prices, title: "Prices for #{options[:epic]}"
22
23
 
23
24
  puts <<-END
24
25
  #{table}
25
26
 
26
- Allowance: #{result.allowance.total_allowance}
27
- Remaining: #{result.allowance.remaining_allowance}
27
+ Allowance: #{allowance.total_allowance}
28
+ Remaining: #{allowance.remaining_allowance}
28
29
  END
29
30
  end
30
31
  end
@@ -50,7 +51,7 @@ END
50
51
  end
51
52
 
52
53
  def historical_price_result_from_number(market)
53
- market.recent_prices resolution, options[:number]
54
+ market.historical_prices resolution: resolution, max: options[:number]
54
55
  end
55
56
 
56
57
  def historical_price_result_from_date_range(market)
@@ -59,7 +60,7 @@ END
59
60
  self.class.parse_date_time filtered, :start_date, Time, '%FT%R%z', 'yyyy-mm-ddThh:mm(+|-)zz:zz'
60
61
  self.class.parse_date_time filtered, :end_date, Time, '%FT%R%z', 'yyyy-mm-ddThh:mm(+|-)zz:zz'
61
62
 
62
- market.prices_in_date_range resolution, filtered[:start_date], filtered[:end_date]
63
+ market.historical_prices resolution: resolution, from: filtered[:start_date], to: filtered[:end_date]
63
64
  end
64
65
  end
65
66
  end
@@ -29,7 +29,7 @@ module IGMarkets
29
29
  def gather_transactions
30
30
  regex = Regexp.new options.fetch('instrument', '')
31
31
 
32
- gather_account_history(:transactions).sort_by(&:date).select do |transaction|
32
+ gather_account_history(:transactions).sort_by(&:date_utc).select do |transaction|
33
33
  regex.match(transaction.instrument_name) && (options[:interest] || !transaction.interest?)
34
34
  end
35
35
  end
@@ -2,12 +2,14 @@ module IGMarkets
2
2
  module CLI
3
3
  # Helper class for working with the config files supported by the command-line client.
4
4
  class ConfigFile
5
- # Initializes this config file with the contents of the specified config file.
6
- def initialize(config_file)
7
- @lines = File.readlines config_file
5
+ # Initializes this config file with the passed lines.
6
+ #
7
+ # @param [Array<String>] lines
8
+ def initialize(lines = [])
9
+ @lines = lines
8
10
  end
9
11
 
10
- # Returns the arguments in this config file as an array.
12
+ # Returns the arguments contained in this config file.
11
13
  #
12
14
  # @return [Array<String>]
13
15
  def arguments
@@ -17,20 +19,28 @@ module IGMarkets
17
19
  .split(' ')
18
20
  end
19
21
 
20
- # Returns the config file to use, or `nil` if there is no config file.
22
+ # Inserts the arguments from this config file into the passed arguments array.
23
+ #
24
+ # @param [Array<String>] argv The array of command-line arguments to alter.
21
25
  #
22
- # @return [CLI::ConfigFile]
23
- def self.find
24
- config_file_locations = [
25
- "#{Dir.pwd}/.ig_markets",
26
- "#{Dir.home}/.ig_markets"
27
- ]
26
+ # @return [void]
27
+ def prepend_arguments_to_argv(argv)
28
+ insert_index = argv.index do |argument|
29
+ argument[0] == '-'
30
+ end || -1
28
31
 
29
- config_file = config_file_locations.detect do |filename|
32
+ argv.insert insert_index, *arguments
33
+ end
34
+
35
+ # Takes a list of potential config files and returns a {ConfigFile} instance for the first one that exists.
36
+ #
37
+ # @return [ConfigFile]
38
+ def self.find(*config_files)
39
+ config_file = config_files.detect do |filename|
30
40
  File.exist? filename
31
41
  end
32
42
 
33
- config_file ? new(config_file) : nil
43
+ new(config_file ? File.readlines(config_file) : [])
34
44
  end
35
45
  end
36
46
  end
@@ -7,7 +7,6 @@ module IGMarkets
7
7
  class_option :password, required: true, desc: 'The password for the session'
8
8
  class_option :api_key, required: true, desc: 'The API key for the session'
9
9
  class_option :demo, type: :boolean, desc: 'Use the demo platform (default is production)'
10
- class_option :account_time_zone, default: '+0000', desc: 'The time zone of the account'
11
10
  class_option :print_requests, type: :boolean, desc: 'Whether to print the raw REST API requests and responses'
12
11
 
13
12
  desc 'orders [SUBCOMAND=list ...]', 'Command for working with orders'
@@ -34,8 +33,6 @@ module IGMarkets
34
33
 
35
34
  RequestPrinter.enabled = true if options[:print_requests]
36
35
 
37
- dealing_platform.account_time_zone = options[:account_time_zone]
38
-
39
36
  dealing_platform.sign_in options[:username], options[:password], options[:api_key], platform
40
37
 
41
38
  yield dealing_platform
@@ -125,7 +122,7 @@ END
125
122
  #
126
123
  # @return [void]
127
124
  def bootstrap(argv)
128
- prepend_config_file_arguments argv
125
+ config_file.prepend_arguments_to_argv argv
129
126
 
130
127
  if argv.index('--version') || argv.index('-v')
131
128
  puts VERSION
@@ -135,21 +132,11 @@ END
135
132
  start argv
136
133
  end
137
134
 
138
- # Searches for a config file and if found inserts its arguments into the passed arguments array.
139
- #
140
- # @param [Array<String>] argv The array of command-line arguments.
135
+ # Returns the config file to use for this invocation.
141
136
  #
142
- # @return [void]
143
- def prepend_config_file_arguments(argv)
144
- config_file = ConfigFile.find
145
-
146
- return unless config_file
147
-
148
- insert_index = argv.index do |argument|
149
- argument[0] == '-'
150
- end || -1
151
-
152
- argv.insert insert_index, *config_file.arguments
137
+ # @return [ConfigFile]
138
+ def config_file
139
+ ConfigFile.find "#{Dir.pwd}/.ig_markets", "#{Dir.home}/.ig_markets"
153
140
  end
154
141
  end
155
142
  end
@@ -9,15 +9,15 @@ module IGMarkets
9
9
  end
10
10
 
11
11
  def headings
12
- %w(Date Time Channel Type Status EPIC Market Size Level Limit Stop Result)
12
+ %w(Date Channel Type Status EPIC Market Size Level Limit Stop Result)
13
13
  end
14
14
 
15
15
  def right_aligned_columns
16
- [7, 8, 9, 10]
16
+ [6, 7, 8, 9]
17
17
  end
18
18
 
19
19
  def row(activity)
20
- [activity.date, activity.time, activity.channel, activity.activity, activity_status(activity), activity.epic,
20
+ [activity.date, activity.channel, activity.activity, activity_status(activity), activity.epic,
21
21
  activity.market_name, activity.size, activity.level, activity.limit, activity.stop, activity.result]
22
22
  end
23
23
 
@@ -13,7 +13,7 @@ module IGMarkets
13
13
  end
14
14
 
15
15
  def row(snapshot)
16
- [snapshot.snapshot_time, format_price(snapshot.open_price), format_price(snapshot.close_price),
16
+ [snapshot.snapshot_time_utc, format_price(snapshot.open_price), format_price(snapshot.close_price),
17
17
  format_price(snapshot.low_price), format_price(snapshot.high_price)]
18
18
  end
19
19
 
@@ -66,7 +66,7 @@ module IGMarkets
66
66
  return format_boolean(value) if value.is_a?(TrueClass) || value.is_a?(FalseClass)
67
67
  return format_float(value) if value.is_a? Float
68
68
  return format_time(value) if value.is_a? Time
69
- return format_date(value) if value.is_a? Date
69
+ return format_symbol(value) if value.is_a? Symbol
70
70
 
71
71
  format_string value
72
72
  end
@@ -83,16 +83,12 @@ module IGMarkets
83
83
  value.localtime.strftime '%F %T %Z'
84
84
  end
85
85
 
86
- def format_date(value)
87
- value.strftime '%F'
86
+ def format_symbol(value)
87
+ Format.symbol value
88
88
  end
89
89
 
90
90
  def format_string(value)
91
- value = if value.is_a? Symbol
92
- Format.symbol value
93
- else
94
- value.to_s
95
- end
91
+ value = value.to_s
96
92
 
97
93
  return '' if value.empty?
98
94
 
@@ -17,7 +17,7 @@ module IGMarkets
17
17
  end
18
18
 
19
19
  def row(transaction)
20
- [transaction.date, transaction.reference, formatted_type(transaction.transaction_type),
20
+ [transaction.date_utc, transaction.reference, formatted_type(transaction.transaction_type),
21
21
  transaction.instrument_name, transaction.size, Format.level(transaction.open_level),
22
22
  Format.level(transaction.close_level),
23
23
  Format.currency(transaction.profit_and_loss_amount, transaction.currency)]
@@ -38,11 +38,6 @@ module IGMarkets
38
38
  # @return [WorkingOrderMethods] Methods for working with working orders.
39
39
  attr_reader :working_orders
40
40
 
41
- # @return [String] The time zone of the account, e.g. `'+1000'` or `'-0800'`. This is required in order for certain
42
- # dates and times reported by this library to be correct, due to the fact that the IG Markets API does not reliably
43
- # report time zone details in all attributes. Defaults to `'+0000'`.
44
- attr_accessor :account_time_zone
45
-
46
41
  def initialize
47
42
  @session = Session.new
48
43
 
@@ -53,8 +48,6 @@ module IGMarkets
53
48
  @sprint_market_positions = SprintMarketPositionMethods.new self
54
49
  @watchlists = WatchlistMethods.new self
55
50
  @working_orders = WorkingOrderMethods.new self
56
-
57
- @account_time_zone = '+0000'
58
51
  end
59
52
 
60
53
  # Signs in to the IG Markets Dealing Platform, either the production platform or the demo platform.
@@ -18,91 +18,67 @@ module IGMarkets
18
18
  @dealing_platform.instantiate_models Account, result
19
19
  end
20
20
 
21
- # Returns all account activities that occurred in the specified date range.
21
+ # Returns activities for this account, either the most recent activities by specifying the `:days` option, or
22
+ # those from a date range by specifying the `:from` and `:to` options.
22
23
  #
23
- # @param [Date] from_date The start date of the desired date range.
24
- # @param [Date] to_date The end date of the desired date range.
24
+ # @param [Hash] options The options hash.
25
+ # @option options [Float] :days The number of recent days to return activities for. If this is specified then the
26
+ # `:from` and `:to` options must not be specified.
27
+ # @option options [Date] :from The start of the period to return activities for.
28
+ # @option options [Date] :to The end of the period to return activities for.
25
29
  #
26
30
  # @return [Array<Activity>]
27
- def activities_in_date_range(from_date, to_date)
28
- from_date = format_date from_date
29
- to_date = format_date to_date
31
+ def activities(options)
32
+ parse_history_options options
30
33
 
31
- result = @dealing_platform.session.get("history/activity/#{from_date}/#{to_date}").fetch :activities
34
+ result = history_request('history/activity', options)
32
35
 
33
- @dealing_platform.instantiate_models Activity, result
36
+ @dealing_platform.instantiate_models Activity, result.fetch(:activities)
34
37
  end
35
38
 
36
- # Returns all account activities that occurred in the most recent specified number of days.
39
+ # Returns transactions for this account, either the most recent transactions by specifying the `:days` option, or
40
+ # those from a date range by specifying the `:from` and `:to` options.
37
41
  #
38
- # @param [Fixnum, Float] days The number of days to return recent activities for.
42
+ # @param [Hash] options The options hash.
43
+ # @option options [:all, :all_deal, :deposit, :withdrawal] :type The type of transactions to return. Defaults to
44
+ # `:all`.
45
+ # @option options [Float] :days The number of recent days to return transactions for. If this is specified then
46
+ # the `:from` and `:to` options must not be specified.
47
+ # @option options [Date] :from The start of the period to return transactions for.
48
+ # @option options [Date] :to The end of the period to return transactions for.
39
49
  #
40
50
  # @return [Array<Activity>]
41
- def recent_activities(days)
42
- result = @dealing_platform.session.get("history/activity/#{milliseconds(days)}").fetch :activities
51
+ def transactions(options)
52
+ options[:type] ||= :all
43
53
 
44
- @dealing_platform.instantiate_models Activity, result
45
- end
46
-
47
- # Returns all transactions that occurred in the specified date range.
48
- #
49
- # @param [Date] from_date The start date of the desired date range.
50
- # @param [Date] to_date The end date of the desired date range.
51
- # @param [:all, :all_deal, :deposit, :withdrawal] transaction_type The type of transactions to return.
52
- #
53
- # @return [Array<Transaction>]
54
- def transactions_in_date_range(from_date, to_date, transaction_type = :all)
55
- validate_transaction_type transaction_type
56
-
57
- from_date = format_date from_date
58
- to_date = format_date to_date
59
-
60
- url = "history/transactions/#{transaction_type.to_s.upcase}/#{from_date}/#{to_date}"
61
- result = @dealing_platform.session.get(url).fetch :transactions
62
-
63
- @dealing_platform.instantiate_models Transaction, result
64
- end
54
+ parse_history_options options
65
55
 
66
- # Returns all transactions that occurred in the last specified number of days.
67
- #
68
- # @param [Fixnum, Float] days The number of days to return recent transactions for.
69
- # @param [:all, :all_deal, :deposit, :withdrawal] transaction_type The type of transactions to return.
70
- #
71
- # @return [Array<Transaction>]
72
- def recent_transactions(days, transaction_type = :all)
73
- validate_transaction_type transaction_type
56
+ result = history_request('history/transactions', options)
74
57
 
75
- url = "history/transactions/#{transaction_type.to_s.upcase}/#{milliseconds(days)}"
76
- result = @dealing_platform.session.get(url).fetch :transactions
77
-
78
- @dealing_platform.instantiate_models Transaction, result
58
+ @dealing_platform.instantiate_models Transaction, result.fetch(:transactions)
79
59
  end
80
60
 
81
61
  private
82
62
 
83
- # Validates whether the passed argument is a valid transaction type.
84
- #
85
- # @param [Symbol] type The candidate transaction type to validate.
86
- def validate_transaction_type(type)
87
- raise ArgumentError, 'transaction type is invalid' unless [:all, :all_deal, :deposit, :withdrawal].include? type
88
- end
89
-
90
- # Formats the passed `Date` as a string in the manner needed for building IG Markets URLs.
63
+ # Sends a GET request to the specified URL with the passed options and returns the response.
91
64
  #
92
- # @param [Date] date The date to format.
65
+ # @param [String] url The base URL.
66
+ # @param [Hash] options The options to put with the URL.
93
67
  #
94
- # @return [String]
95
- def format_date(date)
96
- date.strftime '%d-%m-%Y'
68
+ # @return [Hash]
69
+ def history_request(url, options)
70
+ url = "#{url}?#{options.map { |key, value| "#{key}=#{value.to_s.upcase}" }.join '&'}"
71
+
72
+ @dealing_platform.session.get url, API_V2
97
73
  end
98
74
 
99
- # Converts a number of days into a number of milliseconds.
100
- #
101
- # @param [Fixnum, Float] days The number of days.
75
+ # Parses and formats the history options shared by {#activities} and {#transactions}.
102
76
  #
103
- # @return [Fixnum]
104
- def milliseconds(days)
105
- (days.to_f * 24 * 60 * 60 * 1000).to_i
77
+ # @param [Hash] options
78
+ def parse_history_options(options)
79
+ options[:maxSpanSeconds] = (options.delete(:days).to_f * 24 * 60 * 60).to_i if options.key? :days
80
+ options[:from] = options[:from].strftime('%F') if options.key? :from
81
+ options[:to] = options[:to].strftime('%F') if options.key? :to
106
82
  end
107
83
  end
108
84
  end
@@ -13,7 +13,7 @@ module IGMarkets
13
13
  #
14
14
  # @return [Array<SprintMarketPosition>]
15
15
  def all
16
- result = @dealing_platform.session.get('positions/sprintmarkets').fetch :sprint_market_positions
16
+ result = @dealing_platform.session.get('positions/sprintmarkets', API_V2).fetch :sprint_market_positions
17
17
 
18
18
  @dealing_platform.instantiate_models SprintMarketPosition, result
19
19
  end
@@ -1,33 +1,48 @@
1
1
  module IGMarkets
2
- # Contains details on a historical price query result. Returned by {Market#recent_prices} and
3
- # {Market#prices_in_date_range}.
2
+ # Contains details on a historical price query result. Returned by {Market#historical_prices}.
4
3
  class HistoricalPriceResult < Model
5
- # Contains details on the remaining allowance for looking up historical prices. Used by {#allowance}.
6
- class DataAllowance < Model
7
- attribute :allowance_expiry, Fixnum
8
- attribute :remaining_allowance, Fixnum
9
- attribute :total_allowance, Fixnum
10
- end
4
+ # Contains metadata associated with an historical price lookup. Used by {#metadata}.
5
+ class Metadata < Model
6
+ # Contains details on the remaining allowance for looking up historical prices. Used by {#allowance}.
7
+ class Allowance < Model
8
+ attribute :allowance_expiry, Fixnum
9
+ attribute :remaining_allowance, Fixnum
10
+ attribute :total_allowance, Fixnum
11
+ end
12
+
13
+ # Contains details on paging status for a historical price lookup. Used by {#page_data}.
14
+ class PageData < Model
15
+ attribute :page_number, Fixnum
16
+ attribute :page_size, Fixnum
17
+ attribute :total_pages, Fixnum
18
+ end
11
19
 
12
- # Contains details on a single historical price. Used by {Snapshot}.
13
- class Price < Model
14
- attribute :ask, Float
15
- attribute :bid, Float
16
- attribute :last_traded, Float
20
+ attribute :allowance, Allowance
21
+ attribute :page_data, PageData
22
+ attribute :size, Fixnum
17
23
  end
18
24
 
19
25
  # Contains details on a single historical price snapshot. Used by {#prices}.
20
26
  class Snapshot < Model
27
+ # Contains details on a single historical price.
28
+ class Price < Model
29
+ attribute :ask, Float
30
+ attribute :bid, Float
31
+ attribute :last_traded, Float
32
+ end
33
+
21
34
  attribute :close_price, Price
22
35
  attribute :high_price, Price
23
36
  attribute :last_traded_volume, Float
24
37
  attribute :low_price, Price
25
38
  attribute :open_price, Price
26
- attribute :snapshot_time, Time, format: '%Y/%m/%d %T', time_zone: -> { @dealing_platform.account_time_zone }
39
+ attribute :snapshot_time_utc, Time, format: '%FT%T'
40
+
41
+ deprecated_attribute :snapshot_time
27
42
  end
28
43
 
29
- attribute :allowance, DataAllowance
30
44
  attribute :instrument_type, Symbol, allowed_values: Instrument.allowed_values(:type)
45
+ attribute :metadata, Metadata
31
46
  attribute :prices, Snapshot
32
47
  end
33
48
  end
@@ -12,7 +12,7 @@ module IGMarkets
12
12
 
13
13
  # Contains details on the expiry details of an instrument. Returned by {#expiry_details}.
14
14
  class ExpiryDetails < Model
15
- attribute :last_dealing_date, Time, format: '%FT%R', time_zone: -> { @dealing_platform.account_time_zone }
15
+ attribute :last_dealing_date, Time, format: '%FT%R'
16
16
  attribute :settlement_info
17
17
  end
18
18
 
@@ -43,57 +43,48 @@ module IGMarkets
43
43
  attribute :instrument, Instrument
44
44
  attribute :snapshot, Snapshot
45
45
 
46
- # Returns recent historical prices for this market at a specified resolution.
46
+ # Returns historical prices for this market at a given resolution, either the most recent prices by specifying the
47
+ # `:number` option, or those from a date range by specifying the `:from` and `:to` options.
47
48
  #
48
- # @param [:minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour, :hour_2, :hour_3,
49
- # :hour_4, :day, :week, :month] resolution The resolution of the historical prices to return.
50
- # @param [Fixnum] num_points The number of historical prices to return.
49
+ # @param [Hash] options The options hash.
50
+ # @option options [:minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour, :hour_2,
51
+ # :hour_3, :hour_4, :day, :week, :month] :resolution The resolution of the prices to return.
52
+ # Required.
53
+ # @option options [Fixnum] :number The number of historical prices to return. If this is specified then the `:from`
54
+ # and `:to` options must not be specified.
55
+ # @option options [Time] :from The start of the period to return prices for.
56
+ # @option options [Time] :to The end of the period to return prices for.
51
57
  #
52
58
  # @return [HistoricalPriceResult]
53
- def recent_prices(resolution, num_points)
54
- validate_historical_price_resolution resolution
59
+ def historical_prices(options)
60
+ validate_historical_prices_options options
55
61
 
56
- url = "prices/#{instrument.epic}/#{resolution.to_s.upcase}/#{num_points.to_i}"
62
+ options[:max] = options.delete(:number) if options.key? :number
63
+ options[:from] = options[:from].utc.strftime '%FT%T' if options.key? :from
64
+ options[:to] = options[:to].utc.strftime '%FT%T' if options.key? :to
57
65
 
58
- @dealing_platform.instantiate_models HistoricalPriceResult, @dealing_platform.session.get(url, API_V2)
66
+ @dealing_platform.instantiate_models HistoricalPriceResult, historical_prices_response(options)
59
67
  end
60
68
 
61
- # Returns historical prices for this market at a specified resolution over a specified time period.
62
- #
63
- # @param [:minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour, :hour_2, :hour_3,
64
- # :hour_4, :day, :week, :month] resolution The resolution of the historical prices to return.
65
- # @param [Time] start_time The start of the desired time period.
66
- # @param [Time] end_time The end of the desired time period.
67
- #
68
- # @return [HistoricalPriceResult]
69
- def prices_in_date_range(resolution, start_time, end_time)
70
- validate_historical_price_resolution resolution
69
+ private
71
70
 
72
- start_time = format_time start_time
73
- end_time = format_time end_time
71
+ # Validates the options passed to {#historical_prices}.
72
+ def validate_historical_prices_options(options)
73
+ resolutions = [:second, :minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour,
74
+ :hour_2, :hour_3, :hour_4, :day, :week, :month]
74
75
 
75
- url = "prices/#{instrument.epic}/#{resolution.to_s.upcase}/#{start_time}/#{end_time}"
76
+ raise ArgumentError, 'resolution is invalid' unless resolutions.include? options[:resolution]
76
77
 
77
- @dealing_platform.instantiate_models HistoricalPriceResult, @dealing_platform.session.get(url, API_V2)
78
+ if options.keys != [:resolution, :from, :to] && options.keys != [:resolution, :number]
79
+ raise ArgumentError, 'options must specify either :number or :from and :to'
80
+ end
78
81
  end
79
82
 
80
- private
83
+ # Returns the API response to a request for historical prices.
84
+ def historical_prices_response(options)
85
+ url = "prices/#{instrument.epic}?#{options.map { |key, value| "#{key}=#{value.to_s.upcase}" }.join '&'}"
81
86
 
82
- # Validates whether the passed argument is a valid historical price resolution.
83
- #
84
- # @param [Symbol] resolution The candidate historical price resolution to validate.
85
- def validate_historical_price_resolution(resolution)
86
- resolutions = [:minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour, :hour_2,
87
- :hour_3, :hour_4, :day, :week, :month]
88
-
89
- raise ArgumentError, 'resolution is invalid' unless resolutions.include? resolution
90
- end
91
-
92
- # Takes a `Time` and formats it for the historical prices API URLs.
93
- #
94
- # @param [Time] time The `Time` to format.
95
- def format_time(time)
96
- time.utc.strftime '%F %T'
87
+ @dealing_platform.session.get url, API_V3
97
88
  end
98
89
  end
99
90
  end
@@ -18,7 +18,8 @@ module IGMarkets
18
18
  attribute :percentage_change, Float
19
19
  attribute :scaling_factor, Float
20
20
  attribute :streaming_prices_available, Boolean
21
- attribute :update_time
22
21
  attribute :update_time_utc
22
+
23
+ deprecated_attribute :update_time
23
24
  end
24
25
  end
@@ -11,16 +11,14 @@ module IGMarkets
11
11
  #
12
12
  # @param [Hash] attributes The attribute values to set on this new model.
13
13
  def initialize(attributes = {})
14
- defined_attribute_names = self.class.defined_attribute_names
15
-
16
- defined_attribute_names.each do |name|
14
+ self.class.defined_attribute_names.each do |name|
17
15
  send "#{name}=", attributes[name]
18
16
  end
19
17
 
20
- (attributes.keys - defined_attribute_names).map do |attribute|
21
- value = attributes[attribute]
18
+ attributes.each do |name, value|
19
+ next if respond_to? "#{name}="
22
20
 
23
- raise ArgumentError, "unknown attribute: #{self.class.name}##{attribute}, value: #{inspect_value value}"
21
+ raise ArgumentError, "unknown attribute: #{self.class.name}##{name}, value: #{inspect_value value}"
24
22
  end
25
23
  end
26
24
 
@@ -58,7 +56,7 @@ module IGMarkets
58
56
  # Returns the {#inspect} string for the given value.
59
57
  def inspect_value(value)
60
58
  if value.is_a? Time
61
- value.utc.strftime '%F %T %Z'
59
+ value.localtime.strftime '%F %T %Z'
62
60
  elsif value.is_a? Date
63
61
  value.strftime '%F'
64
62
  else
@@ -107,10 +105,6 @@ module IGMarkets
107
105
  # Optional.
108
106
  # @option options [String] :format When `type` is `Date` or `Time` this specifies the format for parsing String
109
107
  # and `Fixnum` instances assigned to this attribute.
110
- # @option options [String, Proc] :time_zone When `type` is `Time` this specifies the time zone to append to
111
- # `String` values assigned to this attribute prior to parsing them with `:format`. Defaults to
112
- # `+0000` (UTC) unless `:format` is `%Q`. Can be a `Proc` that returns the time zone string to
113
- # use.
114
108
  #
115
109
  # @macro [attach] attribute
116
110
  # The $1 attribute.
@@ -123,6 +117,15 @@ module IGMarkets
123
117
  self.defined_attributes[name] = options.merge type: type
124
118
  end
125
119
 
120
+ # Defines a no-op setter method for each of the passed attribute names. This is used to silently allow deprecated
121
+ # attributes to be set on the model but not have them be otherwise part of the model's structure.
122
+ def deprecated_attribute(*names)
123
+ names.each do |name|
124
+ define_method "#{name}=" do |_value|
125
+ end
126
+ end
127
+ end
128
+
126
129
  private
127
130
 
128
131
  def define_attribute_reader(name)
@@ -135,7 +138,7 @@ module IGMarkets
135
138
  define_method "#{name}=" do |value|
136
139
  value = nil if Array(options.fetch(:nil_if, [])).include? value
137
140
 
138
- value = typecaster.call value, options, self, name
141
+ value = typecaster.call value, options, name
139
142
 
140
143
  allowed_values = options[:allowed_values]
141
144
  if !value.nil? && allowed_values
@@ -8,9 +8,9 @@ module IGMarkets
8
8
  if [Boolean, String, Fixnum, Float, Symbol, Date, Time].include? type
9
9
  method "typecaster_#{type.to_s.gsub(/\AIGMarkets::/, '').downcase}"
10
10
  elsif type
11
- lambda do |value, _options, model, name|
11
+ lambda do |value, _options, name|
12
12
  if Array(value).any? { |entry| !entry.is_a? type }
13
- raise ArgumentError, "incorrect type set on #{model.class}##{name}: #{value.inspect}"
13
+ raise ArgumentError, "incorrect type set on #{self}##{name}: #{value.inspect}"
14
14
  end
15
15
 
16
16
  value
@@ -18,75 +18,76 @@ module IGMarkets
18
18
  end
19
19
  end
20
20
 
21
- def typecaster_boolean(value, _options, _model, _name)
21
+ def typecaster_boolean(value, _options, _name)
22
22
  return value if [nil, true, false].include? value
23
23
 
24
- raise ArgumentError, "#{self}: invalid boolean value: #{value}"
24
+ raise ArgumentError, "#{self}##{name}: invalid boolean value: #{value}"
25
25
  end
26
26
 
27
- def typecaster_string(value, options, _model, _name)
27
+ def typecaster_string(value, options, _name)
28
28
  return nil if value.nil?
29
29
 
30
- if options.key? :regex
31
- raise ArgumentError, "#{self}: invalid string value: #{value}" unless options[:regex].match value.to_s
30
+ if options.key?(:regex) && !options[:regex].match(value.to_s)
31
+ raise ArgumentError, "#{self}##{name}: invalid string value: #{value}"
32
32
  end
33
33
 
34
34
  value.to_s
35
35
  end
36
36
 
37
- def typecaster_fixnum(value, _options, _model, _name)
37
+ def typecaster_fixnum(value, _options, _name)
38
38
  return nil if value.nil?
39
39
 
40
40
  value.to_s.to_i
41
41
  end
42
42
 
43
- def typecaster_float(value, _options, _model, _name)
43
+ def typecaster_float(value, _options, _name)
44
44
  return nil if value.nil? || value == ''
45
45
 
46
46
  Float(value)
47
47
  end
48
48
 
49
- def typecaster_symbol(value, _options, _model, _name)
49
+ def typecaster_symbol(value, _options, _name)
50
50
  return nil if value.nil?
51
51
 
52
52
  value.to_s.downcase.to_sym
53
53
  end
54
54
 
55
- def typecaster_date(value, options, _model, _name)
56
- raise ArgumentError, "#{self}: invalid or missing date format" unless options[:format].is_a? String
55
+ def typecaster_date(value, options, name)
56
+ raise ArgumentError, "#{self}##{name}: invalid or missing date format" unless options[:format].is_a? String
57
57
 
58
58
  if value.is_a? String
59
59
  begin
60
60
  Date.strptime value, options[:format]
61
61
  rescue ArgumentError
62
- raise ArgumentError, "#{self}: failed parsing date '#{value}' with format '#{options[:format]}'"
62
+ raise ArgumentError, "#{self}##{name}: failed parsing date: #{value}"
63
63
  end
64
64
  else
65
65
  value
66
66
  end
67
67
  end
68
68
 
69
- def typecaster_time(value, options, model, name)
70
- raise ArgumentError, "#{self}: invalid or missing time format" unless options[:format].is_a? String
69
+ def typecaster_time(value, options, name)
70
+ raise ArgumentError, "#{self}##{name}: invalid or missing time format" unless options[:format].is_a? String
71
71
 
72
72
  if value.is_a?(String) || value.is_a?(Fixnum)
73
- parse_time_from_string value.to_s, options, model, name
73
+ parse_time_from_string value.to_s, options, name
74
74
  else
75
75
  value
76
76
  end
77
77
  end
78
78
 
79
- def parse_time_from_string(value, options, model, name)
79
+ def parse_time_from_string(value, options, name)
80
80
  format = options[:format]
81
81
 
82
- time_zone = options[:time_zone]
83
- time_zone ||= '+0000' unless format == '%Q'
84
- time_zone = model.instance_exec(&time_zone) if time_zone.is_a? Proc
82
+ unless format == '%Q'
83
+ format += '%z'
84
+ value += '+0000'
85
+ end
85
86
 
86
87
  begin
87
- Time.strptime "#{value}#{time_zone}", "#{format}#{'%z' if time_zone}"
88
+ Time.strptime value, format
88
89
  rescue ArgumentError
89
- raise ArgumentError, "#{self}##{name}: failed parsing time '#{value}' with format '#{format}'"
90
+ raise ArgumentError, "#{self}##{name}: failed parsing time: #{value}"
90
91
  end
91
92
  end
92
93
  end
@@ -4,7 +4,6 @@ module IGMarkets
4
4
  class Position < Model
5
5
  attribute :contract_size, Float
6
6
  attribute :controlled_risk, Boolean
7
- attribute :created_date, Time, format: '%Y/%m/%d %T:%L'
8
7
  attribute :created_date_utc, Time, format: '%FT%T'
9
8
  attribute :currency, String, regex: Regex::CURRENCY
10
9
  attribute :deal_id
@@ -18,6 +17,8 @@ module IGMarkets
18
17
 
19
18
  attribute :market, MarketOverview
20
19
 
20
+ deprecated_attribute :created_date
21
+
21
22
  # Returns whether this position has a trailing stop.
22
23
  def trailing_stop?
23
24
  !trailing_step.nil? && !trailing_stop_distance.nil?
@@ -1,13 +1,13 @@
1
1
  module IGMarkets
2
2
  # Contains details on a sprint market position. Returned by {DealingPlatform::SprintMarketPositionMethods#all}.
3
3
  class SprintMarketPosition < Model
4
- attribute :created_date, Time, format: '%Y/%m/%d %T:%L', time_zone: -> { @dealing_platform.account_time_zone }
4
+ attribute :created_date, Time, format: '%FT%T'
5
5
  attribute :currency, String, regex: Regex::CURRENCY
6
6
  attribute :deal_id
7
7
  attribute :description
8
8
  attribute :direction, Symbol, allowed_values: [:buy, :sell]
9
9
  attribute :epic, String, regex: Regex::EPIC
10
- attribute :expiry_time, Time, format: '%Y/%m/%d %T:%L', time_zone: -> { @dealing_platform.account_time_zone }
10
+ attribute :expiry_time, Time, format: '%FT%T'
11
11
  attribute :instrument_name
12
12
  attribute :market_status, Symbol, allowed_values: Market::Snapshot.allowed_values(:market_status)
13
13
  attribute :payout_amount, Float
@@ -1,20 +1,21 @@
1
1
  module IGMarkets
2
2
  # Contains details on a single transaction that occurred on an IG Markets account. Returned by
3
- # {DealingPlatform::AccountMethods#transactions_in_date_range} and
4
- # {DealingPlatform::AccountMethods#recent_transactions}.
3
+ # {DealingPlatform::AccountMethods#transactions}.
5
4
  class Transaction < Model
6
5
  attribute :cash_transaction, Boolean
7
6
  attribute :close_level, String, nil_if: %w(- 0)
8
7
  attribute :currency
9
- attribute :date, Date, format: '%d/%m/%y'
8
+ attribute :date_utc, Time, format: '%FT%T'
10
9
  attribute :instrument_name
11
10
  attribute :open_level, String, nil_if: %w(- 0)
12
- attribute :period, Time, nil_if: '-', format: '%d/%m/%y %T', time_zone: -> { @dealing_platform.account_time_zone }
11
+ attribute :period, Time, nil_if: '-', format: '%FT%T'
13
12
  attribute :profit_and_loss
14
13
  attribute :reference
15
14
  attribute :size, String, nil_if: '-'
16
15
  attribute :transaction_type, Symbol, allowed_values: [:deal, :depo, :dividend, :exchange, :with]
17
16
 
17
+ deprecated_attribute :date
18
+
18
19
  # Returns whether or not this transaction was an interest payment. Interest payments can be either deposits or
19
20
  # withdrawals depending on the underlying instrument and currencies involved. Interest payments are identified by
20
21
  # the presence of the word `interest` in {#instrument_name}.
@@ -1,4 +1,4 @@
1
1
  module IGMarkets
2
2
  # The version of this gem.
3
- VERSION = '0.6'.freeze
3
+ VERSION = '0.7'.freeze
4
4
  end
@@ -2,7 +2,6 @@ module IGMarkets
2
2
  # Contains details on a working order. Returned by {DealingPlatform::WorkingOrderMethods#all} and
3
3
  # {DealingPlatform::WorkingOrderMethods#[]}.
4
4
  class WorkingOrder < Model
5
- attribute :created_date, Time, format: '%Y/%m/%d %T:%L'
6
5
  attribute :created_date_utc, Time, format: '%FT%T'
7
6
  attribute :currency_code, String, regex: Regex::CURRENCY
8
7
  attribute :deal_id
@@ -10,7 +9,6 @@ module IGMarkets
10
9
  attribute :dma, Boolean
11
10
  attribute :epic, String, regex: Regex::EPIC
12
11
  attribute :good_till_date, Time, format: '%Y/%m/%d %R'
13
- attribute :good_till_date_iso, Time, format: '%FT%R'
14
12
  attribute :guaranteed_stop, Boolean
15
13
  attribute :limit_distance, Fixnum
16
14
  attribute :order_level, Float
@@ -21,6 +19,8 @@ module IGMarkets
21
19
 
22
20
  attribute :market, MarketOverview
23
21
 
22
+ deprecated_attribute :created_date, :good_till_date_iso
23
+
24
24
  # Deletes this working order.
25
25
  #
26
26
  # @return [String] The deal reference of the deletion operation. Use {DealingPlatform#deal_confirmation} to check
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.6'
4
+ version: '0.7'
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-04-27 00:00:00.000000000 Z
11
+ date: 2016-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rest-client
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -198,6 +212,7 @@ files:
198
212
  - lib/ig_markets/cli/commands/account_command.rb
199
213
  - lib/ig_markets/cli/commands/activities_command.rb
200
214
  - lib/ig_markets/cli/commands/confirmation_command.rb
215
+ - lib/ig_markets/cli/commands/console_command.rb
201
216
  - lib/ig_markets/cli/commands/orders_command.rb
202
217
  - lib/ig_markets/cli/commands/positions_command.rb
203
218
  - lib/ig_markets/cli/commands/prices_command.rb