dtn 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +21 -5
  3. data/.env.example +5 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +7 -0
  6. data/CHANGELOG.md +5 -1
  7. data/Gemfile +3 -1
  8. data/README.md +288 -9
  9. data/Rakefile +2 -0
  10. data/docker-compose.yml +34 -0
  11. data/dtn.gemspec +1 -1
  12. data/lib/dtn.rb +18 -1
  13. data/lib/dtn/concerns/id.rb +36 -0
  14. data/lib/dtn/concerns/validation.rb +71 -0
  15. data/lib/dtn/helpers/catalog.rb +32 -0
  16. data/lib/dtn/lookups/catalog/listed_markets.rb +12 -0
  17. data/lib/dtn/lookups/catalog/naic_codes.rb +12 -0
  18. data/lib/dtn/lookups/catalog/security_types.rb +12 -0
  19. data/lib/dtn/lookups/catalog/sic_codes.rb +12 -0
  20. data/lib/dtn/lookups/catalog/trade_conditions.rb +12 -0
  21. data/lib/dtn/lookups/historical/base.rb +43 -0
  22. data/lib/dtn/lookups/historical/daily_datapoint.rb +13 -0
  23. data/lib/dtn/lookups/historical/daily_timeframe.rb +34 -0
  24. data/lib/dtn/lookups/historical/datapoint.rb +27 -0
  25. data/lib/dtn/lookups/historical/interval.rb +14 -0
  26. data/lib/dtn/lookups/historical/interval_datapoint.rb +31 -0
  27. data/lib/dtn/lookups/historical/interval_day.rb +32 -0
  28. data/lib/dtn/lookups/historical/interval_timeframe.rb +37 -0
  29. data/lib/dtn/lookups/historical/monthly_datapoint.rb +13 -0
  30. data/lib/dtn/lookups/historical/tick.rb +14 -0
  31. data/lib/dtn/lookups/historical/tick_datapoint.rb +24 -0
  32. data/lib/dtn/lookups/historical/tick_day.rb +34 -0
  33. data/lib/dtn/lookups/historical/tick_timeframe.rb +31 -0
  34. data/lib/dtn/lookups/historical/weekly_datapoint.rb +13 -0
  35. data/lib/dtn/lookups/news/base.rb +85 -0
  36. data/lib/dtn/lookups/news/config.rb +25 -0
  37. data/lib/dtn/lookups/news/headline.rb +40 -0
  38. data/lib/dtn/lookups/news/story.rb +40 -0
  39. data/lib/dtn/lookups/news/story_count.rb +36 -0
  40. data/lib/dtn/lookups/request.rb +92 -0
  41. data/lib/dtn/lookups/symbol/base.rb +11 -0
  42. data/lib/dtn/lookups/symbol/by_filter.rb +58 -0
  43. data/lib/dtn/lookups/symbol/by_naic.rb +26 -0
  44. data/lib/dtn/lookups/symbol/by_sic.rb +26 -0
  45. data/lib/dtn/message.rb +29 -0
  46. data/lib/dtn/messages/bar/base.rb +30 -0
  47. data/lib/dtn/messages/bar/current_bar.rb +11 -0
  48. data/lib/dtn/messages/bar/historical_bar.rb +11 -0
  49. data/lib/dtn/messages/bar/update_bar.rb +11 -0
  50. data/lib/dtn/messages/catalog/code.rb +22 -0
  51. data/lib/dtn/messages/catalog/listed_markets.rb +20 -0
  52. data/lib/dtn/messages/catalog/naic_codes.rb +10 -0
  53. data/lib/dtn/messages/catalog/security_types.rb +20 -0
  54. data/lib/dtn/messages/catalog/sic_codes.rb +10 -0
  55. data/lib/dtn/messages/catalog/trade_conditions.rb +20 -0
  56. data/lib/dtn/messages/historical/daily_weekly_monthly.rb +25 -0
  57. data/lib/dtn/messages/historical/interval.rb +28 -0
  58. data/lib/dtn/messages/historical/tick.rb +31 -0
  59. data/lib/dtn/messages/level2/level2_update.rb +38 -0
  60. data/lib/dtn/messages/level2/market_maker_name.rb +20 -0
  61. data/lib/dtn/messages/message_with_simple_parser.rb +38 -0
  62. data/lib/dtn/messages/news/base.rb +34 -0
  63. data/lib/dtn/messages/news/config.rb +24 -0
  64. data/lib/dtn/messages/news/headline.rb +25 -0
  65. data/lib/dtn/messages/news/story.rb +20 -0
  66. data/lib/dtn/messages/news/story_count.rb +21 -0
  67. data/lib/dtn/messages/quote/level1.rb +150 -0
  68. data/lib/dtn/messages/quote/level1_fundamental.rb +17 -0
  69. data/lib/dtn/messages/quote/level1_news.rb +27 -0
  70. data/lib/dtn/messages/quote/level1_regional.rb +31 -0
  71. data/lib/dtn/messages/quote/level1_summary.rb +19 -0
  72. data/lib/dtn/messages/quote/level1_update.rb +21 -0
  73. data/lib/dtn/messages/symbol/base.rb +35 -0
  74. data/lib/dtn/messages/symbol/by_filter.rb +11 -0
  75. data/lib/dtn/messages/symbol/by_naic.rb +22 -0
  76. data/lib/dtn/messages/symbol/by_sic.rb +22 -0
  77. data/lib/dtn/messages/system/client_stats.rb +46 -0
  78. data/lib/dtn/messages/system/customer_info.rb +30 -0
  79. data/lib/dtn/messages/system/end_of_message_characters.rb +22 -0
  80. data/lib/dtn/messages/system/error.rb +20 -0
  81. data/lib/dtn/messages/system/generic.rb +98 -0
  82. data/lib/dtn/messages/system/no_data_characters.rb +22 -0
  83. data/lib/dtn/messages/system/stats.rb +38 -0
  84. data/lib/dtn/messages/system/symbol_not_found.rb +19 -0
  85. data/lib/dtn/messages/system/timestamp.rb +16 -0
  86. data/lib/dtn/messages/unknown.rb +15 -0
  87. data/lib/dtn/registry.rb +57 -0
  88. data/lib/dtn/streaming/client.rb +105 -0
  89. data/lib/dtn/streaming/clients/admin.rb +20 -0
  90. data/lib/dtn/streaming/clients/bar.rb +49 -0
  91. data/lib/dtn/streaming/clients/level2.rb +25 -0
  92. data/lib/dtn/streaming/clients/quote.rb +57 -0
  93. data/lib/dtn/streaming/messages_recorder_observer.rb +26 -0
  94. data/lib/dtn/streaming/request.rb +27 -0
  95. data/lib/dtn/streaming/request_builder.rb +57 -0
  96. data/lib/dtn/streaming/requests/admin/register_client_app.rb +24 -0
  97. data/lib/dtn/streaming/requests/admin/remove_client_app.rb +22 -0
  98. data/lib/dtn/streaming/requests/admin/save_login_info.rb +18 -0
  99. data/lib/dtn/streaming/requests/admin/set_autoconnect.rb +18 -0
  100. data/lib/dtn/streaming/requests/admin/set_client_stats.rb +20 -0
  101. data/lib/dtn/streaming/requests/admin/set_loginid.rb +21 -0
  102. data/lib/dtn/streaming/requests/admin/set_password.rb +21 -0
  103. data/lib/dtn/streaming/requests/bar/unwatch.rb +18 -0
  104. data/lib/dtn/streaming/requests/bar/unwatch_all.rb +16 -0
  105. data/lib/dtn/streaming/requests/bar/watch.rb +81 -0
  106. data/lib/dtn/streaming/requests/bar/watches.rb +21 -0
  107. data/lib/dtn/streaming/requests/level2/connect.rb +16 -0
  108. data/lib/dtn/streaming/requests/level2/disconnect.rb +16 -0
  109. data/lib/dtn/streaming/requests/level2/market_maker_by_id.rb +18 -0
  110. data/lib/dtn/streaming/requests/level2/unwatch.rb +18 -0
  111. data/lib/dtn/streaming/requests/level2/watch.rb +18 -0
  112. data/lib/dtn/streaming/requests/quote/all_update_fieldnames.rb +16 -0
  113. data/lib/dtn/streaming/requests/quote/connect.rb +16 -0
  114. data/lib/dtn/streaming/requests/quote/current_update_fieldnames.rb +16 -0
  115. data/lib/dtn/streaming/requests/quote/fundamental_fieldnames.rb +16 -0
  116. data/lib/dtn/streaming/requests/quote/news_switch.rb +18 -0
  117. data/lib/dtn/streaming/requests/quote/refresh.rb +29 -0
  118. data/lib/dtn/streaming/requests/quote/regional_switch.rb +26 -0
  119. data/lib/dtn/streaming/requests/quote/set_client_name.rb +16 -0
  120. data/lib/dtn/streaming/requests/quote/set_protocol.rb +16 -0
  121. data/lib/dtn/streaming/requests/quote/timestamp.rb +21 -0
  122. data/lib/dtn/streaming/requests/quote/timestamp_switch.rb +18 -0
  123. data/lib/dtn/streaming/requests/quote/trades.rb +21 -0
  124. data/lib/dtn/streaming/requests/quote/unwatch.rb +22 -0
  125. data/lib/dtn/streaming/requests/quote/unwatch_all.rb +16 -0
  126. data/lib/dtn/streaming/requests/quote/update_fields.rb +40 -0
  127. data/lib/dtn/streaming/requests/quote/watch.rb +22 -0
  128. data/lib/dtn/streaming/requests/quote/watches.rb +21 -0
  129. data/lib/dtn/version.rb +1 -1
  130. data/lib/ext/business_day.rb +15 -0
  131. data/lib/tasks/spec_date.rake +13 -0
  132. metadata +126 -6
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ # Request abstraction
6
+ class Request
7
+ def initialize(client:)
8
+ @client = client
9
+ end
10
+
11
+ # Initialize the request to api, should be used in children classes only
12
+ #
13
+ # @returns nil
14
+ def call(*)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ private
19
+
20
+ def socket
21
+ client.socket
22
+ end
23
+
24
+ attr_reader :client
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ # Request builder abstraction
6
+ class RequestBuilder
7
+ # Proxy class between builder and request
8
+ class Proxy
9
+ def initialize(client:, requests_module:)
10
+ @client = client
11
+ @requests_module = requests_module
12
+ end
13
+
14
+ def method_missing(method_name, *args, **opts, &blk)
15
+ req = requests_module.const_get(method_name.to_s.camelize).new(client: client)
16
+ req.call(*args, **opts, &blk)
17
+ rescue NameError
18
+ super
19
+ end
20
+
21
+ def respond_to_missing?(method_name, include_private = false)
22
+ requests_module.const_get(method_name.to_s.camelize).is_a? Module
23
+ rescue NameError
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :client, :requests_module
30
+ end
31
+
32
+ def initialize(client:)
33
+ @client = client
34
+ end
35
+
36
+ def method_missing(method_name, *_args, **_opts, &_blk)
37
+ Proxy.new(client: client, requests_module: _requests_module(method_name))
38
+ rescue NameError
39
+ super
40
+ end
41
+
42
+ def respond_to_missing?(method_name, include_private = false)
43
+ _requests_module(method_name).is_a? Module
44
+ rescue NameError
45
+ super
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :client
51
+
52
+ def _requests_module(method_name)
53
+ "Dtn::Streaming::Requests::#{method_name.to_s.camelize}".constantize
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Register your application with IQFeed.
8
+ #
9
+ # :param product: Developer/Product token from DTN.
10
+ #
11
+ # IQFeed requires all developers to get a developer/app token from
12
+ # them before you develop using IQFeed. You can register you developer
13
+ # token either using the command line used to start IQFeed or here.
14
+ # IQFeed will not connect to DTN servers or send you any data without
15
+ # a developer token.
16
+ class RegisterClientApp < Request
17
+ def call(product:)
18
+ socket.puts "S,REGISTER CLIENT APP,#{product}\r\n"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Unregister your application with IQFeed.
8
+ #
9
+ # :param product: Developer/Product token from DTN.
10
+ #
11
+ # Unregister your developer token. Apps from other developers may still
12
+ # be talking to IQFeed. This tells IQFeed that any further calls are
13
+ # from them and not you.
14
+ class RemoveClientApp < Request
15
+ def call(product:)
16
+ socket.puts "S,REMOVE CLIENT APP,#{product}\r\n"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Tells IQFeed to save the login info
8
+ #
9
+ # :param turned_on: True means save info, False means don't
10
+ class SaveLoginInfo < Request
11
+ def call(turned_on: true, **)
12
+ socket.puts "S,SET SAVE LOGIN INFO,#{turned_on ? "On" : "Off"}\r\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Tells IQFeed to autoconnect to DTN servers on startup or not
8
+ #
9
+ # :param turned_on: True means autoconnect, False means don't
10
+ class SetAutoconnect < Request
11
+ def call(turned_on: true, **)
12
+ socket.puts "S,SET AUTOCONNECT,#{turned_on ? "On" : "Off"}\r\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Request client statistics from IQFeed.
8
+ #
9
+ # Call this is you want IQFeed to send you a message every second about
10
+ # the status of every connection. Lets you know things like number of
11
+ # subscriptions and if the connection is buffering.
12
+ class SetClientStats < Request
13
+ def call(turned_on: true, **)
14
+ socket.puts "S,CLIENTSTATS #{turned_on ? "ON" : "OFF"}\r\n"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Set the user login.
8
+ #
9
+ # :param login: IQFeed subscriber's login
10
+ #
11
+ # The user of the app must have a subscription to IQFeed. Pass the user's
12
+ # login either here or in cmd line options when IQFeed is started.
13
+ class SetLoginid < Request
14
+ def call(loginid:)
15
+ socket.puts "S,SET LOGINID,#{loginid}\r\n"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Admin
7
+ # Set the user password.
8
+ #
9
+ # :param password: IQFeed subscriber's pwd
10
+ #
11
+ # The user of the app must have a subscription to IQFeed. Pass the user's
12
+ # password either here or in cmd line options when IQFeed is started.
13
+ class SetPassword < Request
14
+ def call(password:)
15
+ socket.puts "S,SET PASSWORD,#{password}\r\n"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Bar
7
+ # Stop watching a symbol.
8
+ #
9
+ # :param symbol: A valid symbol for a security or derivative.
10
+ class Unwatch < Request
11
+ def call(symbol:)
12
+ socket.puts "BR,#{symbol.upcase}\r\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Bar
7
+ # Unwatch all symbols.
8
+ class UnwatchAll < Request
9
+ def call
10
+ socket.puts "S,UNWATCH ALL\r\n"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Bar
7
+ # Request live interval (bar) data.
8
+ #
9
+ # :param symbol: Symbol for which you are requesting data.
10
+ # :param interval: Interval length in interval_type units (can be fractal)
11
+ # :param interval_type: 's' = secs, 'v' = volume, 't' = ticks
12
+ # :param begin_filter_time: Earliest time of day for which you want data
13
+ # :param end_filter_time: Latest time of day for which you want data
14
+ # :param update_interval: Update the current bar every update secs.
15
+ # :param begin_datetime: Get back-fill bars starting at begin_datetime
16
+ # :param lookback_days: Get lookback_days of backfill data
17
+ # :param lookback_bars: Get lookback_bars of backfill data
18
+ #
19
+ # Only one of begin_datetime, lookback_days or lookback_bars should be set.
20
+ #
21
+ # Requests live interval data. You can also request some backfill data.
22
+ # When you call this function:
23
+ # 1) Client will return HistoricalBar messages with bars
24
+ # that go back either a) upto begin_datetime, b) upto lookback_days or
25
+ # c) upto lookback_bars.
26
+ # 2) The callback on observer update_bar is called on every update
27
+ # to data for the current live bar.
28
+ # 3) The callback current_bar is called every time we cross an
29
+ # interval boundary with data for the now complete bar.
30
+ class Watch < Request
31
+ TEMPLATE =
32
+ "BW,%<symbol>s,%<interval>d,%<begin_datetime>s,%<lookback_days>s," \
33
+ "%<lookback_bars>s,%<begin_filter_time>s,%<end_filter_time>s,%<id>s," \
34
+ "%<interval_type>s,'',%<update_interval>d\r\n"
35
+
36
+ include Dtn::Concerns::Validation
37
+ include Dtn::Concerns::Id
38
+
39
+ DEFAULT_BEGIN_FILTER_TIME = "093000"
40
+ DEFAULT_END_FILTER_TIME = "160000"
41
+
42
+ LOOKBACK_OPTIONS = %i[begin_datetime lookback_days lookback_bars].freeze
43
+
44
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
45
+ def call(symbol:, interval: 60, update_interval: 5, **options)
46
+ options.merge!({
47
+ symbol: symbol.to_s.upcase,
48
+ interval: validate_int(interval),
49
+ begin_filter_time: options[:begin_filter_time] || DEFAULT_BEGIN_FILTER_TIME,
50
+ end_filter_time: options[:end_filter_time] || DEFAULT_END_FILTER_TIME,
51
+ interval_type: validate_interval_type(options[:interval_type]),
52
+ update_interval: validate_int(update_interval),
53
+ id: id,
54
+ begin_datetime: options[:begin_datetime],
55
+ lookback_days: options[:lookback_days],
56
+ lookback_bars: options[:lookback_bars]
57
+ }).tap { |o| validate_lookback_options(**o) }
58
+
59
+ socket.print format(TEMPLATE, options)
60
+ end
61
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
62
+
63
+ private
64
+
65
+ def validate_lookback_options(**value)
66
+ v = value.slice(*LOOKBACK_OPTIONS)
67
+
68
+ return if [
69
+ [false, false, false],
70
+ [true, false, false],
71
+ [false, true, false],
72
+ [false, false, true]
73
+ ].include?(v.values.map(&:present?))
74
+
75
+ raise ValidationError, "Only one of '#{LOOKBACK_OPTIONS.join(", ")}' should be entered, but got #{v}"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Bar
7
+ # Request a current watches message.
8
+ #
9
+ # IQFeed.exe will send you a list of all securities currently watched
10
+ #
11
+ # process_watched_symbols is called in each listener when the list of
12
+ # current watches message is received.
13
+ class Watches < Request
14
+ def call
15
+ socket.puts "S,REQUEST WATCHES\r\n"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Level2
7
+ # Connect.
8
+ class Connect < Request
9
+ def call
10
+ socket.puts "c\r\n"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Level2
7
+ # Disconnect.
8
+ class Disconnect < Request
9
+ def call
10
+ socket.puts "x\r\n"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Level2
7
+ # Get MarketMakerById
8
+ #
9
+ # :param mmid: A valid MMID.
10
+ class MarketMakerById < Request
11
+ def call(mmid:)
12
+ socket.puts "m#{mmid.upcase}\r\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Streaming
5
+ module Requests
6
+ module Level2
7
+ # Stop watching a symbol.
8
+ #
9
+ # :param symbol: A valid symbol for a security or derivative.
10
+ class Unwatch < Request
11
+ def call(symbol:)
12
+ socket.puts "r#{symbol.upcase}\r\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end