ig_markets 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/README.md +32 -16
  4. data/bin/ig_markets +29 -0
  5. data/lib/ig_markets.rb +15 -0
  6. data/lib/ig_markets/account_activity.rb +1 -1
  7. data/lib/ig_markets/account_transaction.rb +1 -1
  8. data/lib/ig_markets/application.rb +2 -2
  9. data/lib/ig_markets/cli/account_command.rb +40 -0
  10. data/lib/ig_markets/cli/activities_command.rb +33 -0
  11. data/lib/ig_markets/cli/confirmation_command.rb +32 -0
  12. data/lib/ig_markets/cli/main.rb +39 -0
  13. data/lib/ig_markets/cli/orders_command.rb +28 -0
  14. data/lib/ig_markets/cli/positions_command.rb +20 -0
  15. data/lib/ig_markets/cli/search_command.rb +30 -0
  16. data/lib/ig_markets/cli/sentiment_command.rb +35 -0
  17. data/lib/ig_markets/cli/sprints_command.rb +28 -0
  18. data/lib/ig_markets/cli/transactions_command.rb +56 -0
  19. data/lib/ig_markets/cli/watchlists_command.rb +34 -0
  20. data/lib/ig_markets/deal_confirmation.rb +4 -4
  21. data/lib/ig_markets/dealing_platform/position_methods.rb +3 -3
  22. data/lib/ig_markets/dealing_platform/sprint_market_position_methods.rb +1 -1
  23. data/lib/ig_markets/dealing_platform/watchlist_methods.rb +1 -3
  24. data/lib/ig_markets/dealing_platform/working_order_methods.rb +10 -10
  25. data/lib/ig_markets/format.rb +36 -0
  26. data/lib/ig_markets/historical_price_result.rb +1 -1
  27. data/lib/ig_markets/instrument.rb +2 -2
  28. data/lib/ig_markets/market.rb +10 -10
  29. data/lib/ig_markets/model.rb +23 -60
  30. data/lib/ig_markets/model/typecasters.rb +78 -0
  31. data/lib/ig_markets/payload_formatter.rb +19 -6
  32. data/lib/ig_markets/position.rb +7 -21
  33. data/lib/ig_markets/request_failed_error.rb +1 -1
  34. data/lib/ig_markets/sprint_market_position.rb +18 -3
  35. data/lib/ig_markets/version.rb +1 -1
  36. data/lib/ig_markets/working_order.rb +11 -11
  37. metadata +39 -64
  38. data/.codeclimate.yml +0 -15
  39. data/.gitignore +0 -9
  40. data/.rspec +0 -2
  41. data/.rubocop.yml +0 -2
  42. data/.travis.yml +0 -10
  43. data/.yardopts +0 -4
  44. data/Gemfile +0 -2
  45. data/ig_markets.gemspec +0 -28
  46. data/spec/factories/ig_markets/account.rb +0 -14
  47. data/spec/factories/ig_markets/account_activity.rb +0 -21
  48. data/spec/factories/ig_markets/account_balance.rb +0 -8
  49. data/spec/factories/ig_markets/account_transaction.rb +0 -15
  50. data/spec/factories/ig_markets/application.rb +0 -21
  51. data/spec/factories/ig_markets/client_sentiment.rb +0 -7
  52. data/spec/factories/ig_markets/deal_confirmation.rb +0 -20
  53. data/spec/factories/ig_markets/historical_price_result.rb +0 -7
  54. data/spec/factories/ig_markets/historical_price_result_data_allowance.rb +0 -7
  55. data/spec/factories/ig_markets/historical_price_result_price.rb +0 -7
  56. data/spec/factories/ig_markets/historical_price_result_snapshot.rb +0 -10
  57. data/spec/factories/ig_markets/instrument.rb +0 -32
  58. data/spec/factories/ig_markets/instrument_currency.rb +0 -9
  59. data/spec/factories/ig_markets/instrument_expiry_details.rb +0 -6
  60. data/spec/factories/ig_markets/instrument_margin_deposit_band.rb +0 -8
  61. data/spec/factories/ig_markets/instrument_opening_hours.rb +0 -6
  62. data/spec/factories/ig_markets/instrument_rollover_details.rb +0 -6
  63. data/spec/factories/ig_markets/instrument_slippage_factor.rb +0 -6
  64. data/spec/factories/ig_markets/market.rb +0 -7
  65. data/spec/factories/ig_markets/market_dealing_rules.rb +0 -11
  66. data/spec/factories/ig_markets/market_dealing_rules_rule_details.rb +0 -6
  67. data/spec/factories/ig_markets/market_hierarchy_result.rb +0 -6
  68. data/spec/factories/ig_markets/market_hierarchy_result_hierarchy_node.rb +0 -6
  69. data/spec/factories/ig_markets/market_overview.rb +0 -22
  70. data/spec/factories/ig_markets/market_snapshot.rb +0 -17
  71. data/spec/factories/ig_markets/position.rb +0 -19
  72. data/spec/factories/ig_markets/sprint_market_position.rb +0 -16
  73. data/spec/factories/ig_markets/watchlist.rb +0 -9
  74. data/spec/factories/ig_markets/working_order.rb +0 -21
  75. data/spec/ig_markets/account_transaction_spec.rb +0 -30
  76. data/spec/ig_markets/dealing_platform/account_methods_spec.rb +0 -58
  77. data/spec/ig_markets/dealing_platform/client_sentiment_methods_spec.rb +0 -29
  78. data/spec/ig_markets/dealing_platform/market_methods_spec.rb +0 -80
  79. data/spec/ig_markets/dealing_platform/position_methods_spec.rb +0 -137
  80. data/spec/ig_markets/dealing_platform/sprint_market_position_methods_spec.rb +0 -39
  81. data/spec/ig_markets/dealing_platform/watchlist_methods_spec.rb +0 -89
  82. data/spec/ig_markets/dealing_platform/working_order_methods_spec.rb +0 -120
  83. data/spec/ig_markets/dealing_platform_spec.rb +0 -40
  84. data/spec/ig_markets/model_spec.rb +0 -127
  85. data/spec/ig_markets/password_encryptor_spec.rb +0 -23
  86. data/spec/ig_markets/payload_formatter_spec.rb +0 -19
  87. data/spec/ig_markets/position_spec.rb +0 -37
  88. data/spec/ig_markets/response_parser_spec.rb +0 -13
  89. data/spec/ig_markets/session_spec.rb +0 -134
  90. data/spec/spec_helper.rb +0 -14
  91. data/spec/support/factory_girl.rb +0 -7
  92. data/spec/support/random_test_order.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4523bb2bdfdecccd336489d894f6ea218e78624
4
- data.tar.gz: c8b898a21868963278e30fc66cc680c167b4e2f7
3
+ metadata.gz: 30bad1af818d34d53ba4b795a9eabd4ceff42bc1
4
+ data.tar.gz: 10131179ae36ddafba7f61b8db89b162699dd5cb
5
5
  SHA512:
6
- metadata.gz: 369d69e66d370dcbd5ce69727ecd78d8b44e106de52bca60c68dedf30e0ef266d3a6d37021cae85553c2c4089a740743d07f50da7273ea211078d877e134efab
7
- data.tar.gz: ce58fbf4ac2a67fbe02235a0d41484cca37280bf2a0082711cf70e07bf72872a80442096c6402e8e4da73fe6bfa86228030d8bb4dbbcd4d03971873a745aca78
6
+ metadata.gz: 47e56e3def7e7da1e53bb3e990dcaa94eb27fa8eecffe8b6038a1806357bf79e7c08a0692c067fcf3f6c9a68251132c4242c8162fd5e535801c9f6bb59988194
7
+ data.tar.gz: 555d47c8e5b70d92b40e6e942f4bb0a395c7f0eb34c5873cc4864e56d35d8843691795c83dfd8cd74ac5f27124d9198929cf40719f52c55f686b9de2a42bbb47
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # IG Markets Changelog
2
+
3
+ ### 0.2 - April 14, 2016
4
+
5
+ - Added `ig_markets` command-line client
6
+ - `IGMarkets::Model` now has separate `Date` and `Time` attribute types, and a new `:time_zone` option is used for
7
+ `Time` attributes that have a known time zone. Previous uses of `DateTime` should be replaced with either `Date` or
8
+ `Time`.
9
+ - Changed `size` attribute to always be of type `Float` on all models.
10
+ - Changed `limit_distance` and `stop_distance` attributes to always be of type `Fixnum` on all models.
11
+ - Added `IGMarkets::Format` module
12
+ - Added `#expired?` and `#seconds_till_expiry` to `IGMarkets::SprintMarketPosition`
13
+ - Fixed `IGMarkets::RequestFailedError#message`
14
+
15
+ ### 0.1 - April 8, 2016
16
+
17
+ - Initial release
data/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
  [![Documentation][documentation-badge]][documentation-link]
9
9
  [![License][license-badge]][license-link]
10
10
 
11
- Easily access the IG Markets Dealing Platform from Ruby with this gem. Written against the
12
- [official REST API](http://labs.ig.com/rest-trading-api-reference).
11
+ Easily access the IG Markets Dealing Platform from Ruby with this gem, either directly through code or by using the
12
+ provided command-line client. Written against the [official REST API](http://labs.ig.com/rest-trading-api-reference).
13
13
 
14
14
  Includes support for:
15
15
 
@@ -28,25 +28,41 @@ An IG Markets production or demo trading account is needed in order to use this
28
28
 
29
29
  Licensed under the MIT license. You must read and agree to its terms to use this software.
30
30
 
31
- ## Requirements
31
+ ## Usage — Command-Line Client
32
32
 
33
- Ruby 2.0 or later.
33
+ ```sh
34
+ $ gem install ig_markets
34
35
 
35
- ## Installation
36
+ Usage: ig_markets <command> --username=<username> --password=<password> --api-key=<api-key> [--demo]
37
+ ```
36
38
 
37
- Add the following to your `Gemfile`
39
+ On startup `ig_markets` searches for files named `"./.ig_markets"` and then `"~/.ig_markets"`, and if they are present
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:
38
42
 
39
- ```ruby
40
- gem 'ig_markets', git: 'https://github.com/rviney/ig_markets.git'
43
+ ```
44
+ --username=<username>
45
+ --password=<password>
46
+ --api-key=<api-key>
47
+ # (include if this is a demo account) --demo
41
48
  ```
42
49
 
43
- Then run
50
+ Run `ig_markets help` to list details on available commands. The full list of commands is:
44
51
 
45
- ```ruby
46
- bundle install
47
- ```
52
+ - `ig_markets account`
53
+ - `ig_markets activities [--days=3]`
54
+ - `ig_markets confirmation --deal-reference=<...>`
55
+ - `ig_markets orders`
56
+ - `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...
48
64
 
49
- ## Usage
65
+ ## Usage — Library
50
66
 
51
67
  ```ruby
52
68
  ig = IGMarkets::DealingPlatform.new
@@ -59,8 +75,8 @@ ig.sign_out
59
75
  ig.account.all
60
76
  ig.account.recent_activities 24 * 60 * 60
61
77
  ig.account.recent_transactions 24 * 60 * 60
62
- ig.account.activities_in_date_range Date.today.prev_month(2), Date.today.prev_month(1)
63
- ig.account.transactions_in_date_range Date.today.prev_month(2), Date.today.prev_month(1)
78
+ ig.account.activities_in_date_range Date.today - 14, Date.today - 7
79
+ ig.account.transactions_in_date_range Date.today - 14, Date.today - 7
64
80
 
65
81
  # Dealing
66
82
  ig.deal_confirmation 'deal_reference'
@@ -91,7 +107,7 @@ ig.markets.hierarchy
91
107
  ig.markets.search 'EURUSD'
92
108
  ig.markets['CS.D.EURUSD.CFD.IP']
93
109
  ig.markets['CS.D.EURUSD.CFD.IP'].recent_prices :day, 10
94
- ig.markets['CS.D.EURUSD.CFD.IP'].prices_in_date_range :day, Date.today.prev_month(2), Date.today.prev_month(1)
110
+ ig.markets['CS.D.EURUSD.CFD.IP'].prices_in_date_range :day, Date.today - 14, Date.today - 7
95
111
 
96
112
  # Watchlists
97
113
  ig.watchlists.all
data/bin/ig_markets ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(File.realpath(__FILE__)) + '/../lib'
4
+
5
+ require 'ig_markets'
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
+ IGMarkets::CLI::Main.start modified_argv
data/lib/ig_markets.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'base64'
2
+ require 'date'
2
3
  require 'rest-client'
4
+ require 'thor'
3
5
 
4
6
  require 'ig_markets/boolean'
5
7
  require 'ig_markets/model'
8
+ require 'ig_markets/model/typecasters'
6
9
  require 'ig_markets/regex'
7
10
 
8
11
  require 'ig_markets/account'
@@ -10,6 +13,17 @@ require 'ig_markets/account_activity'
10
13
  require 'ig_markets/account_transaction'
11
14
  require 'ig_markets/api_versions'
12
15
  require 'ig_markets/application'
16
+ require 'ig_markets/cli/main'
17
+ require 'ig_markets/cli/account_command'
18
+ require 'ig_markets/cli/activities_command'
19
+ require 'ig_markets/cli/confirmation_command'
20
+ require 'ig_markets/cli/orders_command'
21
+ require 'ig_markets/cli/positions_command'
22
+ require 'ig_markets/cli/search_command'
23
+ require 'ig_markets/cli/sentiment_command'
24
+ require 'ig_markets/cli/sprints_command'
25
+ require 'ig_markets/cli/transactions_command'
26
+ require 'ig_markets/cli/watchlists_command'
13
27
  require 'ig_markets/client_sentiment'
14
28
  require 'ig_markets/deal_confirmation'
15
29
  require 'ig_markets/dealing_platform'
@@ -20,6 +34,7 @@ require 'ig_markets/dealing_platform/position_methods'
20
34
  require 'ig_markets/dealing_platform/sprint_market_position_methods'
21
35
  require 'ig_markets/dealing_platform/watchlist_methods'
22
36
  require 'ig_markets/dealing_platform/working_order_methods'
37
+ require 'ig_markets/format'
23
38
  require 'ig_markets/instrument'
24
39
  require 'ig_markets/historical_price_result'
25
40
  require 'ig_markets/market'
@@ -8,7 +8,7 @@ module IGMarkets
8
8
  attribute :activity_history_id
9
9
  attribute :channel
10
10
  attribute :currency
11
- attribute :date, DateTime, format: '%d/%m/%y'
11
+ attribute :date, Date, format: '%d/%m/%y'
12
12
  attribute :deal_id
13
13
  attribute :epic, String, regex: Regex::EPIC
14
14
  attribute :level, Float
@@ -6,7 +6,7 @@ module IGMarkets
6
6
  attribute :cash_transaction, Boolean
7
7
  attribute :close_level
8
8
  attribute :currency
9
- attribute :date, DateTime, format: '%d/%m/%y'
9
+ attribute :date, Date, format: '%d/%m/%y'
10
10
  attribute :instrument_name
11
11
  attribute :open_level, String, nil_if: '-'
12
12
  attribute :period, String, nil_if: '-'
@@ -10,13 +10,13 @@ module IGMarkets
10
10
  attribute :api_key
11
11
  attribute :client_id
12
12
  attribute :concurrent_subscriptions_limit, Fixnum
13
- attribute :created_date, DateTime, format: '%Q'
13
+ attribute :created_date, Time, format: '%Q'
14
14
  attribute :fast_markets_settlement_price_enabled, Boolean
15
15
  attribute :id
16
16
  attribute :name
17
17
  attribute :restricted_to_self, Boolean
18
18
  attribute :status, Symbol, allowed_values: [:disabled, :enabled, :revoked]
19
- attribute :terms_accepted_date, DateTime, format: '%Q'
19
+ attribute :terms_accepted_date, Time, format: '%Q'
20
20
  attribute :tier
21
21
  end
22
22
  end
@@ -0,0 +1,40 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets account` command.
4
+ class Main
5
+ desc 'account', 'Prints account overview and balances'
6
+
7
+ def account
8
+ begin_session do
9
+ dealing_platform.account.all.each do |account|
10
+ print_account account
11
+ print_account_balance account
12
+ end
13
+ end
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
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets activities` command.
4
+ class Main
5
+ desc 'activities', 'Prints recent activities'
6
+
7
+ option :days, default: 3, type: :numeric, desc: 'The number of days to print recent activities for'
8
+
9
+ def activities
10
+ begin_session do
11
+ dealing_platform.account.recent_activities(seconds).each do |activity|
12
+ print_activity activity
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def seconds
20
+ (options[:days].to_f * 60 * 60 * 24).to_i
21
+ end
22
+
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
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets confirmation` command.
4
+ class Main < Thor
5
+ desc 'confirmation', 'Prints the deal confirmation for the specified deal reference.'
6
+
7
+ option :deal_reference, aliases: '-r', required: true, desc: 'The deal reference to print the confirmation for.'
8
+
9
+ def confirmation
10
+ begin_session do
11
+ deal_confirmation = dealing_platform.deal_confirmation options[:deal_reference]
12
+
13
+ print_deal_confirmation deal_confirmation
14
+ end
15
+ 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
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ module IGMarkets
2
+ # This module contains the code for the CLI frontend. See `README.md` for usage details.
3
+ module CLI
4
+ # Implements the `ig_markets` command-line client.
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
16
+
17
+ # Intercepts calls to `exit` in the CLI commands, used by the test suite.
18
+ def exit(code)
19
+ Kernel.exit code
20
+ end
21
+
22
+ def dealing_platform
23
+ @dealing_platform ||= DealingPlatform.new
24
+ end
25
+
26
+ def begin_session
27
+ platform = options[:demo] ? :demo : :production
28
+
29
+ dealing_platform.sign_in options[:username], options[:password], options[:api_key], platform
30
+
31
+ yield
32
+ rescue IGMarkets::RequestFailedError => error
33
+ print "ERROR: #{error.error}\n"
34
+ exit 1
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets orders` command.
4
+ class Main
5
+ desc 'orders', 'Prints working orders'
6
+
7
+ def orders
8
+ begin_session do
9
+ dealing_platform.working_orders.all.each do |order|
10
+ print_working_order order
11
+ end
12
+ end
13
+ end
14
+
15
+ private
16
+
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
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets positions` command.
4
+ class Main
5
+ desc 'positions', 'Prints open positions'
6
+
7
+ def positions
8
+ begin_session do
9
+ dealing_platform.positions.all.each do |position|
10
+ print <<-END
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
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ module IGMarkets
2
+ module CLI
3
+ # Implements the `ig_markets search` command.
4
+ class Main < Thor
5
+ desc 'search', 'Searches markets based on a query string'
6
+
7
+ option :query, aliases: '-q', required: true, desc: 'The search query'
8
+
9
+ def search
10
+ begin_session do
11
+ dealing_platform.markets.search(options[:query]).each do |market|
12
+ print_market_overview market
13
+ end
14
+ end
15
+ 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
+ end
29
+ end
30
+ end