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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Quote
6
+ # Streaming level1 fundamental data.
7
+ # Always return full dataset
8
+ class Level1Fundamental < MessageWithSimpleParser
9
+ class << self
10
+ def fields
11
+ @fields ||= Level1::ALL_FUNDAMENTAL_FIELDS
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Quote
6
+ # Streaming level1 news update.
7
+ class Level1News < MessageWithSimpleParser
8
+ class << self
9
+ def fields
10
+ @fields ||= {
11
+ _skip: :nil,
12
+ distributor: :to_s,
13
+ story_id: :to_i,
14
+ symbols_list: :to_s,
15
+ story_datetime: :to_datetime,
16
+ headline: :to_s
17
+ }
18
+ end
19
+ end
20
+
21
+ def after_initialization
22
+ self.symbols_list = symbols_list.to_s.split(":").delete_if(&:blank?).uniq
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Quote
6
+ # Streaming level1 regional update.
7
+ class Level1Regional < MessageWithSimpleParser
8
+ class << self
9
+ # rubocop:disable Metrics/MethodLength
10
+ def fields
11
+ @fields ||= {
12
+ _skip: :nil,
13
+ "Symbol" => :to_s,
14
+ _skip2: :to_s,
15
+ "Regional Bid" => :to_f,
16
+ "Regional BidSize" => :to_i,
17
+ "Regional BidTime" => :to_time,
18
+ "Regional Ask" => :to_f,
19
+ "Regional AskSize" => :to_i,
20
+ "Regional AskTime" => :to_time,
21
+ "Fraction Display Code" => :to_i,
22
+ "Decimal Precision" => :to_i,
23
+ "Market Center" => :to_i
24
+ }
25
+ end
26
+ # rubocop:enable Metrics/MethodLength
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Quote
6
+ # Streaming level1 dynamic data.
7
+ # Always return full dataset
8
+ class Level1Summary < MessageWithSimpleParser
9
+ class << self
10
+ def parse(line:, **)
11
+ new.tap do |n|
12
+ apply_values instance: n, attributes: Level1::ALL_FIELDS, values: line.split(",")
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Quote
6
+ # Streaming level1 dynamic data
7
+ # This message can not be received outside of trading hours
8
+ class Level1Update < MessageWithSimpleParser
9
+ class << self
10
+ def parse(line:, client:)
11
+ new.tap do |n|
12
+ apply_values instance: n,
13
+ attributes: { _skip: nil }.merge(Level1::ALL_FIELDS.slice(*client.quote_update_fields)),
14
+ values: line.split(",")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Symbol
6
+ # Base lookup
7
+ class Base < MessageWithSimpleParser
8
+ class << self
9
+ def fields
10
+ @fields ||= {
11
+ request_id: :to_i,
12
+ code_id => :to_i,
13
+ symbol: :to_s,
14
+ listed_market_id: :to_i,
15
+ security_type_id: :to_i,
16
+ description: :to_s
17
+ }.delete_if { |k, _| !k }.freeze
18
+ end
19
+
20
+ private
21
+
22
+ def code_id; end
23
+ end
24
+
25
+ def listed_market
26
+ Dtn.listed_markets_catalog[listed_market_id]
27
+ end
28
+
29
+ def security_type
30
+ Dtn.security_types_catalog[security_type_id]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Symbol
6
+ # By Filter code
7
+ class ByFilter < Base
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Symbol
6
+ # By NAIC code
7
+ class ByNaic < Base
8
+ class << self
9
+ private
10
+
11
+ def code_id
12
+ :naic_code_id
13
+ end
14
+ end
15
+
16
+ def naic_code
17
+ Dtn.naic_codes_catalog[naic_code_id]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module Symbol
6
+ # By SIC code
7
+ class BySic < Base
8
+ class << self
9
+ private
10
+
11
+ def code_id
12
+ :sic_code_id
13
+ end
14
+ end
15
+
16
+ def sic_code
17
+ Dtn.sic_codes_catalog[sic_code_id]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module System
6
+ # Full client statistics
7
+ class ClientStats < MessageWithSimpleParser
8
+ class << self
9
+ # rubocop:disable Metrics/MethodLength
10
+ def fields
11
+ @fields ||= {
12
+ _skip1: nil,
13
+ _skip2: nil,
14
+ type_int: :to_i,
15
+ client_id: :to_i,
16
+ client_name: :to_s,
17
+ timestamp: :to_s,
18
+ num_sym: :to_i,
19
+ num_reg_sym: :to_i,
20
+ kb_sent: :to_f,
21
+ kb_recvd: :to_f,
22
+ kb_queued: :to_f
23
+ }
24
+ end
25
+ # rubocop:enable Metrics/MethodLength
26
+ end
27
+
28
+ def after_initialization
29
+ normalize_type
30
+ end
31
+
32
+ private
33
+
34
+ def normalize_type
35
+ self.type = case type_int
36
+ when 0 then :admin
37
+ when 1 then :quote
38
+ when 2 then :bar
39
+ when 3 then :lookup
40
+ else :unknown
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module System
6
+ # Handle a customer information message.
7
+ class CustomerInfo < MessageWithSimpleParser
8
+ class << self
9
+ # rubocop:disable Metrics/MethodLength
10
+ def fields
11
+ @fields ||= {
12
+ svc_type: :to_s,
13
+ ip_address: :to_s,
14
+ port: :to_i,
15
+ token: :to_s,
16
+ version: :to_s,
17
+ _unknown1: :to_s,
18
+ rt_exchanges: :to_s,
19
+ _unknown2: :to_s,
20
+ max_symbols: :to_i,
21
+ flags: :to_s,
22
+ trial_ends: :to_s
23
+ }
24
+ end
25
+ # rubocop:enable Metrics/MethodLength
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module System
6
+ # End of the message abstraction.
7
+ class EndOfMessageCharacters < MessageWithSimpleParser
8
+ class << self
9
+ def fields
10
+ @fields ||= {
11
+ request_id: :to_i
12
+ }
13
+ end
14
+ end
15
+
16
+ def termination?
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module System
6
+ # Error message abstraction.
7
+ class Error < Message
8
+ class << self
9
+ def parse(line:, **)
10
+ raise "DTN Error: #{line}"
11
+ end
12
+ end
13
+
14
+ def termination?
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dtn
4
+ module Messages
5
+ module System
6
+ # Atm we return all system messages class or
7
+ # give it bask as a plain text if it does not know it
8
+ class Generic < Message
9
+ class Obsolete < Message; end
10
+
11
+ class Connected < Message; end
12
+
13
+ class Disconnected < Message; end
14
+
15
+ class ReconnectionFailed < Message; end
16
+
17
+ class SymbolLimitReached < Message; end
18
+
19
+ class ReplacedPreviousWatch < Message; end
20
+
21
+ class RegisterClientApp < Message; end
22
+
23
+ class RemoveClientApp < Message; end
24
+
25
+ class CurrentLoginid < Message; end
26
+
27
+ class CurrentPassword < Message; end
28
+
29
+ class LoginInfoSaved < Message; end
30
+
31
+ class LoginInfoNotSaved < Message; end
32
+
33
+ class AutoconnectOn < Message; end
34
+
35
+ class AutoconnectOff < Message; end
36
+
37
+ # Gem will support only one protocol version at the time
38
+ class CurrentProtocol < Message
39
+ class << self
40
+ def parse(**)
41
+ super.tap { |message| (message.line =~ /6.1/) || raise("Unsupported protocol: #{message}") }
42
+ end
43
+ end
44
+ end
45
+
46
+ class Ip < ParseListFromMessage; end
47
+
48
+ class FundamentalFieldnames < ParseListFromMessage; end
49
+
50
+ class AllUpdateFieldnames < ParseListFromMessage; end
51
+
52
+ # With this return we also updating our vision of the current fields
53
+ class CurrentUpdateFieldnames < ParseListFromMessage
54
+ class << self
55
+ def parse(client:, **)
56
+ super.tap { |message| client.quote_update_fields = message.list }
57
+ end
58
+ end
59
+ end
60
+
61
+ class Watches < ParseListFromMessage; end
62
+
63
+ class << self
64
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
65
+ def parse(line:, client:, **)
66
+ case line
67
+ when /^S,KEY/ then Obsolete.new
68
+ when /^S,SERVER CONNECTED/ then Connected.new
69
+ when /^S,SERVER DISCONNECTED/ then Disconnected.new
70
+ when /^S,SERVER RECONNECT FAILED/ then ReconnectionFailed.new
71
+ when /^S,SYMBOL LIMIT REACHED/ then SymbolLimitReached.new
72
+ when /^S,CURRENT PROTOCOL,/ then CurrentProtocol.parse(line: line[19..])
73
+ when /^S,IP,/ then Ip.parse(line: line[5..])
74
+ when /^S,CUST,/ then CustomerInfo.parse(line: line[7..])
75
+ when /^S,FUNDAMENTAL FIELDNAMES,/ then FundamentalFieldnames.parse(line: line[25..])
76
+ when /^S,UPDATE FIELDNAMES,/ then AllUpdateFieldnames.parse(line: line[20..])
77
+ when /^S,CURRENT UPDATE FIELDNAMES,/ then CurrentUpdateFieldnames.parse(line: line[28..], client: client)
78
+ when /^S,WATCHES/ then Watches.parse(line: line[10..])
79
+ when /^S,REPLACED PREVIOUS WATCH/ then ReplacedPreviousWatch.parse(line: line[25..])
80
+ when /^S,REGISTER CLIENT APP COMPLETED/ then RegisterClientApp.parse(line: line[31..])
81
+ when /^S,REMOVE CLIENT APP COMPLETED/ then RemoveClientApp.parse(line: line[29..])
82
+ when /^S,CURRENT LOGINID/ then CurrentLoginid.parse(line: line[17..])
83
+ when /^S,CURRENT PASSWORD/ then CurrentPassword.parse(line: line[18..])
84
+ when /^S,LOGIN INFO SAVED/ then LoginInfoSaved.parse(line: line[18..])
85
+ when /^S,LOGIN INFO NOT SAVED/ then LoginInfoNotSaved.parse(line: line[22..])
86
+ when /^S,AUTOCONNECT ON/ then AutoconnectOn.parse(line: line[16..])
87
+ when /^S,AUTOCONNECT OFF/ then AutoconnectOff.parse(line: line[17..])
88
+ when /^S,CLIENTSTATS/ then ClientStats.parse(line: line[13..])
89
+ when /^S,STATS/ then Stats.parse(line: line[8..])
90
+ else new(line: line[2..])
91
+ end
92
+ end
93
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end