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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 790fdd28852c741b152d6ad02e17251b7cd388cf
4
- data.tar.gz: 6addb19b82511550ffdfa99b06feb806dd4ab6ab
3
+ metadata.gz: 724ac2b070ec1f942dc3d1be4417bb82486dd414
4
+ data.tar.gz: f219d63e7c52e427e5813a6369abcfcea838c9c3
5
5
  SHA512:
6
- metadata.gz: 8e79af030e563ea9f8f75a7b5c8f16ec06f03a73687a8bb1b29ae59f6e84fb04caff20e0389237df9c21c0c3431a3d1f5c9be128e1a54f794ca1be3265df8ab0
7
- data.tar.gz: 05a68232d1b4bdc221fca16c3ee07782646a4dbd481915e57c360dd5aa0c8152c6151d3e54cdff632a017dbbc2d137334e9a73a3a398b384db6754b75cb1747d
6
+ metadata.gz: 037f9fe742a849aa0c74a537bafbe276078e7a34d41fd30f5302e5ff9f025de88ba13842438cef017fddb3ed562f9bb4a317735c72e79b6e2679a9054965b9b5
7
+ data.tar.gz: 89aa2c236ca20d253a5b5be311cbf9f0819c440fa77b2caa0d0c91380ae200287375b8ff60b52ebeca9b5eaa3e18c41374c9379aa61b26c52e5c2c27c7ccbf9e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # IG Markets Changelog
2
2
 
3
+ ### 0.4 - April 18, 2016
4
+
5
+ - Added `create`, `update` and `delete` subcommands to `ig_markets orders`
6
+ - Added `create`, `update` and `close` subcommands to `ig_markets positions`
7
+ - Added `create` subcommand to `ig_markets sprints`
8
+ - Added `create`, `add-markets`, `remove-markets` and `delete` subcommands to `ig_markets watchlists`
9
+ - `ig_markets confirmation`, `ig_markets search` and `ig_markets sentiment` now take their mandatory argument directly
10
+ - Added `--start-date` option to the `ig_markets activities` and `ig_markets transactions` commands
11
+ - Removed the `:time_in_force` option from `IGMarkets::WorkingOrderMethods#create` and `IGMarkets::WorkingOrder#update`,
12
+ just set `:good_till_date` if it is needed
13
+ - `IGMarkets::AccountMethods#recent_activities` and `IGMarkets::AccountMethods#recent_transactions` now take a number of
14
+ days rather than a number of seconds
15
+ - Fixed errors working with a working order's `#good_till_date` attribute
16
+ - Automatically reauthenticate if the client security token has expired
17
+
3
18
  ### 0.3 - April 14, 2016
4
19
 
5
20
  - Added `--version` and `-v` options to the command-line client
data/README.md CHANGED
@@ -33,34 +33,43 @@ Licensed under the MIT license. You must read and agree to its terms to use this
33
33
  ```sh
34
34
  $ gem install ig_markets
35
35
 
36
- Usage: ig_markets <command> --username=<username> --password=<password> --api-key=<api-key> [--demo]
36
+ Usage: ig_markets COMMAND --username=USERNAME --password=PASSWORD --api-key=API-KEY [--demo]
37
37
  ```
38
38
 
39
39
  On startup `ig_markets` searches for files named `"./.ig_markets"` and then `"~/.ig_markets"`, and if they are present
40
40
  interprets their contents as command-line arguments. This can be used to avoid having to specify authentication details
41
- with every invocation. To do this create a file at `./".ig_markets"` or `~/".ig_markets"` with the following contents:
41
+ with every invocation. To do this create a file at `"./.ig_markets"` or `"~/.ig_markets"` with the following contents:
42
42
 
43
43
  ```
44
- --username=<username>
45
- --password=<password>
46
- --api-key=<api-key>
47
- # (include if this is a demo account) --demo
44
+ --username=USERNAME
45
+ --password=PASSWORD
46
+ --api-key=API-KEY
47
+ --demo # Include only if this is a demo account
48
48
  ```
49
49
 
50
50
  Run `ig_markets help` to list details on available commands. The full list of commands is:
51
51
 
52
52
  - `ig_markets account`
53
- - `ig_markets activities [--days=3]`
54
- - `ig_markets confirmation --deal-reference=<...>`
55
- - `ig_markets orders`
53
+ - `ig_markets activities --days=N [--start-date=YYYY-MM-DD]`
54
+ - `ig_markets confirmation DEAL-REFERENCE`
55
+ - `ig_markets orders [list]`
56
+ - `ig_markets orders create ...`
57
+ - `ig_markets orders update DEAL-ID ...`
58
+ - `ig_markets orders delete DEAL-ID`
56
59
  - `ig_markets positions`
57
- - `ig_markets search --query=<...>`
58
- - `ig_markets sentiment --market=<...> [--related]`
59
- - `ig_markets sprints`
60
- - `ig_markets transactions [--days=3]`
61
- - `ig_markets watchlists`
62
-
63
- Note: at present there is no support in the command-line client for creating/updating/deleting positions, orders, etc...
60
+ - `ig_markets positions create ...`
61
+ - `ig_markets positions update DEAL-ID ...`
62
+ - `ig_markets positions close DEAL-ID ...`
63
+ - `ig_markets search QUERY`
64
+ - `ig_markets sentiment MARKET [--related]`
65
+ - `ig_markets sprints [list]`
66
+ - `ig_markets sprints create ...`
67
+ - `ig_markets transactions --days=N [--start-date=YYYY-MM-DD]`
68
+ - `ig_markets watchlists [list]`
69
+ - `ig_markets watchlists create NAME [EPIC EPIC ...]`
70
+ - `ig_markets watchlists add-markets WATCHLIST-ID [EPIC EPIC ...]`
71
+ - `ig_markets watchlists remove-markets WATCHLIST-ID [EPIC EPIC ...]`
72
+ - `ig_markets watchlists delete WATCHLIST-ID`
64
73
 
65
74
  ## Usage — Library
66
75
 
@@ -73,8 +82,8 @@ ig.sign_out
73
82
 
74
83
  # Account
75
84
  ig.account.all
76
- ig.account.recent_activities 24 * 60 * 60
77
- ig.account.recent_transactions 24 * 60 * 60
85
+ ig.account.recent_activities 365
86
+ ig.account.recent_transactions 365
78
87
  ig.account.activities_in_date_range Date.today - 14, Date.today - 7
79
88
  ig.account.transactions_in_date_range Date.today - 14, Date.today - 7
80
89
 
@@ -97,7 +106,7 @@ ig.sprint_market_positions.create direction: :buy, epic: 'FM.D.EURUSD24.EURUSD24
97
106
  # Working orders
98
107
  ig.working_orders.all
99
108
  ig.working_orders.create currency_code: 'USD', direction: :buy, epic: 'CS.D.EURUSD.CFD.IP', level: 0.99,
100
- size: 1, time_in_force: :good_till_cancelled, type: :limit
109
+ size: 1, type: :limit
101
110
  ig.working_orders['deal_id']
102
111
  ig.working_orders['deal_id'].update level: 1.25, limit_distance: 50, stop_distance: 0.02
103
112
  ig.working_orders['deal_id'].delete
data/bin/ig_markets CHANGED
@@ -4,32 +4,4 @@ $LOAD_PATH.unshift File.dirname(File.realpath(__FILE__)) + '/../lib'
4
4
 
5
5
  require 'ig_markets'
6
6
 
7
- # Read arguments from any '.ig_markets' config file
8
- def config_file_arguments
9
- config_file = ['.ig_markets', "#{Dir.home}/.ig_markets"].detect { |file| File.exist? file }
10
-
11
- return [] unless config_file
12
-
13
- File.readlines(config_file)
14
- .map { |line| line.gsub(/#.*/, '') }
15
- .map(&:strip)
16
- .join(' ')
17
- .split(' ')
18
- end
19
-
20
- # Put arguments from the config file into a modified ARGV array
21
- def modified_argv
22
- insert_index = ARGV.index do |argument|
23
- argument[0] == '-'
24
- end || ARGV.size
25
-
26
- ARGV.dup.insert insert_index, *config_file_arguments
27
- end
28
-
29
- # Implement --version and -v arguments
30
- if ARGV.index('--version') || ARGV.index('-v')
31
- puts IGMarkets::VERSION
32
- exit
33
- end
34
-
35
- IGMarkets::CLI::Main.start modified_argv
7
+ IGMarkets::CLI::Main.bootstrap ARGV
@@ -5,36 +5,13 @@ module IGMarkets
5
5
  desc 'account', 'Prints account overview and balances'
6
6
 
7
7
  def account
8
- begin_session do
8
+ self.class.begin_session(options) do |dealing_platform|
9
9
  dealing_platform.account.all.each do |account|
10
- print_account account
11
- print_account_balance account
10
+ Output.print_account account
11
+ Output.print_account_balance account
12
12
  end
13
13
  end
14
14
  end
15
-
16
- private
17
-
18
- def print_account(account)
19
- print <<-END
20
- Account '#{account.account_name}':
21
- ID: #{account.account_id}
22
- Type: #{account.account_type.to_s.upcase}
23
- Currency: #{account.currency}
24
- Status: #{account.status.to_s.upcase}
25
- END
26
- end
27
-
28
- def print_account_balance(account)
29
- {
30
- available: 'Available: ',
31
- balance: 'Balance: ',
32
- deposit: 'Margin: ',
33
- profit_loss: 'Profit/loss:'
34
- }.each do |attribute, display_name|
35
- print " #{display_name} #{Format.currency account.balance.send(attribute), account.currency}\n"
36
- end
37
- end
38
15
  end
39
16
  end
40
17
  end
@@ -2,31 +2,29 @@ module IGMarkets
2
2
  module CLI
3
3
  # Implements the `ig_markets activities` command.
4
4
  class Main
5
- desc 'activities', 'Prints recent activities'
5
+ desc 'activities', 'Prints account activities'
6
6
 
7
- option :days, default: 3, type: :numeric, desc: 'The number of days to print recent activities for'
7
+ option :days, type: :numeric, required: true, desc: 'The number of days to print account activities for'
8
+ option :start_date, desc: 'The start date to print account activities from, format: yyyy-mm-dd'
8
9
 
9
10
  def activities
10
- begin_session do
11
- dealing_platform.account.recent_activities(seconds).each do |activity|
12
- print_activity activity
11
+ self.class.begin_session(options) do |dealing_platform|
12
+ gather_activities(dealing_platform, options[:days], options[:start_date]).sort_by(&:date).each do |activity|
13
+ Output.print_activity activity
13
14
  end
14
15
  end
15
16
  end
16
17
 
17
18
  private
18
19
 
19
- def seconds
20
- (options[:days].to_f * 60 * 60 * 24).to_i
21
- end
20
+ def gather_activities(dealing_platform, days, start_date = nil)
21
+ if start_date
22
+ start_date = Date.strptime options[:start_date], '%F'
22
23
 
23
- def print_activity(activity)
24
- print <<-END
25
- #{activity.deal_id}: \
26
- #{activity.size} of #{activity.epic}, \
27
- level: #{activity.level}, \
28
- result: #{activity.result}
29
- END
24
+ dealing_platform.account.activities_in_date_range start_date, start_date + days.to_i
25
+ else
26
+ dealing_platform.account.recent_activities days
27
+ end
30
28
  end
31
29
  end
32
30
  end
@@ -0,0 +1,37 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Helper class for working with the config files supported by the command-line client.
4
+ class ConfigFile
5
+ # Initializes this config file with the contents of the specified config file.
6
+ def initialize(config_file)
7
+ @lines = File.readlines config_file
8
+ end
9
+
10
+ # Returns the arguments in this config file as an array.
11
+ #
12
+ # @return [Array<String>]
13
+ def arguments
14
+ @lines.map { |line| line.gsub(/#.*/, '') }
15
+ .map(&:strip)
16
+ .join(' ')
17
+ .split(' ')
18
+ end
19
+
20
+ # Returns the config file to use, or nil if there is no config file.
21
+ #
22
+ # @return [CLI::ConfigFile]
23
+ def self.find
24
+ config_file_locations = [
25
+ "#{Dir.pwd}/.ig_markets",
26
+ "#{Dir.home}/.ig_markets"
27
+ ]
28
+
29
+ config_file = config_file_locations.detect do |filename|
30
+ File.exist? filename
31
+ end
32
+
33
+ config_file ? new(config_file) : nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,31 +2,15 @@ module IGMarkets
2
2
  module CLI
3
3
  # Implements the `ig_markets confirmation` command.
4
4
  class Main < Thor
5
- desc 'confirmation', 'Prints the deal confirmation for the specified deal reference.'
5
+ desc 'confirmation <DEAL-REFERENCE>', 'Prints the deal confirmation for the specified deal reference'
6
6
 
7
- option :deal_reference, aliases: '-r', required: true, desc: 'The deal reference to print the confirmation for.'
7
+ def confirmation(deal_reference)
8
+ self.class.begin_session(options) do |dealing_platform|
9
+ deal_confirmation = dealing_platform.deal_confirmation deal_reference
8
10
 
9
- def confirmation
10
- begin_session do
11
- deal_confirmation = dealing_platform.deal_confirmation options[:deal_reference]
12
-
13
- print_deal_confirmation deal_confirmation
11
+ Output.print_deal_confirmation deal_confirmation
14
12
  end
15
13
  end
16
-
17
- private
18
-
19
- def print_deal_confirmation(deal_confirmation)
20
- print "#{deal_confirmation.deal_id}: #{deal_confirmation.deal_status}, "
21
-
22
- if deal_confirmation.deal_status == :accepted
23
- print "affected deals: #{deal_confirmation.affected_deals.map(&:deal_id).join(',')}, "
24
- else
25
- print "reason: #{deal_confirmation.reason}, "
26
- end
27
-
28
- print "epic: #{deal_confirmation.epic}\n"
29
- end
30
14
  end
31
15
  end
32
16
  end
@@ -3,36 +3,95 @@ module IGMarkets
3
3
  module CLI
4
4
  # Implements the `ig_markets` command-line client.
5
5
  class Main < Thor
6
- class_option :username, aliases: '-u', required: true, desc: 'The username for the session'
7
- class_option :password, aliases: '-p', required: true, desc: 'The password for the session'
8
- class_option :api_key, aliases: '-k', required: true, desc: 'The API key for the session'
9
- class_option :demo, aliases: '-d', type: :boolean, desc: 'Use the demo platform (default is production)'
10
-
11
- no_commands do
12
- # Intercepts calls to `print` in the CLI commands, used by the test suite.
13
- def print(string)
14
- Kernel.print string
15
- end
6
+ class_option :username, required: true, desc: 'The username for the session'
7
+ class_option :password, required: true, desc: 'The password for the session'
8
+ class_option :api_key, required: true, desc: 'The API key for the session'
9
+ class_option :demo, type: :boolean, desc: 'Use the demo platform (default is production)'
16
10
 
17
- # Intercepts calls to `exit` in the CLI commands, used by the test suite.
18
- def exit(code)
19
- Kernel.exit code
20
- end
11
+ desc 'orders [SUBCOMAND=list ...]', 'Command for working with orders'
12
+ subcommand 'orders', Orders
21
13
 
22
- def dealing_platform
23
- @dealing_platform ||= DealingPlatform.new
24
- end
14
+ desc 'positions [SUBCOMAND=list ...]', 'Command for working with positions'
15
+ subcommand 'positions', Positions
25
16
 
26
- def begin_session
17
+ desc 'sprints [SUBCOMAND=list ...]', 'Command for working with sprint market positions'
18
+ subcommand 'sprints', Sprints
19
+
20
+ desc 'watchlists [SUBCOMAND=list ...]', 'Command for working with watchlists'
21
+ subcommand 'watchlists', Watchlists
22
+
23
+ class << self
24
+ # Signs in to IG Markets and yields back an {DealingPlatform} instance, with common error handling if exceptions
25
+ # occur. This method is used by all of the CLI commands to authenticate.
26
+ #
27
+ # @param [Thor::CoreExt::HashWithIndifferentAccess] options The Thor options hash.
28
+ #
29
+ # @return [void]
30
+ def begin_session(options)
27
31
  platform = options[:demo] ? :demo : :production
28
32
 
29
33
  dealing_platform.sign_in options[:username], options[:password], options[:api_key], platform
30
34
 
31
- yield
35
+ yield dealing_platform
32
36
  rescue IGMarkets::RequestFailedError => error
33
- print "ERROR: #{error.error}\n"
37
+ warn "Request failed: #{error.error}"
38
+ exit 1
39
+ rescue StandardError => error
40
+ warn "Error: #{error}"
34
41
  exit 1
35
42
  end
43
+
44
+ # The dealing platform instance used by {begin_session}.
45
+ def dealing_platform
46
+ @dealing_platform ||= DealingPlatform.new
47
+ end
48
+
49
+ # Parses and validates a Date or Time option received on the command line. Raises `ArgumentError` if the
50
+ # attribute has been specified in an invalid format.
51
+ #
52
+ # @param [Hash] attributes The attributes hash.
53
+ # @param [Symbol] attribute The name of the date or time attribute to parse and validate.
54
+ # @param [Date, Time] klass The class to validate with.
55
+ # @param [String] format The `strptime` format string to parse the attribute with.
56
+ # @param [String] display_format The human-readable version of `format` to put into an exception if there is
57
+ # a problem parsing the attribute.
58
+ #
59
+ # @return [void]
60
+ def parse_date_time(attributes, attribute, klass, format, display_format)
61
+ return unless attributes.key? attribute
62
+
63
+ if !['', attribute.to_s].include? attributes[attribute].to_s
64
+ begin
65
+ attributes[attribute] = klass.strptime attributes[attribute], format
66
+ rescue ArgumentError
67
+ raise "invalid #{attribute}, use format \"#{display_format}\""
68
+ end
69
+ else
70
+ attributes[attribute] = nil
71
+ end
72
+ end
73
+
74
+ # This is the initial entry point for the execution of the command-line client. It is responsible for reading
75
+ # any config files, implementing the --version/-v options, and then invoking the main application.
76
+ #
77
+ # @param [Array<String>] argv The array of command-line arguments.
78
+ #
79
+ # @return [void]
80
+ def bootstrap(argv)
81
+ if argv.index('--version') || argv.index('-v')
82
+ puts VERSION
83
+ exit
84
+ end
85
+
86
+ # Use arguments from a config file if one exists
87
+ config_file = ConfigFile.find
88
+ if config_file
89
+ insert_index = argv.index { |argument| argument[0] == '-' } || -1
90
+ argv.insert insert_index, *config_file.arguments
91
+ end
92
+
93
+ start argv
94
+ end
36
95
  end
37
96
  end
38
97
  end
@@ -1,27 +1,95 @@
1
1
  module IGMarkets
2
2
  module CLI
3
3
  # Implements the `ig_markets orders` command.
4
- class Main
5
- desc 'orders', 'Prints working orders'
4
+ class Orders < Thor
5
+ desc 'list', 'Prints working orders'
6
6
 
7
- def orders
8
- begin_session do
7
+ def list
8
+ Main.begin_session(options) do |dealing_platform|
9
9
  dealing_platform.working_orders.all.each do |order|
10
- print_working_order order
10
+ Output.print_working_order order
11
11
  end
12
12
  end
13
13
  end
14
14
 
15
+ default_task :list
16
+
17
+ desc 'create', 'Creates a new working order'
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, default: false'
25
+ option :good_till_date, desc: 'The date that the order will live till, if not specified then the order will ' \
26
+ 'remain until it is deleted, format: yyyy-mm-ddThh:mm(+|-)zz:zz'
27
+ option :guaranteed_stop, type: :boolean, default: false, desc: 'Whether a guaranteed stop is required, ' \
28
+ 'default: false'
29
+ option :level, type: :numeric, required: true, desc: 'The level at which the order will be triggered'
30
+ option :limit_distance, type: :numeric, desc: 'The distance away in pips to place the limit'
31
+ option :size, type: :numeric, required: true, desc: 'The size of the order'
32
+ option :stop_distance, type: :numeric, desc: 'The distance away in pips to place the stop'
33
+ option :type, required: true, desc: 'The order type, either \'limit\' or \'stop\''
34
+
35
+ def create
36
+ Main.begin_session(options) do |dealing_platform|
37
+ deal_reference = dealing_platform.working_orders.create working_order_attributes
38
+
39
+ puts "Deal reference: #{deal_reference}"
40
+
41
+ Output.print_deal_confirmation dealing_platform.deal_confirmation(deal_reference)
42
+ end
43
+ end
44
+
45
+ desc 'update <DEAL-ID>', 'Updates an existing working order'
46
+
47
+ option :good_till_date, desc: 'The date that the order will live till, if not specified then the order will ' \
48
+ 'remain until it is deleted, format: yyyy-mm-ddThh:mm(+|-)zz:zz'
49
+ option :level, type: :numeric, desc: 'The level at which the order will be triggered'
50
+ option :limit_distance, type: :numeric, desc: 'The distance away in pips to place the limit'
51
+ option :stop_distance, type: :numeric, desc: 'The distance away in pips to place the stop'
52
+ option :type, desc: 'The order type, either \'limit\' or \'stop\''
53
+
54
+ def update(deal_id)
55
+ Main.begin_session(options) do |dealing_platform|
56
+ deal_reference = dealing_platform.working_orders[deal_id].update working_order_attributes
57
+
58
+ puts "Deal reference: #{deal_reference}"
59
+
60
+ Output.print_deal_confirmation dealing_platform.deal_confirmation(deal_reference)
61
+ end
62
+ end
63
+
64
+ desc 'delete <DEAL-ID>', 'Deletes the working order with the specified deal ID'
65
+
66
+ def delete(deal_id)
67
+ Main.begin_session(options) do |dealing_platform|
68
+ working_order = dealing_platform.working_orders[deal_id]
69
+
70
+ raise 'No working order with the specified deal ID' unless working_order
71
+
72
+ deal_reference = working_order.delete
73
+ puts "Deal reference: #{deal_reference}"
74
+
75
+ Output.print_deal_confirmation dealing_platform.deal_confirmation(deal_reference)
76
+ end
77
+ end
78
+
15
79
  private
16
80
 
17
- def print_working_order(order)
18
- print <<-END
19
- #{order.deal_id}: \
20
- #{order.direction} #{format '%g', order.order_size} of #{order.epic} at #{order.order_level}\
21
- , limit distance: #{order.limit_distance || '-'}\
22
- , stop distance: #{order.stop_distance || '-'}\
23
- #{", good till #{order.good_till_date.utc.strftime '%F %T %z'}" if order.time_in_force == :good_till_date}
24
- END
81
+ def working_order_attributes
82
+ attributes = options.each_with_object({}) do |(key, value), new_hash|
83
+ if [:currency_code, :direction, :epic, :expiry, :force_open, :good_till_date, :guaranteed_stop, :level,
84
+ :limit_distance, :size, :stop_distance, :type].include? key.to_sym
85
+ new_hash[key.to_sym] = value
86
+ end
87
+ end
88
+
89
+ Main.parse_date_time attributes, :expiry, Date, '%F', 'yyyy-mm-dd'
90
+ Main.parse_date_time attributes, :good_till_date, Time, '%FT%R%z', 'yyyy-mm-ddThh:mm(+|-)zz:zz'
91
+
92
+ attributes
25
93
  end
26
94
  end
27
95
  end
@@ -0,0 +1,115 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Contains methods used by the CLI to print models to stdout.
4
+ module Output
5
+ module_function
6
+
7
+ def print_account(account)
8
+ puts <<-END
9
+ Account '#{account.account_name}':
10
+ ID: #{account.account_id}
11
+ Type: #{account.account_type.to_s.upcase}
12
+ Currency: #{account.currency}
13
+ Status: #{account.status.to_s.upcase}
14
+ END
15
+ end
16
+
17
+ def print_account_balance(account)
18
+ {
19
+ available: 'Available: ',
20
+ balance: 'Balance: ',
21
+ deposit: 'Margin: ',
22
+ profit_loss: 'Profit/loss:'
23
+ }.each do |attribute, title|
24
+ puts " #{title} #{Format.currency account.balance.send(attribute), account.currency}"
25
+ end
26
+ end
27
+
28
+ def print_activity(activity)
29
+ puts <<-END
30
+ #{activity.date.strftime '%F'} #{activity.deal_id}: \
31
+ #{activity.size} of #{activity.epic}, \
32
+ level: #{activity.level}, \
33
+ result: #{activity.result}
34
+ END
35
+ end
36
+
37
+ def print_client_sentiment(client_sentiment)
38
+ puts <<-END
39
+ #{client_sentiment.market_id}: \
40
+ longs: #{client_sentiment.long_position_percentage}%, \
41
+ shorts: #{client_sentiment.short_position_percentage}%
42
+ END
43
+ end
44
+
45
+ def print_deal_confirmation(deal_confirmation)
46
+ print "Deal confirmation: #{deal_confirmation.deal_id}, #{deal_confirmation.deal_status}, "
47
+
48
+ if deal_confirmation.deal_status == :accepted
49
+ print "affected deals: #{deal_confirmation.affected_deals.map(&:deal_id).join(',')}, "
50
+ else
51
+ print "reason: #{deal_confirmation.reason}, "
52
+ end
53
+
54
+ puts "epic: #{deal_confirmation.epic}"
55
+ end
56
+
57
+ def print_market_overview(market)
58
+ puts <<-END
59
+ #{market.epic}: \
60
+ #{market.instrument_name}, \
61
+ type: #{market.instrument_type}, \
62
+ bid: #{market.bid} \
63
+ offer: #{market.offer}
64
+ END
65
+ end
66
+
67
+ def print_position(position)
68
+ puts <<-END
69
+ #{position.deal_id}: \
70
+ #{position.formatted_size} of #{position.market.epic} at #{position.level}, \
71
+ profit/loss: #{Format.currency position.profit_loss, position.currency}
72
+ END
73
+ end
74
+
75
+ def print_sprint_market_position(sprint)
76
+ puts <<-END
77
+ #{sprint.deal_id}: \
78
+ #{Format.currency sprint.size, sprint.currency} on #{sprint.epic} \
79
+ to be #{{ buy: 'above', sell: 'below' }.fetch(sprint.direction)} #{sprint.strike_level} \
80
+ in #{Format.seconds sprint.seconds_till_expiry}, \
81
+ payout: #{Format.currency sprint.payout_amount, sprint.currency}
82
+ END
83
+ end
84
+
85
+ def print_transaction(transaction)
86
+ puts <<-END
87
+ #{transaction.date.strftime '%F'} #{transaction.reference}: \
88
+ #{transaction.formatted_transaction_type}, \
89
+ #{"#{transaction.size} of " if transaction.size}\
90
+ #{transaction.instrument_name}, \
91
+ profit/loss: #{Format.currency transaction.profit_and_loss_amount, transaction.currency}
92
+ END
93
+ end
94
+
95
+ def print_watchlist(watchlist)
96
+ puts <<-END
97
+ #{watchlist.id}: #{watchlist.name}, \
98
+ editable: #{watchlist.editable}, \
99
+ deleteable: #{watchlist.deleteable}, \
100
+ default: #{watchlist.default_system_watchlist}
101
+ END
102
+ end
103
+
104
+ def print_working_order(order)
105
+ puts <<-END
106
+ #{order.deal_id}: \
107
+ #{order.direction} #{format '%g', order.order_size} of #{order.epic} at #{order.order_level}\
108
+ , limit distance: #{order.limit_distance || '-'}\
109
+ , stop distance: #{order.stop_distance || '-'}\
110
+ #{", good till #{order.good_till_date.utc.strftime '%F %R %z'}" if order.time_in_force == :good_till_date}
111
+ END
112
+ end
113
+ end
114
+ end
115
+ end