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