ig_markets 0.1 → 0.2
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 +17 -0
- data/README.md +32 -16
- data/bin/ig_markets +29 -0
- data/lib/ig_markets.rb +15 -0
- data/lib/ig_markets/account_activity.rb +1 -1
- data/lib/ig_markets/account_transaction.rb +1 -1
- data/lib/ig_markets/application.rb +2 -2
- data/lib/ig_markets/cli/account_command.rb +40 -0
- data/lib/ig_markets/cli/activities_command.rb +33 -0
- data/lib/ig_markets/cli/confirmation_command.rb +32 -0
- data/lib/ig_markets/cli/main.rb +39 -0
- data/lib/ig_markets/cli/orders_command.rb +28 -0
- data/lib/ig_markets/cli/positions_command.rb +20 -0
- data/lib/ig_markets/cli/search_command.rb +30 -0
- data/lib/ig_markets/cli/sentiment_command.rb +35 -0
- data/lib/ig_markets/cli/sprints_command.rb +28 -0
- data/lib/ig_markets/cli/transactions_command.rb +56 -0
- data/lib/ig_markets/cli/watchlists_command.rb +34 -0
- data/lib/ig_markets/deal_confirmation.rb +4 -4
- data/lib/ig_markets/dealing_platform/position_methods.rb +3 -3
- data/lib/ig_markets/dealing_platform/sprint_market_position_methods.rb +1 -1
- data/lib/ig_markets/dealing_platform/watchlist_methods.rb +1 -3
- data/lib/ig_markets/dealing_platform/working_order_methods.rb +10 -10
- data/lib/ig_markets/format.rb +36 -0
- data/lib/ig_markets/historical_price_result.rb +1 -1
- data/lib/ig_markets/instrument.rb +2 -2
- data/lib/ig_markets/market.rb +10 -10
- data/lib/ig_markets/model.rb +23 -60
- data/lib/ig_markets/model/typecasters.rb +78 -0
- data/lib/ig_markets/payload_formatter.rb +19 -6
- data/lib/ig_markets/position.rb +7 -21
- data/lib/ig_markets/request_failed_error.rb +1 -1
- data/lib/ig_markets/sprint_market_position.rb +18 -3
- data/lib/ig_markets/version.rb +1 -1
- data/lib/ig_markets/working_order.rb +11 -11
- metadata +39 -64
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.rubocop.yml +0 -2
- data/.travis.yml +0 -10
- data/.yardopts +0 -4
- data/Gemfile +0 -2
- data/ig_markets.gemspec +0 -28
- data/spec/factories/ig_markets/account.rb +0 -14
- data/spec/factories/ig_markets/account_activity.rb +0 -21
- data/spec/factories/ig_markets/account_balance.rb +0 -8
- data/spec/factories/ig_markets/account_transaction.rb +0 -15
- data/spec/factories/ig_markets/application.rb +0 -21
- data/spec/factories/ig_markets/client_sentiment.rb +0 -7
- data/spec/factories/ig_markets/deal_confirmation.rb +0 -20
- data/spec/factories/ig_markets/historical_price_result.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_data_allowance.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_price.rb +0 -7
- data/spec/factories/ig_markets/historical_price_result_snapshot.rb +0 -10
- data/spec/factories/ig_markets/instrument.rb +0 -32
- data/spec/factories/ig_markets/instrument_currency.rb +0 -9
- data/spec/factories/ig_markets/instrument_expiry_details.rb +0 -6
- data/spec/factories/ig_markets/instrument_margin_deposit_band.rb +0 -8
- data/spec/factories/ig_markets/instrument_opening_hours.rb +0 -6
- data/spec/factories/ig_markets/instrument_rollover_details.rb +0 -6
- data/spec/factories/ig_markets/instrument_slippage_factor.rb +0 -6
- data/spec/factories/ig_markets/market.rb +0 -7
- data/spec/factories/ig_markets/market_dealing_rules.rb +0 -11
- data/spec/factories/ig_markets/market_dealing_rules_rule_details.rb +0 -6
- data/spec/factories/ig_markets/market_hierarchy_result.rb +0 -6
- data/spec/factories/ig_markets/market_hierarchy_result_hierarchy_node.rb +0 -6
- data/spec/factories/ig_markets/market_overview.rb +0 -22
- data/spec/factories/ig_markets/market_snapshot.rb +0 -17
- data/spec/factories/ig_markets/position.rb +0 -19
- data/spec/factories/ig_markets/sprint_market_position.rb +0 -16
- data/spec/factories/ig_markets/watchlist.rb +0 -9
- data/spec/factories/ig_markets/working_order.rb +0 -21
- data/spec/ig_markets/account_transaction_spec.rb +0 -30
- data/spec/ig_markets/dealing_platform/account_methods_spec.rb +0 -58
- data/spec/ig_markets/dealing_platform/client_sentiment_methods_spec.rb +0 -29
- data/spec/ig_markets/dealing_platform/market_methods_spec.rb +0 -80
- data/spec/ig_markets/dealing_platform/position_methods_spec.rb +0 -137
- data/spec/ig_markets/dealing_platform/sprint_market_position_methods_spec.rb +0 -39
- data/spec/ig_markets/dealing_platform/watchlist_methods_spec.rb +0 -89
- data/spec/ig_markets/dealing_platform/working_order_methods_spec.rb +0 -120
- data/spec/ig_markets/dealing_platform_spec.rb +0 -40
- data/spec/ig_markets/model_spec.rb +0 -127
- data/spec/ig_markets/password_encryptor_spec.rb +0 -23
- data/spec/ig_markets/payload_formatter_spec.rb +0 -19
- data/spec/ig_markets/position_spec.rb +0 -37
- data/spec/ig_markets/response_parser_spec.rb +0 -13
- data/spec/ig_markets/session_spec.rb +0 -134
- data/spec/spec_helper.rb +0 -14
- data/spec/support/factory_girl.rb +0 -7
- data/spec/support/random_test_order.rb +0 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module IGMarkets
|
|
2
|
+
module CLI
|
|
3
|
+
# Implements the `ig_markets sentiment` command.
|
|
4
|
+
class Main
|
|
5
|
+
desc 'sentiment', 'Prints sentiment for the specified market'
|
|
6
|
+
|
|
7
|
+
option :market, aliases: '-m', required: true, desc: 'The name of the market to print sentiment for'
|
|
8
|
+
option :related, aliases: '-r', type: :boolean, desc: 'Whether to print sentiment for related markets as well'
|
|
9
|
+
|
|
10
|
+
def sentiment
|
|
11
|
+
begin_session do
|
|
12
|
+
result = dealing_platform.client_sentiment[options[:market]]
|
|
13
|
+
|
|
14
|
+
print_sentiment result
|
|
15
|
+
|
|
16
|
+
if options[:related]
|
|
17
|
+
result.related_sentiments.each do |model|
|
|
18
|
+
print_sentiment model
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def print_sentiment(model)
|
|
27
|
+
print <<-END
|
|
28
|
+
#{model.market_id}: \
|
|
29
|
+
longs: #{model.long_position_percentage}%, \
|
|
30
|
+
shorts: #{model.short_position_percentage}%
|
|
31
|
+
END
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module IGMarkets
|
|
2
|
+
module CLI
|
|
3
|
+
# Implements the `ig_markets sprints` command.
|
|
4
|
+
class Main
|
|
5
|
+
desc 'sprints', 'Prints open sprint market positions'
|
|
6
|
+
|
|
7
|
+
def sprints
|
|
8
|
+
begin_session do
|
|
9
|
+
dealing_platform.sprint_market_positions.all.each do |sprint|
|
|
10
|
+
print_sprint_market_position sprint
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def print_sprint_market_position(sprint)
|
|
18
|
+
print <<-END
|
|
19
|
+
#{sprint.deal_id}: \
|
|
20
|
+
#{Format.currency sprint.size, sprint.currency} on #{sprint.epic} \
|
|
21
|
+
to be #{{ buy: 'above', sell: 'below' }.fetch(sprint.direction)} #{sprint.strike_level} \
|
|
22
|
+
in #{Format.seconds sprint.seconds_till_expiry}, \
|
|
23
|
+
payout: #{Format.currency sprint.payout_amount, sprint.currency}
|
|
24
|
+
END
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module IGMarkets
|
|
2
|
+
module CLI
|
|
3
|
+
# Implements the `ig_markets transactions` command.
|
|
4
|
+
class Main
|
|
5
|
+
desc 'transactions', 'Prints recent transactions'
|
|
6
|
+
|
|
7
|
+
option :days, default: 3, type: :numeric, desc: 'The number of days to print recent transactions for'
|
|
8
|
+
|
|
9
|
+
def transactions
|
|
10
|
+
begin_session do
|
|
11
|
+
transactions = dealing_platform.account.recent_transactions(seconds).sort_by(&:date)
|
|
12
|
+
|
|
13
|
+
transactions.each do |transaction|
|
|
14
|
+
print_transaction transaction
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
print_transaction_totals transactions
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def transaction_totals(transactions)
|
|
24
|
+
transactions.each_with_object({}) do |transaction, hash|
|
|
25
|
+
profit_loss = transaction.profit_and_loss_amount
|
|
26
|
+
|
|
27
|
+
currency = (hash[transaction.currency] ||= Hash.new(0))
|
|
28
|
+
|
|
29
|
+
currency[:delta] += profit_loss
|
|
30
|
+
currency[:interest] += profit_loss if transaction.interest?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def print_transaction(transaction)
|
|
35
|
+
print <<-END
|
|
36
|
+
#{transaction.reference}: #{transaction.date.strftime '%Y-%m-%d'}, \
|
|
37
|
+
#{transaction.formatted_transaction_type}, \
|
|
38
|
+
#{"#{transaction.size} of " if transaction.size}\
|
|
39
|
+
#{transaction.instrument_name}, \
|
|
40
|
+
profit/loss: #{Format.currency transaction.profit_and_loss_amount, transaction.currency}
|
|
41
|
+
END
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def print_transaction_totals(transactions)
|
|
45
|
+
transaction_totals(transactions).each do |currency, value|
|
|
46
|
+
print <<-END
|
|
47
|
+
|
|
48
|
+
Totals for currency '#{currency}':
|
|
49
|
+
Interest: #{Format.currency value[:interest], currency}
|
|
50
|
+
Profit/loss: #{Format.currency value[:delta], currency}
|
|
51
|
+
END
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module IGMarkets
|
|
2
|
+
module CLI
|
|
3
|
+
# Implements the `ig_markets watchlists` command.
|
|
4
|
+
class Main
|
|
5
|
+
desc 'watchlists', 'Prints all watchlists and their markets'
|
|
6
|
+
|
|
7
|
+
def watchlists
|
|
8
|
+
begin_session do
|
|
9
|
+
dealing_platform.watchlists.all.each do |watchlist|
|
|
10
|
+
print_watchlist watchlist
|
|
11
|
+
|
|
12
|
+
watchlist.markets.each do |market|
|
|
13
|
+
print ' - '
|
|
14
|
+
print_market_overview market
|
|
15
|
+
end
|
|
16
|
+
print "\n"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def print_watchlist(watchlist)
|
|
24
|
+
print <<-END
|
|
25
|
+
#{watchlist.id}: \
|
|
26
|
+
#{watchlist.name}, \
|
|
27
|
+
editable: #{watchlist.editable}, \
|
|
28
|
+
deleteable: #{watchlist.deleteable}, \
|
|
29
|
+
default: #{watchlist.default_system_watchlist}
|
|
30
|
+
END
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -13,10 +13,10 @@ module IGMarkets
|
|
|
13
13
|
attribute :deal_status, Symbol, allowed_values: [:accepted, :fund_account, :rejected]
|
|
14
14
|
attribute :direction, Symbol, allowed_values: [:buy, :sell]
|
|
15
15
|
attribute :epic
|
|
16
|
-
attribute :expiry,
|
|
16
|
+
attribute :expiry, Date, nil_if: '-', format: '%d-%b-%y'
|
|
17
17
|
attribute :guaranteed_stop, Boolean
|
|
18
18
|
attribute :level, Float
|
|
19
|
-
attribute :limit_distance,
|
|
19
|
+
attribute :limit_distance, Fixnum
|
|
20
20
|
attribute :limit_level, Float
|
|
21
21
|
attribute :reason, Symbol, allowed_values: [
|
|
22
22
|
:account_not_enabled_to_trading, :attached_order_level_error, :attached_order_trailing_stop_error,
|
|
@@ -32,9 +32,9 @@ module IGMarkets
|
|
|
32
32
|
:reject_spreadbet_order_on_cfd_account, :size_increment, :sprint_market_expiry_after_market_close,
|
|
33
33
|
:stop_or_limit_not_allowed, :stop_required_error, :strike_level_tolerance, :success, :trailing_stop_not_allowed,
|
|
34
34
|
:unknown, :wrong_side_of_market]
|
|
35
|
-
attribute :size,
|
|
35
|
+
attribute :size, Float
|
|
36
36
|
attribute :status, Symbol, allowed_values: [:amended, :closed, :deleted, :open, :partially_closed]
|
|
37
|
-
attribute :stop_distance,
|
|
37
|
+
attribute :stop_distance, Fixnum
|
|
38
38
|
attribute :stop_level, Float
|
|
39
39
|
attribute :trailing_stop, Boolean
|
|
40
40
|
end
|
|
@@ -36,7 +36,7 @@ module IGMarkets
|
|
|
36
36
|
# currencies (see {Instrument#currencies}). Required.
|
|
37
37
|
# @option attributes [:buy, :sell] :direction The position direction. Required.
|
|
38
38
|
# @option attributes [String] :epic The EPIC of the instrument to create a position for. Required.
|
|
39
|
-
# @option attributes [
|
|
39
|
+
# @option attributes [Time] :expiry The expiry date of the instrument, if it has one. Optional.
|
|
40
40
|
# @option attributes [Boolean] :force_open Whether a force open is required. Defaults to `false`.
|
|
41
41
|
# @option attributes [Boolean] :guaranteed_stop Whether a guaranteed stop is required. Defaults to `false`.
|
|
42
42
|
# @option attributes [Float] :level Required if and only if `:order_type` is `:limit` or `:quote`.
|
|
@@ -89,7 +89,7 @@ module IGMarkets
|
|
|
89
89
|
attribute :currency_code, String, regex: Regex::CURRENCY
|
|
90
90
|
attribute :direction, Symbol, allowed_values: [:buy, :sell]
|
|
91
91
|
attribute :epic, String, regex: Regex::EPIC
|
|
92
|
-
attribute :expiry,
|
|
92
|
+
attribute :expiry, Time, format: '%d-%b-%y'
|
|
93
93
|
attribute :force_open, Boolean
|
|
94
94
|
attribute :guaranteed_stop, Boolean
|
|
95
95
|
attribute :level, Float
|
|
@@ -97,7 +97,7 @@ module IGMarkets
|
|
|
97
97
|
attribute :limit_level, Float
|
|
98
98
|
attribute :order_type, Symbol, allowed_values: [:limit, :market, :quote]
|
|
99
99
|
attribute :quote_id
|
|
100
|
-
attribute :size,
|
|
100
|
+
attribute :size, Float
|
|
101
101
|
attribute :stop_distance, Fixnum
|
|
102
102
|
attribute :stop_level, Float
|
|
103
103
|
attribute :time_in_force, Symbol, allowed_values: [:execute_and_eliminate, :fill_or_kill]
|
|
@@ -39,7 +39,7 @@ module IGMarkets
|
|
|
39
39
|
attribute :epic, String, regex: Regex::EPIC
|
|
40
40
|
attribute :expiry_period, Symbol, allowed_values: [
|
|
41
41
|
:one_minute, :two_minutes, :five_minutes, :twenty_minutes, :sixty_minutes]
|
|
42
|
-
attribute :size,
|
|
42
|
+
attribute :size, Float
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -33,9 +33,7 @@ module IGMarkets
|
|
|
33
33
|
def create(name, *epics)
|
|
34
34
|
result = @dealing_platform.session.post 'watchlists', { name: name, epics: epics.flatten }, API_V1
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
watchlist.id == result.fetch(:watchlist_id)
|
|
38
|
-
end
|
|
36
|
+
self[result.fetch(:watchlist_id)]
|
|
39
37
|
end
|
|
40
38
|
end
|
|
41
39
|
end
|
|
@@ -40,15 +40,15 @@ module IGMarkets
|
|
|
40
40
|
# currencies (see {Instrument#currencies}). Required.
|
|
41
41
|
# @option attributes [:buy, :sell] :direction Order direction. Required.
|
|
42
42
|
# @option attributes [String] :epic The EPIC of the instrument for the order. Required.
|
|
43
|
-
# @option attributes [
|
|
43
|
+
# @option attributes [Time] :expiry The expiry date of the instrument (if applicable). Optional.
|
|
44
44
|
# @option attributes [Boolean] :force_open Whether a force open is required. Defaults to `false`.
|
|
45
|
-
# @option attributes [
|
|
45
|
+
# @option attributes [Time] :good_till_date The date that the working order will live till. Must be set if
|
|
46
46
|
# `:time_in_force` is `:good_till_date`.
|
|
47
47
|
# @option attributes [Boolean] :guaranteed_stop Whether a guaranteed stop is required. Defaults to `false`.
|
|
48
48
|
# @option attributes [Float] :level The level at which the order will be triggered.
|
|
49
|
-
# @option attributes [
|
|
49
|
+
# @option attributes [Fixnum] :limit_distance The distance away in pips to place the limit. Optional.
|
|
50
50
|
# @option attributes [Float] :size The size of the working order. Required.
|
|
51
|
-
# @option attributes [
|
|
51
|
+
# @option attributes [Fixnum] :stop_distance The distance away in pips to place the stop. Optional.
|
|
52
52
|
# @option attributes [:good_till_cancelled, :good_till_date] :time_in_force The lifespan of the working order.
|
|
53
53
|
# `:good_till_cancelled` means the working order will live until it is explicitly cancelled.
|
|
54
54
|
# `:good_till_date` means the working order will live until the date specified by
|
|
@@ -85,7 +85,7 @@ module IGMarkets
|
|
|
85
85
|
raise ArgumentError, "#{attribute} attribute must be set" if attributes[attribute].nil?
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
if model.time_in_force == :good_till_date && !model.good_till_date.is_a?(
|
|
88
|
+
if model.time_in_force == :good_till_date && !model.good_till_date.is_a?(Time)
|
|
89
89
|
raise ArgumentError, 'good_till_date must be set when time_in_force is :good_till_date'
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -97,14 +97,14 @@ module IGMarkets
|
|
|
97
97
|
attribute :currency_code, String, regex: Regex::CURRENCY
|
|
98
98
|
attribute :direction, Symbol, allowed_values: [:buy, :sell]
|
|
99
99
|
attribute :epic, String, regex: Regex::EPIC
|
|
100
|
-
attribute :expiry,
|
|
100
|
+
attribute :expiry, Time, format: '%d-%b-%y'
|
|
101
101
|
attribute :force_open, Boolean
|
|
102
|
-
attribute :good_till_date,
|
|
102
|
+
attribute :good_till_date, Time, format: '%F %T'
|
|
103
103
|
attribute :guaranteed_stop, Boolean
|
|
104
104
|
attribute :level, Float
|
|
105
|
-
attribute :limit_distance,
|
|
106
|
-
attribute :size,
|
|
107
|
-
attribute :stop_distance,
|
|
105
|
+
attribute :limit_distance, Fixnum
|
|
106
|
+
attribute :size, Float
|
|
107
|
+
attribute :stop_distance, Fixnum
|
|
108
108
|
attribute :time_in_force, Symbol, allowed_values: [:good_till_cancelled, :good_till_date]
|
|
109
109
|
attribute :type, Symbol, allowed_values: [:limit, :stop]
|
|
110
110
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module IGMarkets
|
|
2
|
+
# This module contains shared methods for formatting different content types for display.
|
|
3
|
+
module Format
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
# Returns a formatted string for the specified currency amount and currency symbol. Two decimal places are used for
|
|
7
|
+
# all currencies except the Japanese Yen.
|
|
8
|
+
#
|
|
9
|
+
# @param [Float, Fixnum] amount The currency amount to format.
|
|
10
|
+
# @param [String] symbol The currency symbol.
|
|
11
|
+
#
|
|
12
|
+
# @return [String] The formatted currency amount, e.g. `"USD -130.40"`, `"AUD 539.10"`, `"JPY 3560"`.
|
|
13
|
+
def currency(amount, symbol)
|
|
14
|
+
if ['JPY', '¥'].include? symbol
|
|
15
|
+
"#{symbol} #{format '%i', amount.to_i}"
|
|
16
|
+
else
|
|
17
|
+
"#{symbol} #{format '%.2f', amount.to_f}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns a formatted string for the specified number of seconds in the format `[<hours>:]<minutes>:<seconds>`.
|
|
22
|
+
#
|
|
23
|
+
# @param [Fixnum] value The number of seconds to format.
|
|
24
|
+
#
|
|
25
|
+
# @return [String]
|
|
26
|
+
def seconds(value)
|
|
27
|
+
result = if value >= 60 * 60
|
|
28
|
+
"#{value / 60 / 60}:#{Kernel.format('%02i', (value / 60) % 60)}"
|
|
29
|
+
else
|
|
30
|
+
(value / 60).to_s
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
result + ':' + Kernel.format('%02i', value % 60)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -23,7 +23,7 @@ module IGMarkets
|
|
|
23
23
|
attribute :last_traded_volume, Float
|
|
24
24
|
attribute :low_price, Price
|
|
25
25
|
attribute :open_price, Price
|
|
26
|
-
attribute :snapshot_time,
|
|
26
|
+
attribute :snapshot_time, Time, format: '%Y/%m/%d %T', time_zone: '+1000'
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
attribute :allowance, DataAllowance
|
|
@@ -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,
|
|
15
|
+
attribute :last_dealing_date, Time, format: '%FT%R', time_zone: '+1000'
|
|
16
16
|
attribute :settlement_info
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -60,7 +60,7 @@ module IGMarkets
|
|
|
60
60
|
attribute :country
|
|
61
61
|
attribute :currencies, Currency
|
|
62
62
|
attribute :epic, String, regex: Regex::EPIC
|
|
63
|
-
attribute :expiry,
|
|
63
|
+
attribute :expiry, Date, nil_if: '-', format: '%d-%b-%y'
|
|
64
64
|
attribute :expiry_details, ExpiryDetails
|
|
65
65
|
attribute :force_open_allowed, Boolean
|
|
66
66
|
attribute :lot_size, Float
|
data/lib/ig_markets/market.rb
CHANGED
|
@@ -62,17 +62,17 @@ module IGMarkets
|
|
|
62
62
|
#
|
|
63
63
|
# @param [:minute, :minute_2, :minute_3, :minute_5, :minute_10, :minute_15, :minute_30, :hour, :hour_2, :hour_3,
|
|
64
64
|
# :hour_4, :day, :week, :month] resolution The resolution of the historical prices to return.
|
|
65
|
-
# @param [
|
|
66
|
-
# @param [
|
|
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
67
|
#
|
|
68
68
|
# @return [HistoricalPriceResult]
|
|
69
|
-
def prices_in_date_range(resolution,
|
|
69
|
+
def prices_in_date_range(resolution, start_time, end_time)
|
|
70
70
|
validate_historical_price_resolution resolution
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
start_time = format_time start_time
|
|
73
|
+
end_time = format_time end_time
|
|
74
74
|
|
|
75
|
-
url = "prices/#{instrument.epic}/#{resolution.to_s.upcase}/#{
|
|
75
|
+
url = "prices/#{instrument.epic}/#{resolution.to_s.upcase}/#{start_time}/#{end_time}"
|
|
76
76
|
|
|
77
77
|
HistoricalPriceResult.from @dealing_platform.session.get(url, API_V2)
|
|
78
78
|
end
|
|
@@ -89,11 +89,11 @@ module IGMarkets
|
|
|
89
89
|
raise ArgumentError, 'resolution is invalid' unless resolutions.include? resolution
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
# Takes a `
|
|
92
|
+
# Takes a `Time` and formats it for the historical prices API URLs.
|
|
93
93
|
#
|
|
94
|
-
# @param [
|
|
95
|
-
def
|
|
96
|
-
|
|
94
|
+
# @param [Time] time The `Time` to format.
|
|
95
|
+
def format_time(time)
|
|
96
|
+
time.utc.strftime '%FT%T'
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
end
|
data/lib/ig_markets/model.rb
CHANGED
|
@@ -19,9 +19,8 @@ module IGMarkets
|
|
|
19
19
|
|
|
20
20
|
(attributes.keys - defined_attribute_names).map do |attribute|
|
|
21
21
|
value = attributes[attribute]
|
|
22
|
-
value = value.inspect unless value.is_a? DateTime
|
|
23
22
|
|
|
24
|
-
raise ArgumentError, "Unknown attribute: #{self.class.name}##{attribute}, value: #{value}"
|
|
23
|
+
raise ArgumentError, "Unknown attribute: #{self.class.name}##{attribute}, value: #{inspect_value value}"
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
|
|
@@ -39,14 +38,25 @@ module IGMarkets
|
|
|
39
38
|
# @return [String]
|
|
40
39
|
def inspect
|
|
41
40
|
formatted_attributes = self.class.defined_attribute_names.map do |attribute|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"#{attribute}: #{value.is_a?(DateTime) ? value : value.inspect}"
|
|
41
|
+
"#{attribute}: #{inspect_value send(attribute)}"
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
"#<#{self.class.name} #{formatted_attributes.join ', '}>"
|
|
48
45
|
end
|
|
49
46
|
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Returns the #inspect string for the given value.
|
|
50
|
+
def inspect_value(value)
|
|
51
|
+
if value.is_a? Time
|
|
52
|
+
value.utc.strftime '%F %T %z'
|
|
53
|
+
elsif value.is_a? Date
|
|
54
|
+
value.strftime '%F'
|
|
55
|
+
else
|
|
56
|
+
value.inspect
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
50
60
|
class << self
|
|
51
61
|
# @return [Hash] A hash containing details of all attributes that have been defined on this model.
|
|
52
62
|
attr_accessor :defined_attributes
|
|
@@ -70,15 +80,17 @@ module IGMarkets
|
|
|
70
80
|
# Defines setter and getter methods for a new attribute on this class.
|
|
71
81
|
#
|
|
72
82
|
# @param [Symbol] name The name of the new attribute.
|
|
73
|
-
# @param [Boolean, String,
|
|
83
|
+
# @param [Boolean, String, Date, Time, Fixnum, Float, Symbol, #from] type The attribute's type.
|
|
74
84
|
# @param [Hash] options The configuration options for the new attribute.
|
|
75
85
|
# @option options [Array] :allowed_values The set of values that this attribute is allowed to be set to. An
|
|
76
86
|
# attempt to set this attribute to a value not in this list will raise `ArgumentError`. Optional.
|
|
77
87
|
# @option options [Array] :nil_if Values that, when set on the attribute, should be converted to `nil`.
|
|
78
88
|
# @option options [Regexp] :regex When `type` is `String` only values matching this regex will be allowed.
|
|
79
89
|
# Optional.
|
|
80
|
-
# @option options [String] :format When `type` is `
|
|
81
|
-
# Fixnum instances assigned to this attribute.
|
|
90
|
+
# @option options [String] :format When `type` is `Date` or `Time` this specifies the format for parsing String
|
|
91
|
+
# and `Fixnum` instances assigned to this attribute.
|
|
92
|
+
# @option options [String] :time_zone When `type` is `Time` this specifies the time zone to append to
|
|
93
|
+
# `String` values assigned to this attribute prior to parsing them with `:format`. Optional.
|
|
82
94
|
#
|
|
83
95
|
# @macro [attach] attribute
|
|
84
96
|
# The $1 attribute.
|
|
@@ -87,7 +99,8 @@ module IGMarkets
|
|
|
87
99
|
define_attribute_reader name
|
|
88
100
|
define_attribute_writer name, type, options
|
|
89
101
|
|
|
90
|
-
|
|
102
|
+
self.defined_attributes ||= {}
|
|
103
|
+
self.defined_attributes[name] = options.merge type: type
|
|
91
104
|
end
|
|
92
105
|
|
|
93
106
|
# Creates a new Model instance from the specified source, which can take a variety of different forms.
|
|
@@ -120,7 +133,7 @@ module IGMarkets
|
|
|
120
133
|
define_method "#{name}=" do |value|
|
|
121
134
|
value = nil if Array(options.fetch(:nil_if, [])).include? value
|
|
122
135
|
|
|
123
|
-
value = typecaster.call
|
|
136
|
+
value = typecaster.call value, options
|
|
124
137
|
|
|
125
138
|
allowed_values = options[:allowed_values]
|
|
126
139
|
if !value.nil? && allowed_values
|
|
@@ -130,56 +143,6 @@ module IGMarkets
|
|
|
130
143
|
(@attributes ||= {})[name] = value
|
|
131
144
|
end
|
|
132
145
|
end
|
|
133
|
-
|
|
134
|
-
def typecaster_for(type)
|
|
135
|
-
if [Boolean, String, Fixnum, Float, Symbol, DateTime].include? type
|
|
136
|
-
method "typecaster_#{type.to_s.gsub(/\AIGMarkets::/, '').downcase}"
|
|
137
|
-
elsif type.respond_to? :from
|
|
138
|
-
-> (value, _options) { type.from value }
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def typecaster_boolean(value, _options)
|
|
143
|
-
return value if [nil, true, false].include? value
|
|
144
|
-
|
|
145
|
-
raise ArgumentError, "#{self}: invalid boolean value: #{value}"
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def typecaster_string(value, options)
|
|
149
|
-
return nil if value.nil?
|
|
150
|
-
|
|
151
|
-
if options.key? :regex
|
|
152
|
-
raise ArgumentError, "#{self}: invalid string value: #{value}" unless options[:regex].match value.to_s
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
value.to_s
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def typecaster_fixnum(value, _options)
|
|
159
|
-
value.nil? ? nil : value.to_s.to_i
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def typecaster_float(value, _options)
|
|
163
|
-
value.nil? ? nil : Float(value)
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def typecaster_symbol(value, _options)
|
|
167
|
-
value.nil? ? nil : value.to_s.downcase.to_sym
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def typecaster_datetime(value, options)
|
|
171
|
-
raise ArgumentError, "#{self}: invalid or missing date time format" unless options[:format].is_a? String
|
|
172
|
-
|
|
173
|
-
if value.is_a?(String) || value.is_a?(Fixnum)
|
|
174
|
-
begin
|
|
175
|
-
DateTime.strptime(value.to_s, options[:format])
|
|
176
|
-
rescue ArgumentError
|
|
177
|
-
raise ArgumentError, "#{self}: failed parsing date '#{value}' with format '#{options[:format]}'"
|
|
178
|
-
end
|
|
179
|
-
else
|
|
180
|
-
value
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
146
|
end
|
|
184
147
|
end
|
|
185
148
|
end
|