ig_markets 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +28 -19
- data/bin/ig_markets +1 -29
- data/lib/ig_markets/cli/account_command.rb +3 -26
- data/lib/ig_markets/cli/activities_command.rb +13 -15
- data/lib/ig_markets/cli/config_file.rb +37 -0
- data/lib/ig_markets/cli/confirmation_command.rb +5 -21
- data/lib/ig_markets/cli/main.rb +79 -20
- data/lib/ig_markets/cli/orders_command.rb +81 -13
- data/lib/ig_markets/cli/output.rb +115 -0
- data/lib/ig_markets/cli/positions_command.rb +100 -9
- data/lib/ig_markets/cli/search_command.rb +5 -19
- data/lib/ig_markets/cli/sentiment_command.rb +8 -19
- data/lib/ig_markets/cli/sprints_command.rb +42 -14
- data/lib/ig_markets/cli/transactions_command.rb +17 -16
- data/lib/ig_markets/cli/watchlists_command.rb +55 -16
- data/lib/ig_markets/dealing_platform/account_methods.rb +17 -8
- data/lib/ig_markets/dealing_platform/position_methods.rb +17 -9
- data/lib/ig_markets/dealing_platform/working_order_methods.rb +7 -15
- data/lib/ig_markets/model/typecasters.rb +14 -5
- data/lib/ig_markets/model.rb +2 -1
- data/lib/ig_markets/password_encryptor.rb +10 -1
- data/lib/ig_markets/position.rb +2 -2
- data/lib/ig_markets/session.rb +14 -11
- data/lib/ig_markets/version.rb +1 -1
- data/lib/ig_markets/working_order.rb +6 -5
- data/lib/ig_markets.rb +6 -4
- metadata +11 -9
@@ -1,20 +1,111 @@
|
|
1
1
|
module IGMarkets
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets positions` command.
|
4
|
-
class
|
5
|
-
desc '
|
4
|
+
class Positions < Thor
|
5
|
+
desc 'list', 'Prints open positions'
|
6
6
|
|
7
|
-
def
|
8
|
-
begin_session do
|
7
|
+
def list
|
8
|
+
Main.begin_session(options) do |dealing_platform|
|
9
9
|
dealing_platform.positions.all.each do |position|
|
10
|
-
|
11
|
-
#{position.deal_id}: \
|
12
|
-
#{position.formatted_size} of #{position.market.epic} at #{position.level}, \
|
13
|
-
profit/loss: #{Format.currency position.profit_loss, position.currency}
|
14
|
-
END
|
10
|
+
Output.print_position position
|
15
11
|
end
|
16
12
|
end
|
17
13
|
end
|
14
|
+
|
15
|
+
default_task :list
|
16
|
+
|
17
|
+
desc 'create', 'Creates a new position'
|
18
|
+
|
19
|
+
option :currency_code, required: true, desc: 'The 3 character currency code, must be one of the instrument\'s ' \
|
20
|
+
'currencies'
|
21
|
+
option :direction, required: true, desc: 'The trade direction, must be \'buy\' or \'sell\''
|
22
|
+
option :epic, required: true, desc: 'The EPIC of the market to trade'
|
23
|
+
option :expiry, desc: 'The expiry date of the instrument (if applicable), format: yyyy-mm-dd'
|
24
|
+
option :force_open, type: :boolean, default: false, desc: 'Whether a force open is required'
|
25
|
+
option :guaranteed_stop, type: :boolean, default: false, desc: 'Whether a guaranteed stop is required'
|
26
|
+
option :level, type: :numeric, desc: 'Required if and only if --order-type is \'limit\' or \'quote\''
|
27
|
+
option :limit_distance, type: :numeric, desc: 'The distance away in pips to place the limit, if set then ' \
|
28
|
+
'--limit-level must not be used'
|
29
|
+
option :limit_level, type: :numeric, desc: 'The limit level, if set then --limit-distance must not be used'
|
30
|
+
option :order_type, default: 'market', desc: 'The order type, must be \'limit\', \'market\' or \'quote\''
|
31
|
+
option :quote_id, desc: 'The Lightstreamer quote ID, required when using --order-type=quote'
|
32
|
+
option :size, type: :numeric, required: true, desc: 'The size of the position'
|
33
|
+
option :stop_distance, type: :numeric, desc: 'The distance away in pips to place the stop, if set then ' \
|
34
|
+
'--stop-level must not be used'
|
35
|
+
option :stop_level, type: :numeric, desc: 'The stop level, if set then --stop-distance must not be used'
|
36
|
+
option :time_in_force, desc: 'The order fill strategy, either \'execute_and_eliminate\' or \'fill_or_kill\''
|
37
|
+
option :trailing_stop, type: :boolean, desc: 'Whether to use a trailing stop, defaults to false'
|
38
|
+
option :trailing_stop_increment, type: :numeric, desc: 'The increment step in pips for the trailing stop, ' \
|
39
|
+
'required when --trailing-stop is specified'
|
40
|
+
|
41
|
+
def create
|
42
|
+
Main.begin_session(options) do |dealing_platform|
|
43
|
+
deal_reference = dealing_platform.positions.create position_attributes
|
44
|
+
|
45
|
+
puts "Deal reference: #{deal_reference}"
|
46
|
+
|
47
|
+
Output.print_deal_confirmation dealing_platform.deal_confirmation(deal_reference)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'update <DEAL-ID>', 'Updates attributes of an existing position'
|
52
|
+
|
53
|
+
option :limit_level, type: :numeric, desc: 'The limit level'
|
54
|
+
option :stop_level, type: :numeric, desc: 'The stop level'
|
55
|
+
option :trailing_stop, type: :boolean, desc: 'Whether to use a trailing stop, defaults to false'
|
56
|
+
option :trailing_stop_distance, type: :numeric, desc: 'The distance away in pips to place the trailing stop'
|
57
|
+
option :trailing_stop_increment, type: :numeric, desc: 'The increment step in pips for the trailing stop'
|
58
|
+
|
59
|
+
def update(deal_id)
|
60
|
+
with_position(deal_id) do |position|
|
61
|
+
position.update position_attributes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'close <DEAL-ID>', 'Closes or partially closes a position'
|
66
|
+
|
67
|
+
option :level, type: :numeric, desc: 'Required if and only if --order-type is \'limit\' or \'quote\''
|
68
|
+
option :order_type, default: 'market', desc: 'The order type, must be \'limit\', \'market\' or \'quote\''
|
69
|
+
option :quote_id, desc: 'The Lightstreamer quote ID, required when using --order-type=quote'
|
70
|
+
option :size, type: :numeric, desc: 'The size of the position to close, if not specified then the entire ' \
|
71
|
+
'position will be closed'
|
72
|
+
option :time_in_force, desc: 'The order fill strategy, either \'execute_and_eliminate\' or \'fill_or_kill\''
|
73
|
+
|
74
|
+
def close(deal_id)
|
75
|
+
with_position(deal_id) do |position|
|
76
|
+
position.close position_attributes
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def position_attributes
|
83
|
+
attributes = options.each_with_object({}) do |(key, value), new_hash|
|
84
|
+
next unless [:currency_code, :direction, :epic, :expiry, :force_open, :guaranteed_stop, :level,
|
85
|
+
:limit_distance, :limit_level, :order_type, :quote_id, :size, :stop_distance, :stop_level,
|
86
|
+
:time_in_force, :trailing_stop, :trailing_stop_increment].include? key.to_sym
|
87
|
+
|
88
|
+
new_hash[key.to_sym] = value
|
89
|
+
end
|
90
|
+
|
91
|
+
Main.parse_date_time attributes, :expiry, Date, '%F', 'yyyy-mm-dd'
|
92
|
+
|
93
|
+
attributes
|
94
|
+
end
|
95
|
+
|
96
|
+
def with_position(deal_id)
|
97
|
+
Main.begin_session(options) do |dealing_platform|
|
98
|
+
position = dealing_platform.positions[deal_id]
|
99
|
+
|
100
|
+
raise 'no position with the specified ID' unless position
|
101
|
+
|
102
|
+
deal_reference = yield position
|
103
|
+
|
104
|
+
puts "Deal reference: #{deal_reference}"
|
105
|
+
|
106
|
+
Output.print_deal_confirmation dealing_platform.deal_confirmation(deal_reference)
|
107
|
+
end
|
108
|
+
end
|
18
109
|
end
|
19
110
|
end
|
20
111
|
end
|
@@ -2,29 +2,15 @@ module IGMarkets
|
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets search` command.
|
4
4
|
class Main < Thor
|
5
|
-
desc 'search', 'Searches markets based on
|
5
|
+
desc 'search <QUERY>', 'Searches markets based on the specified query string'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
dealing_platform.markets.search(options[:query]).each do |market|
|
12
|
-
print_market_overview market
|
7
|
+
def search(query)
|
8
|
+
self.class.begin_session(options) do |dealing_platform|
|
9
|
+
dealing_platform.markets.search(query).each do |market_overview|
|
10
|
+
Output.print_market_overview market_overview
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def print_market_overview(market)
|
20
|
-
print <<-END
|
21
|
-
#{market.epic}: \
|
22
|
-
#{market.instrument_name}, \
|
23
|
-
type: #{market.instrument_type}, \
|
24
|
-
bid: #{market.bid} \
|
25
|
-
offer: #{market.offer}
|
26
|
-
END
|
27
|
-
end
|
28
14
|
end
|
29
15
|
end
|
30
16
|
end
|
@@ -2,34 +2,23 @@ module IGMarkets
|
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets sentiment` command.
|
4
4
|
class Main
|
5
|
-
desc 'sentiment', 'Prints sentiment for the specified market'
|
5
|
+
desc 'sentiment <MARKET>', 'Prints sentiment for the specified market'
|
6
6
|
|
7
|
-
option :
|
8
|
-
option :related, aliases: '-r', type: :boolean, desc: 'Whether to print sentiment for related markets as well'
|
7
|
+
option :related, type: :boolean, desc: 'Whether to print sentiment for related markets as well'
|
9
8
|
|
10
|
-
def sentiment
|
11
|
-
begin_session do
|
12
|
-
|
9
|
+
def sentiment(market)
|
10
|
+
self.class.begin_session(options) do |dealing_platform|
|
11
|
+
client_sentiment = dealing_platform.client_sentiment[market]
|
13
12
|
|
14
|
-
|
13
|
+
Output.print_client_sentiment client_sentiment
|
15
14
|
|
16
15
|
if options[:related]
|
17
|
-
|
18
|
-
|
16
|
+
client_sentiment.related_sentiments.each do |model|
|
17
|
+
Output.print_client_sentiment model
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
22
21
|
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
22
|
end
|
34
23
|
end
|
35
24
|
end
|
@@ -1,27 +1,55 @@
|
|
1
1
|
module IGMarkets
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets sprints` command.
|
4
|
-
class
|
5
|
-
desc '
|
4
|
+
class Sprints < Thor
|
5
|
+
desc 'list', 'Prints open sprint market positions'
|
6
6
|
|
7
|
-
def
|
8
|
-
begin_session do
|
9
|
-
dealing_platform.sprint_market_positions.all.each do |
|
10
|
-
print_sprint_market_position
|
7
|
+
def list
|
8
|
+
Main.begin_session(options) do |dealing_platform|
|
9
|
+
dealing_platform.sprint_market_positions.all.each do |sprint_market_position|
|
10
|
+
Output.print_sprint_market_position sprint_market_position
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
default_task :list
|
16
|
+
|
17
|
+
desc 'create', 'Creates a new sprint market position'
|
18
|
+
|
19
|
+
option :direction, required: true, desc: 'The trade direction, must be \'buy\' or \'sell\')'
|
20
|
+
option :epic, required: true, desc: 'The EPIC of the market to trade'
|
21
|
+
option :expiry_period, required: true, desc: 'The expiry period in seconds, must be 1, 2, 5, 20 or 60'
|
22
|
+
option :size, required: true, desc: 'The position size'
|
23
|
+
|
24
|
+
def create
|
25
|
+
Main.begin_session(options) do |dealing_platform|
|
26
|
+
deal_reference = dealing_platform.sprint_market_positions.create new_sprint_market_position_attributes
|
27
|
+
|
28
|
+
puts "Deal reference: #{deal_reference}"
|
29
|
+
|
30
|
+
Output.print_deal_confirmation dealing_platform.deal_confirmation deal_reference
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
15
34
|
private
|
16
35
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
36
|
+
def new_sprint_market_position_attributes
|
37
|
+
{
|
38
|
+
direction: options[:direction],
|
39
|
+
epic: options[:epic],
|
40
|
+
expiry_period: expiry_period,
|
41
|
+
size: options[:size]
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def expiry_period
|
46
|
+
{
|
47
|
+
'1' => :one_minute,
|
48
|
+
'2' => :two_minutes,
|
49
|
+
'5' => :five_minutes,
|
50
|
+
'20' => :twenty_minutes,
|
51
|
+
'60' => :sixty_minutes
|
52
|
+
}.fetch options[:expiry_period]
|
25
53
|
end
|
26
54
|
end
|
27
55
|
end
|
@@ -2,16 +2,17 @@ module IGMarkets
|
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets transactions` command.
|
4
4
|
class Main
|
5
|
-
desc 'transactions', 'Prints
|
5
|
+
desc 'transactions', 'Prints account transactions'
|
6
6
|
|
7
|
-
option :days,
|
7
|
+
option :days, type: :numeric, required: true, desc: 'The number of days to print account transactions for'
|
8
|
+
option :start_date, desc: 'The start date to print account transactions from, format: yyyy-mm-dd'
|
8
9
|
|
9
10
|
def transactions
|
10
|
-
begin_session do
|
11
|
-
transactions = dealing_platform
|
11
|
+
self.class.begin_session(options) do |dealing_platform|
|
12
|
+
transactions = gather_transactions(dealing_platform, options[:days], options[:start_date]).sort_by(&:date)
|
12
13
|
|
13
14
|
transactions.each do |transaction|
|
14
|
-
print_transaction transaction
|
15
|
+
Output.print_transaction transaction
|
15
16
|
end
|
16
17
|
|
17
18
|
print_transaction_totals transactions
|
@@ -20,6 +21,16 @@ module IGMarkets
|
|
20
21
|
|
21
22
|
private
|
22
23
|
|
24
|
+
def gather_transactions(dealing_platform, days, start_date = nil)
|
25
|
+
if start_date
|
26
|
+
start_date = Date.strptime options[:start_date], '%F'
|
27
|
+
|
28
|
+
dealing_platform.account.transactions_in_date_range start_date, start_date + days.to_i
|
29
|
+
else
|
30
|
+
dealing_platform.account.recent_transactions days
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
23
34
|
def transaction_totals(transactions)
|
24
35
|
transactions.each_with_object({}) do |transaction, hash|
|
25
36
|
profit_loss = transaction.profit_and_loss_amount
|
@@ -31,19 +42,9 @@ module IGMarkets
|
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
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
45
|
def print_transaction_totals(transactions)
|
45
46
|
transaction_totals(transactions).each do |currency, value|
|
46
|
-
|
47
|
+
puts <<-END
|
47
48
|
|
48
49
|
Totals for currency '#{currency}':
|
49
50
|
Interest: #{Format.currency value[:interest], currency}
|
@@ -1,33 +1,72 @@
|
|
1
1
|
module IGMarkets
|
2
2
|
module CLI
|
3
3
|
# Implements the `ig_markets watchlists` command.
|
4
|
-
class
|
5
|
-
desc '
|
4
|
+
class Watchlists < Thor
|
5
|
+
desc 'list', 'Prints all watchlists and their markets'
|
6
6
|
|
7
|
-
def
|
8
|
-
begin_session do
|
7
|
+
def list
|
8
|
+
Main.begin_session(options) do |dealing_platform|
|
9
9
|
dealing_platform.watchlists.all.each do |watchlist|
|
10
|
-
print_watchlist watchlist
|
10
|
+
Output.print_watchlist watchlist
|
11
11
|
|
12
|
-
watchlist.markets.each do |
|
12
|
+
watchlist.markets.each do |market_overview|
|
13
13
|
print ' - '
|
14
|
-
print_market_overview
|
14
|
+
Output.print_market_overview market_overview
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
|
+
puts ''
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
default_task :list
|
23
|
+
|
24
|
+
desc 'create <NAME> [<EPIC> <EPIC> ...]', 'Creates a new watchlist with the specified name and EPICs'
|
25
|
+
|
26
|
+
def create(name, *epics)
|
27
|
+
Main.begin_session(options) do |dealing_platform|
|
28
|
+
new_watchlist = dealing_platform.watchlists.create(name, *epics)
|
29
|
+
|
30
|
+
puts "New watchlist ID: #{new_watchlist.id}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'add-markets <WATCHLIST-ID> <EPIC> [<EPIC> ...]', 'Adds the specified markets to a watchlist'
|
35
|
+
|
36
|
+
def add_markets(watchlist_id, *epics)
|
37
|
+
with_watchlist(watchlist_id) do |watchlist|
|
38
|
+
epics.each do |epic|
|
39
|
+
watchlist.add_market epic
|
17
40
|
end
|
18
41
|
end
|
19
42
|
end
|
20
43
|
|
44
|
+
desc 'remove-markets <WATCHLIST-ID> <EPIC> [<EPIC> ...]', 'Removes the specified markets from a watchlist'
|
45
|
+
|
46
|
+
def remove_markets(watchlist_id, *epics)
|
47
|
+
with_watchlist(watchlist_id) do |watchlist|
|
48
|
+
epics.each do |epic|
|
49
|
+
watchlist.remove_market epic
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'delete <WATCHLIST-ID>', 'Deletes the watchlist with the specified ID'
|
55
|
+
|
56
|
+
def delete(watchlist_id)
|
57
|
+
with_watchlist(watchlist_id, &:delete)
|
58
|
+
end
|
59
|
+
|
21
60
|
private
|
22
61
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
62
|
+
def with_watchlist(watchlist_id)
|
63
|
+
Main.begin_session(options) do |dealing_platform|
|
64
|
+
watchlist = dealing_platform.watchlists[watchlist_id]
|
65
|
+
|
66
|
+
raise 'no watchlist with the specified ID' unless watchlist
|
67
|
+
|
68
|
+
yield watchlist
|
69
|
+
end
|
31
70
|
end
|
32
71
|
end
|
33
72
|
end
|
@@ -29,13 +29,13 @@ module IGMarkets
|
|
29
29
|
@dealing_platform.gather "history/activity/#{from_date}/#{to_date}", :activities, AccountActivity
|
30
30
|
end
|
31
31
|
|
32
|
-
# Returns all account activities that occurred in the most recent specified number of
|
32
|
+
# Returns all account activities that occurred in the most recent specified number of days.
|
33
33
|
#
|
34
|
-
# @param [
|
34
|
+
# @param [Fixnum, Float] days The number of days to return recent activities for.
|
35
35
|
#
|
36
36
|
# @return [Array<AccountActivity>]
|
37
|
-
def recent_activities(
|
38
|
-
@dealing_platform.gather "history/activity/#{(
|
37
|
+
def recent_activities(days)
|
38
|
+
@dealing_platform.gather "history/activity/#{milliseconds(days)}", :activities, AccountActivity
|
39
39
|
end
|
40
40
|
|
41
41
|
# Returns all transactions that occurred in the specified date range.
|
@@ -56,16 +56,16 @@ module IGMarkets
|
|
56
56
|
@dealing_platform.gather url, :transactions, AccountTransaction
|
57
57
|
end
|
58
58
|
|
59
|
-
# Returns all transactions that occurred in the last specified number of
|
59
|
+
# Returns all transactions that occurred in the last specified number of days.
|
60
60
|
#
|
61
|
-
# @param [
|
61
|
+
# @param [Fixnum, Float] days The number of days to return recent transactions for.
|
62
62
|
# @param [:all, :all_deal, :deposit, :withdrawal] transaction_type The type of transactions to return.
|
63
63
|
#
|
64
64
|
# @return [Array<AccountTransaction>]
|
65
|
-
def recent_transactions(
|
65
|
+
def recent_transactions(days, transaction_type = :all)
|
66
66
|
validate_transaction_type transaction_type
|
67
67
|
|
68
|
-
url = "history/transactions/#{transaction_type.to_s.upcase}/#{(
|
68
|
+
url = "history/transactions/#{transaction_type.to_s.upcase}/#{milliseconds(days)}"
|
69
69
|
|
70
70
|
@dealing_platform.gather url, :transactions, AccountTransaction
|
71
71
|
end
|
@@ -87,6 +87,15 @@ module IGMarkets
|
|
87
87
|
def format_date(date)
|
88
88
|
date.strftime '%d-%m-%Y'
|
89
89
|
end
|
90
|
+
|
91
|
+
# Converts a number of days into a number of milliseconds.
|
92
|
+
#
|
93
|
+
# @param [Fixnum, Float] days The number of days.
|
94
|
+
#
|
95
|
+
# @return [Fixnum]
|
96
|
+
def milliseconds(days)
|
97
|
+
(days.to_f * 24 * 60 * 60 * 1000).to_i
|
98
|
+
end
|
90
99
|
end
|
91
100
|
end
|
92
101
|
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 [Date] :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`.
|
@@ -68,11 +68,6 @@ module IGMarkets
|
|
68
68
|
# @return [String] The resulting deal reference, use {DealingPlatform#deal_confirmation} to check the result of
|
69
69
|
# the position creation.
|
70
70
|
def create(attributes)
|
71
|
-
attributes[:force_open] = false unless attributes.key? :force_open
|
72
|
-
attributes[:guaranteed_stop] = false unless attributes.key? :guaranteed_stop
|
73
|
-
attributes[:order_type] ||= :market
|
74
|
-
attributes[:time_in_force] = :execute_and_eliminate if attributes[:order_type] == :market
|
75
|
-
|
76
71
|
model = PositionCreateAttributes.new attributes
|
77
72
|
model.validate
|
78
73
|
|
@@ -89,7 +84,7 @@ module IGMarkets
|
|
89
84
|
attribute :currency_code, String, regex: Regex::CURRENCY
|
90
85
|
attribute :direction, Symbol, allowed_values: [:buy, :sell]
|
91
86
|
attribute :epic, String, regex: Regex::EPIC
|
92
|
-
attribute :expiry,
|
87
|
+
attribute :expiry, Date, format: '%d-%b-%y'
|
93
88
|
attribute :force_open, Boolean
|
94
89
|
attribute :guaranteed_stop, Boolean
|
95
90
|
attribute :level, Float
|
@@ -104,10 +99,16 @@ module IGMarkets
|
|
104
99
|
attribute :trailing_stop, Boolean
|
105
100
|
attribute :trailing_stop_increment, Fixnum
|
106
101
|
|
102
|
+
def initialize(attributes = {})
|
103
|
+
super
|
104
|
+
|
105
|
+
set_defaults
|
106
|
+
end
|
107
|
+
|
107
108
|
# Runs a series of validations on this model's attributes to check whether it is ready to be sent to the IG
|
108
109
|
# Markets API.
|
109
110
|
def validate
|
110
|
-
|
111
|
+
validate_required_attributes
|
111
112
|
Position.validate_order_type_constraints attributes
|
112
113
|
validate_trailing_stop_constraints
|
113
114
|
validate_stop_and_limit_constraints
|
@@ -116,8 +117,15 @@ module IGMarkets
|
|
116
117
|
|
117
118
|
private
|
118
119
|
|
120
|
+
def set_defaults
|
121
|
+
self.force_open = false if force_open.nil?
|
122
|
+
self.guaranteed_stop = false if guaranteed_stop.nil?
|
123
|
+
self.order_type ||= :market
|
124
|
+
self.time_in_force = :execute_and_eliminate if order_type == :market
|
125
|
+
end
|
126
|
+
|
119
127
|
# Checks that all required attributes for position creation are present.
|
120
|
-
def
|
128
|
+
def validate_required_attributes
|
121
129
|
required = [:currency_code, :direction, :epic, :force_open, :guaranteed_stop, :order_type, :size,
|
122
130
|
:time_in_force]
|
123
131
|
|
@@ -40,19 +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 [Date] :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 [Time] :good_till_date The date that the working order will live till.
|
46
|
-
#
|
45
|
+
# @option attributes [Time] :good_till_date The date that the working order will live till. If not specified then
|
46
|
+
# the working order will remain until it is cancelled.
|
47
47
|
# @option attributes [Boolean] :guaranteed_stop Whether a guaranteed stop is required. Defaults to `false`.
|
48
|
-
# @option attributes [Float] :level The level at which the order will be triggered.
|
48
|
+
# @option attributes [Float] :level The level at which the order will be triggered. Required.
|
49
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
51
|
# @option attributes [Fixnum] :stop_distance The distance away in pips to place the stop. Optional.
|
52
|
-
# @option attributes [:good_till_cancelled, :good_till_date] :time_in_force The lifespan of the working order.
|
53
|
-
# `:good_till_cancelled` means the working order will live until it is explicitly cancelled.
|
54
|
-
# `:good_till_date` means the working order will live until the date specified by
|
55
|
-
# `:good_till_date`. Defaults to `:good_till_cancelled`.
|
56
52
|
# @option attributes [:limit, :stop] :type `:limit` means the working order is intended to buy at a price lower
|
57
53
|
# than at present, or to sell at a price higher than at present, i.e. there is an expectation
|
58
54
|
# of a market reversal at the specified `:level`. `:stop` means the working order is intended
|
@@ -64,7 +60,7 @@ module IGMarkets
|
|
64
60
|
def create(attributes)
|
65
61
|
attributes[:force_open] ||= false
|
66
62
|
attributes[:guaranteed_stop] ||= false
|
67
|
-
attributes[:time_in_force]
|
63
|
+
attributes[:time_in_force] = attributes[:good_till_date] ? :good_till_date : :good_till_cancelled
|
68
64
|
|
69
65
|
model = build_working_order_model attributes
|
70
66
|
|
@@ -85,10 +81,6 @@ module IGMarkets
|
|
85
81
|
raise ArgumentError, "#{attribute} attribute must be set" if attributes[attribute].nil?
|
86
82
|
end
|
87
83
|
|
88
|
-
if model.time_in_force == :good_till_date && !model.good_till_date.is_a?(Time)
|
89
|
-
raise ArgumentError, 'good_till_date must be set when time_in_force is :good_till_date'
|
90
|
-
end
|
91
|
-
|
92
84
|
model
|
93
85
|
end
|
94
86
|
|
@@ -97,9 +89,9 @@ module IGMarkets
|
|
97
89
|
attribute :currency_code, String, regex: Regex::CURRENCY
|
98
90
|
attribute :direction, Symbol, allowed_values: [:buy, :sell]
|
99
91
|
attribute :epic, String, regex: Regex::EPIC
|
100
|
-
attribute :expiry,
|
92
|
+
attribute :expiry, Date, format: '%d-%b-%y'
|
101
93
|
attribute :force_open, Boolean
|
102
|
-
attribute :good_till_date, Time, format: '%
|
94
|
+
attribute :good_till_date, Time, format: '%Y/%m/%d %R:%S'
|
103
95
|
attribute :guaranteed_stop, Boolean
|
104
96
|
attribute :level, Float
|
105
97
|
attribute :limit_distance, Fixnum
|
@@ -64,15 +64,24 @@ module IGMarkets
|
|
64
64
|
raise ArgumentError, "#{self}: invalid or missing time format" unless options[:format].is_a? String
|
65
65
|
|
66
66
|
if value.is_a?(String) || value.is_a?(Fixnum)
|
67
|
-
|
68
|
-
Time.strptime "#{value}#{options[:time_zone]}", "#{options[:format]}#{'%z' if options[:time_zone]}"
|
69
|
-
rescue ArgumentError
|
70
|
-
raise ArgumentError, "#{self}: failed parsing time '#{value}' with format '#{options[:format]}'"
|
71
|
-
end
|
67
|
+
parse_time_from_string value.to_s, options
|
72
68
|
else
|
73
69
|
value
|
74
70
|
end
|
75
71
|
end
|
72
|
+
|
73
|
+
def parse_time_from_string(value, options)
|
74
|
+
format = options[:format]
|
75
|
+
time_zone = options[:time_zone]
|
76
|
+
|
77
|
+
time_zone ||= '+0000' unless format == '%Q'
|
78
|
+
|
79
|
+
begin
|
80
|
+
Time.strptime "#{value}#{time_zone}", "#{format}#{'%z' if time_zone}"
|
81
|
+
rescue ArgumentError
|
82
|
+
raise ArgumentError, "#{self}: failed parsing time '#{value}' with format '#{format}'"
|
83
|
+
end
|
84
|
+
end
|
76
85
|
end
|
77
86
|
end
|
78
87
|
end
|
data/lib/ig_markets/model.rb
CHANGED
@@ -90,7 +90,8 @@ module IGMarkets
|
|
90
90
|
# @option options [String] :format When `type` is `Date` or `Time` this specifies the format for parsing String
|
91
91
|
# and `Fixnum` instances assigned to this attribute.
|
92
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`.
|
93
|
+
# `String` values assigned to this attribute prior to parsing them with `:format`. Defaults to
|
94
|
+
# `+0000` (UTC) unless `:format` is `%Q`.
|
94
95
|
#
|
95
96
|
# @macro [attach] attribute
|
96
97
|
# The $1 attribute.
|
@@ -4,9 +4,18 @@ module IGMarkets
|
|
4
4
|
# @return [OpenSSL::PKey::RSA] The public key used by {#encrypt}, can also be set using {#encoded_public_key=}.
|
5
5
|
attr_accessor :public_key
|
6
6
|
|
7
|
-
# @return [String] The
|
7
|
+
# @return [String] The time stamp used by {#encrypt}.
|
8
8
|
attr_accessor :time_stamp
|
9
9
|
|
10
|
+
# Initializes this password encryptor with the specified encoded public key and timestamp.
|
11
|
+
#
|
12
|
+
# @param [String] encoded_public_key
|
13
|
+
# @param [String] time_stamp
|
14
|
+
def initialize(encoded_public_key = nil, time_stamp = nil)
|
15
|
+
self.encoded_public_key = encoded_public_key if encoded_public_key
|
16
|
+
self.time_stamp = time_stamp
|
17
|
+
end
|
18
|
+
|
10
19
|
# Takes an encoded public key and calls {#public_key=} with the decoded key.
|
11
20
|
#
|
12
21
|
# @param [String] encoded_public_key The public key encoded in Base64.
|