ig_markets 0.3 → 0.4
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 +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.
|