ig_markets 0.12 → 0.13

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: 7e50f2a597b16e6d091f0eff038350ec67847f60
4
- data.tar.gz: 769b2056a2a1db136b05947fccbe42127ff54809
3
+ metadata.gz: d08c5bda8c3b5efc6a42dff92de064a76e42fa24
4
+ data.tar.gz: defcb9eaeb04b87f3772911e66b849a22ab091e2
5
5
  SHA512:
6
- metadata.gz: 091827f393098b7b5b19df4686832ce96dd630038aee32ab15af550bfbdae5c4236bb238993198c9caae96273d02b83d8f34dd8d287dc4456b068e974969a3f0
7
- data.tar.gz: 4ee120e6a912fdeef9ee98752c15559800ee3b406f168d1bb68524ee213b42a92afb7638dcf1675747bab6e6ed634f2a7030a41cb009b2235ff9ef5f090d17ad
6
+ metadata.gz: 80efc7ada830d2c5758c0c58dd5f2e666c0c4f315bd196e1de8c25423b8a901be0ac4cac40ddc6f189ca8bedf3d48001caa925b149fa7afb62340f3e32e41a6b
7
+ data.tar.gz: 4eace6c040b07b374f878c9a621a8e090781758b345da829a841f0b8868c406b3f15b6e27f8166627fae33efebed7a4282e4b0367afce45492477c2e965fd861
data/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # IG Markets Changelog
2
2
 
3
- ### 0.12 - June 21, 2016
3
+ ### 0.13 Unreleased
4
+
5
+ - Added `ig_markets performance` command that summarizes an account's dealing performance over a specified period
6
+ - Added `ig_markets markets` command which prints the current state of all the passed EPICs
7
+ - Correctly handle instrument periods that are formatted as `DD-MMM-YY`
8
+ - The `:type` option to `IGMarkets::AccountMethods::transactions` is now validated
9
+ - The platform type previously referred to as `production` is now referred to as `live` to better match standard IG
10
+ terminology, existing calls to `IGMarkets::DealingPlatform#sign_in` and `IGMarkets::Session#sign_in` will need to be
11
+ updated if they explicitly specified `:production`
12
+
13
+ ### 0.12 — June 21, 2016
4
14
 
5
15
  - Unrecognized attributes returned by the IG Markets API now trigger a warning rather than causing an error, this makes
6
16
  the library able to handle new return values being added to existing APIs
@@ -116,7 +126,7 @@
116
126
  - `IGMarkets::Model` now has separate `Date` and `Time` attribute types, and a new `:time_zone` option is used for
117
127
  `Time` attributes that have a known time zone. Previous uses of `DateTime` should be replaced with either `Date` or
118
128
  `Time`.
119
- - Changed `size` attribute to always be of type `Float` on all models.
129
+ - Changed `size` attribute to always be of type `Float` on all models
120
130
  - Changed `limit_distance` and `stop_distance` attributes to always be of type `Fixnum` on all models
121
131
  - Added `IGMarkets::Format` module
122
132
  - Added `#expired?` and `#seconds_till_expiry` to `IGMarkets::SprintMarketPosition`
data/README.md CHANGED
@@ -22,7 +22,7 @@ Includes support for:
22
22
  * Watchlists
23
23
  * Client sentiment
24
24
 
25
- An IG Markets production or demo trading account is needed in order to use this gem.
25
+ An IG Markets live or demo trading account is needed in order to use this gem.
26
26
 
27
27
  ## License
28
28
 
@@ -71,10 +71,12 @@ commands and their subcommands is:
71
71
  - `ig_markets confirmation DEAL-REFERENCE`
72
72
  - `ig_markets console`
73
73
  - `ig_markets help [COMMAND]`
74
+ - `ig_markets markets EPICS`
74
75
  - `ig_markets orders [list]`
75
76
  - `ig_markets orders create ...`
76
77
  - `ig_markets orders update DEAL-ID ...`
77
78
  - `ig_markets orders delete DEAL-ID`
79
+ - `ig_markets performance --days N [...]`
78
80
  - `ig_markets positions [list] [...]`
79
81
  - `ig_markets positions create ...`
80
82
  - `ig_markets positions update DEAL-ID ...`
@@ -104,6 +106,9 @@ ig_markets transactions --days 7 --instrument EUR/USD --no-interest
104
106
  # Search for EURUSD currency markets
105
107
  ig_markets search EURUSD --type currencies
106
108
 
109
+ # Print details for the EURUSD currency pair and the Dow Jones Industrial Average
110
+ ig_markets markets CS.D.EURUSD.CFD.IP IX.D.DOW.IFD.IP
111
+
107
112
  # Print current positions in aggregate
108
113
  ig_markets positions --aggregate
109
114
 
@@ -130,6 +135,9 @@ ig_markets orders create --direction buy --epic CS.D.EURUSD.CFD.IP --level 1.1 -
130
135
  # Print daily prices for EURUSD from the last two weeks
131
136
  ig_markets prices --epic CS.D.EURUSD.CFD.IP --resolution day --number 14
132
137
 
138
+ # Print account dealing performance from the last 90 days, broken down by the EPICs that were traded
139
+ ig_markets performance --days 90
140
+
133
141
  # Log in and open a Ruby console which can be used to query the IG API, printing all REST requests
134
142
  ig_markets console --verbose
135
143
  ```
@@ -157,7 +165,7 @@ ig.account.activities from: Date.today - 7
157
165
  ig.account.activities from: Date.today - 14, to: Date.today - 7
158
166
  ig.account.transactions from: Date.today - 7
159
167
  ig.account.transactions from: Date.today - 14, to: Date.today - 7
160
- ig.account.transactions from: Date.today - 14, to: Date.today - 7, type: :deal
168
+ ig.account.transactions from: Date.today - 14, to: Date.today - 7, type: :withdrawal
161
169
 
162
170
  # Dealing
163
171
  ig.deal_confirmation 'deal_reference'
@@ -187,7 +195,7 @@ ig.working_orders['deal_id'].delete
187
195
  # Markets
188
196
  ig.markets.hierarchy
189
197
  ig.markets.search 'EURUSD'
190
- ig.markets['CS.D.EURUSD.CFD.IP']
198
+ ig.markets.find 'CS.D.EURUSD.CFD.IP', 'IX.D.DOW.IFD.IP'
191
199
  ig.markets['CS.D.EURUSD.CFD.IP'].historical_prices resolution: :hour, number: 48
192
200
  ig.markets['CS.D.EURUSD.CFD.IP'].historical_prices resolution: :second, from: Time.now - 120,
193
201
  to: Time.now - 60
@@ -39,7 +39,7 @@ module IGMarkets
39
39
  attribute :description
40
40
  attribute :details, Details
41
41
  attribute :epic, String, regex: Regex::EPIC
42
- attribute :period, Time, nil_if: %w(- DFB), format: ['%FT%T', '%b-%y']
42
+ attribute :period, Time, nil_if: %w(- DFB), format: ['%FT%T', '%d-%b-%y', '%b-%y']
43
43
  attribute :status, Symbol, allowed_values: [:accepted, :rejected, :unknown]
44
44
  attribute :type, Symbol, allowed_values: [:edit_stop_and_limit, :position, :system, :working_order]
45
45
  end
@@ -0,0 +1,18 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets markets` command.
4
+ class Main < Thor
5
+ desc 'markets EPICS', 'Prints the state of the markets with the specified EPICs'
6
+
7
+ def markets(*epics)
8
+ self.class.begin_session(options) do |dealing_platform|
9
+ markets = dealing_platform.markets.find(*epics)
10
+
11
+ table = MarketsTable.new markets
12
+
13
+ puts table
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets performance` command.
4
+ class Main
5
+ desc 'performance', 'Prints a summary of trading performance over a period'
6
+
7
+ option :days, type: :numeric, required: true, desc: 'The number of days to print performance for'
8
+ option :from, desc: 'The start date to show performance from, format: yyyy-mm-dd'
9
+
10
+ def performance
11
+ self.class.begin_session(options) do |dealing_platform|
12
+ performances = gather_performances dealing_platform
13
+ lookup_instrument_names performances, dealing_platform
14
+
15
+ table = PerformancesTable.new performances
16
+
17
+ puts table
18
+
19
+ print_summary performances
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def gather_performances(dealing_platform)
26
+ performances = deal_transactions_by_epic(dealing_platform).map do |epic, transactions|
27
+ profit_loss = transactions.map(&:profit_and_loss_amount).inject(&:+)
28
+
29
+ { epic: epic, transactions: transactions, profit_loss: profit_loss }
30
+ end
31
+
32
+ performances.sort_by { |a| a[:profit_loss] }
33
+ end
34
+
35
+ def deal_transactions_by_epic(dealing_platform)
36
+ activities = dealing_platform.account.activities history_options
37
+
38
+ deal_transactions(dealing_platform).group_by do |transaction|
39
+ activities.detect do |activity|
40
+ Regexp.new("^Position(\/s| partially) closed:.*#{transaction.reference}").match activity.description
41
+ end.epic
42
+ end
43
+ end
44
+
45
+ def deal_transactions(dealing_platform)
46
+ dealing_platform.account.transactions(history_options).select do |transaction|
47
+ transaction.transaction_type == :deal
48
+ end
49
+ end
50
+
51
+ def lookup_instrument_names(performances, dealing_platform)
52
+ markets = dealing_platform.markets.find(*performances.map { |a| a[:epic] })
53
+
54
+ performances.each do |a|
55
+ a[:instrument_name] = markets.detect { |market| market.instrument.epic == a[:epic] }.instrument.name
56
+ end
57
+ end
58
+
59
+ def print_summary(performances)
60
+ profit_loss = performances.map { |h| h[:profit_loss] }.inject(:+)
61
+ currency = performances.first[:transactions].first.currency
62
+
63
+ puts <<-END
64
+
65
+ Note: this table only shows the profit/loss made from dealing, it does not include interest payments,
66
+ dividends, or other adjustments that may have occurred over this period.
67
+
68
+ Total: #{Format.currency(profit_loss, currency).colorize(profit_loss < 0 ? :red : :green)}
69
+ END
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,7 +6,7 @@ module IGMarkets
6
6
  class_option :username, required: true, desc: 'The username for the session'
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
- class_option :demo, type: :boolean, desc: 'Use the demo platform (default is production)'
9
+ class_option :demo, type: :boolean, desc: 'Use the demo platform (default is the live platform)'
10
10
  class_option :verbose, type: :boolean, desc: 'Whether to print the raw REST API requests and responses'
11
11
 
12
12
  desc 'orders [SUBCOMAND=list ...]', 'Command for working with orders'
@@ -59,7 +59,7 @@ module IGMarkets
59
59
  #
60
60
  # @param [Thor::CoreExt::HashWithIndifferentAccess] options The Thor options hash.
61
61
  def begin_session(options)
62
- platform = options[:demo] ? :demo : :production
62
+ platform = options[:demo] ? :demo : :live
63
63
 
64
64
  RequestPrinter.enabled = true if options[:verbose]
65
65
 
@@ -9,7 +9,7 @@ module IGMarkets
9
9
  end
10
10
 
11
11
  def headings
12
- ['Type', 'EPIC', 'Instrument', 'Status', 'Expiry', 'Bid', 'Offer', 'High', 'Low', 'Change (net)', 'Change (%)']
12
+ ['EPIC', 'Type', 'Instrument', 'Status', 'Expiry', 'Bid', 'Offer', 'High', 'Low', 'Change (net)', 'Change (%)']
13
13
  end
14
14
 
15
15
  def right_aligned_columns
@@ -17,8 +17,8 @@ module IGMarkets
17
17
  end
18
18
 
19
19
  def row(market_overview)
20
- [market_overview.instrument_type, market_overview.epic, market_overview.instrument_name,
21
- market_status(market_overview), market_overview.expiry, levels(market_overview)]
20
+ [market_overview.epic, market_overview.instrument_type, market_overview.instrument_name,
21
+ market_overview.market_status, market_overview.expiry, levels(market_overview)]
22
22
  end
23
23
 
24
24
  def cell_color(value, _model, _row_index, column_index)
@@ -36,12 +36,6 @@ module IGMarkets
36
36
  Format.level market_overview.send(attribute)
37
37
  end
38
38
  end
39
-
40
- def market_status(market_overview)
41
- { closed: 'Closed', edits_only: 'Edits only', offline: 'Offline', on_auction: 'On auction',
42
- on_auction_no_edits: 'On auction no edits', suspended: 'Suspended', tradeable: 'Tradeable' }
43
- .fetch market_overview.market_status
44
- end
45
39
  end
46
40
  end
47
41
  end
@@ -0,0 +1,13 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Helper class that prints out an array of {IGMarkets::Market} instances in a table.
4
+ class MarketsTable < MarketOverviewsTable
5
+ private
6
+
7
+ def row(market)
8
+ [market.instrument.epic, market.instrument.type, market.instrument.name, market.snapshot.market_status,
9
+ market.instrument.expiry, levels(market.snapshot)]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Helper class that prints out account performance details in a table.
4
+ class PerformancesTable < Table
5
+ private
6
+
7
+ def default_title
8
+ 'Dealing performance'
9
+ end
10
+
11
+ def headings
12
+ ['EPIC', 'Instrument name', '# of closed deals', 'Profit/loss']
13
+ end
14
+
15
+ def right_aligned_columns
16
+ [2, 3]
17
+ end
18
+
19
+ def row(model)
20
+ transactions = model.fetch :transactions
21
+
22
+ [model.fetch(:epic), model.fetch(:instrument_name), transactions.size,
23
+ Format.currency(model.fetch(:profit_loss), transactions.first.currency)]
24
+ end
25
+
26
+ def cell_color(value, _transaction, _row_index, column_index)
27
+ return unless headings[column_index] == 'Profit/loss'
28
+
29
+ if value =~ /-/
30
+ :red
31
+ else
32
+ :green
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -45,6 +45,10 @@ module IGMarkets
45
45
  def transactions(options)
46
46
  options[:type] ||= :all
47
47
 
48
+ unless [:all, :all_deal, :deposit, :withdrawal].include? options[:type]
49
+ raise ArgumentError, "Invalid transaction type: #{options[:type]}"
50
+ end
51
+
48
52
  history_request_complete url: 'history/transactions', url_parameters: history_url_parameters(options),
49
53
  api_version: API_V2, collection_name: :transactions, model_class: Transaction,
50
54
  date_attribute: :date_utc
@@ -50,12 +50,12 @@ module IGMarkets
50
50
  @working_orders = WorkingOrderMethods.new self
51
51
  end
52
52
 
53
- # Signs in to the IG Markets Dealing Platform, either the production platform or the demo platform.
53
+ # Signs in to the IG Markets Dealing Platform, either the live platform or the demo platform.
54
54
  #
55
55
  # @param [String] username The account username.
56
56
  # @param [String] password The account password.
57
57
  # @param [String] api_key The account API key.
58
- # @param [:production, :demo] platform The platform to use.
58
+ # @param [:live, :demo] platform The platform to use.
59
59
  #
60
60
  # @return [ClientAccountSummary] The client account summary returned by the sign in request.
61
61
  def sign_in(username, password, api_key, platform)
@@ -102,10 +102,10 @@ module IGMarkets
102
102
  # of each attribute as defined on the models. All model instances returned by this method will have their
103
103
  # `@dealing_platform` instance variable set.
104
104
  #
105
- # @param [Class] model_class The type of model to create from `source`.
105
+ # @param [Class] model_class The top-level model class to create from `source`.
106
106
  # @param [nil, Hash, Array, Model] source The source object to construct the model(s) from. If `nil` then `nil` is
107
107
  # returned. If an instance of `model_class` subclass then a deep copy of it is
108
- # returned. If a `Hash` then it will be interprted as the attributes for a new
108
+ # returned. If a `Hash` then it will be interpreted as the attributes for a new
109
109
  # instance of `model_class. If an `Array` then each entry will be passed through
110
110
  # this method individually.
111
111
  #
@@ -113,13 +113,11 @@ module IGMarkets
113
113
  def instantiate_models(model_class, source)
114
114
  return nil if source.nil?
115
115
 
116
- source = source.attributes if source.is_a? model_class
116
+ source = prepare_source model_class, source
117
117
 
118
118
  if source.is_a? Array
119
119
  source.map { |entry| instantiate_models model_class, entry }
120
120
  elsif source.is_a? Hash
121
- source = model_class.adjusted_api_attributes source if model_class.respond_to? :adjusted_api_attributes
122
-
123
121
  instantiate_model_from_attributes_hash model_class, source
124
122
  else
125
123
  raise ArgumentError, "#{model_class}: can't instantiate from a source of type #{source.class}"
@@ -128,6 +126,17 @@ module IGMarkets
128
126
 
129
127
  private
130
128
 
129
+ # This method is a helper for {#instantiate_models} that prepares a source object for instantiation.
130
+ def prepare_source(model_class, source)
131
+ source = source.attributes if source.is_a? model_class
132
+
133
+ if source.is_a?(Hash) && model_class.respond_to?(:adjusted_api_attributes)
134
+ source = model_class.adjusted_api_attributes source
135
+ end
136
+
137
+ source
138
+ end
139
+
131
140
  # This method is a companion to {#instantiate_models} and creates a single instance of `model_class` from the passed
132
141
  # attributes hash, setting the `@dealing_platform` instance variable on the new model instance.
133
142
  def instantiate_model_from_attributes_hash(model_class, attributes)
@@ -1,7 +1,7 @@
1
1
  module IGMarkets
2
2
  # Manages a session with the IG Markets REST API, including signing in, signing out, and the sending of requests.
3
3
  # In order to sign in, {#username}, {#password}, {#api_key} and {#platform} must be set. {#platform} must be
4
- # either `:demo` or `:production` depending on which platform is being targeted.
4
+ # either `:demo` or `:live` depending on which platform is being targeted.
5
5
  class Session
6
6
  # @return [String] The username to use to authenticate this session.
7
7
  attr_accessor :username
@@ -12,7 +12,7 @@ module IGMarkets
12
12
  # @return [String] The API key to use to authenticate this session.
13
13
  attr_accessor :api_key
14
14
 
15
- # @return [:demo, :production] The platform variant to log into for this session.
15
+ # @return [:demo, :live] The platform variant to log into for this session.
16
16
  attr_accessor :platform
17
17
 
18
18
  # @return [String] The client session security access token for the currently logged in session, or `nil` if there
@@ -103,7 +103,7 @@ module IGMarkets
103
103
 
104
104
  HOST_URLS = {
105
105
  demo: 'https://demo-api.ig.com/gateway/deal/',
106
- production: 'https://api.ig.com/gateway/deal/'
106
+ live: 'https://api.ig.com/gateway/deal/'
107
107
  }.freeze
108
108
 
109
109
  def validate_authentication
@@ -8,7 +8,7 @@ module IGMarkets
8
8
  attribute :date_utc, Time, format: '%FT%T'
9
9
  attribute :instrument_name
10
10
  attribute :open_level, String, nil_if: %w(- 0)
11
- attribute :period, Time, nil_if: %w(- DFB), format: ['%FT%T', '%b-%y']
11
+ attribute :period, Time, nil_if: %w(- DFB), format: ['%FT%T', '%d-%b-%y', '%b-%y']
12
12
  attribute :profit_and_loss
13
13
  attribute :reference
14
14
  attribute :size, String, nil_if: '-'
@@ -1,4 +1,4 @@
1
1
  module IGMarkets
2
2
  # The version of this gem.
3
- VERSION = '0.12'.freeze
3
+ VERSION = '0.13'.freeze
4
4
  end
data/lib/ig_markets.rb CHANGED
@@ -55,6 +55,8 @@ require 'ig_markets/cli/commands/account_command'
55
55
  require 'ig_markets/cli/commands/activities_command'
56
56
  require 'ig_markets/cli/commands/confirmation_command'
57
57
  require 'ig_markets/cli/commands/console_command'
58
+ require 'ig_markets/cli/commands/markets_command'
59
+ require 'ig_markets/cli/commands/performance_command'
58
60
  require 'ig_markets/cli/commands/prices_command'
59
61
  require 'ig_markets/cli/commands/search_command'
60
62
  require 'ig_markets/cli/commands/sentiment_command'
@@ -65,6 +67,8 @@ require 'ig_markets/cli/tables/activities_table'
65
67
  require 'ig_markets/cli/tables/client_sentiments_table'
66
68
  require 'ig_markets/cli/tables/historical_price_result_snapshots_table'
67
69
  require 'ig_markets/cli/tables/market_overviews_table'
70
+ require 'ig_markets/cli/tables/markets_table'
71
+ require 'ig_markets/cli/tables/performances_table'
68
72
  require 'ig_markets/cli/tables/positions_table'
69
73
  require 'ig_markets/cli/tables/sprint_market_positions_table'
70
74
  require 'ig_markets/cli/tables/transactions_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.12'
4
+ version: '0.13'
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-06-21 00:00:00.000000000 Z
11
+ date: 2016-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -213,7 +213,9 @@ files:
213
213
  - lib/ig_markets/cli/commands/activities_command.rb
214
214
  - lib/ig_markets/cli/commands/confirmation_command.rb
215
215
  - lib/ig_markets/cli/commands/console_command.rb
216
+ - lib/ig_markets/cli/commands/markets_command.rb
216
217
  - lib/ig_markets/cli/commands/orders_command.rb
218
+ - lib/ig_markets/cli/commands/performance_command.rb
217
219
  - lib/ig_markets/cli/commands/positions_command.rb
218
220
  - lib/ig_markets/cli/commands/prices_command.rb
219
221
  - lib/ig_markets/cli/commands/search_command.rb
@@ -228,6 +230,8 @@ files:
228
230
  - lib/ig_markets/cli/tables/client_sentiments_table.rb
229
231
  - lib/ig_markets/cli/tables/historical_price_result_snapshots_table.rb
230
232
  - lib/ig_markets/cli/tables/market_overviews_table.rb
233
+ - lib/ig_markets/cli/tables/markets_table.rb
234
+ - lib/ig_markets/cli/tables/performances_table.rb
231
235
  - lib/ig_markets/cli/tables/positions_table.rb
232
236
  - lib/ig_markets/cli/tables/sprint_market_positions_table.rb
233
237
  - lib/ig_markets/cli/tables/table.rb