dtn 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +21 -5
- data/.env.example +5 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +5 -1
- data/Gemfile +3 -1
- data/README.md +288 -9
- data/Rakefile +2 -0
- data/docker-compose.yml +34 -0
- data/dtn.gemspec +1 -1
- data/lib/dtn.rb +18 -1
- data/lib/dtn/concerns/id.rb +36 -0
- data/lib/dtn/concerns/validation.rb +71 -0
- data/lib/dtn/helpers/catalog.rb +32 -0
- data/lib/dtn/lookups/catalog/listed_markets.rb +12 -0
- data/lib/dtn/lookups/catalog/naic_codes.rb +12 -0
- data/lib/dtn/lookups/catalog/security_types.rb +12 -0
- data/lib/dtn/lookups/catalog/sic_codes.rb +12 -0
- data/lib/dtn/lookups/catalog/trade_conditions.rb +12 -0
- data/lib/dtn/lookups/historical/base.rb +43 -0
- data/lib/dtn/lookups/historical/daily_datapoint.rb +13 -0
- data/lib/dtn/lookups/historical/daily_timeframe.rb +34 -0
- data/lib/dtn/lookups/historical/datapoint.rb +27 -0
- data/lib/dtn/lookups/historical/interval.rb +14 -0
- data/lib/dtn/lookups/historical/interval_datapoint.rb +31 -0
- data/lib/dtn/lookups/historical/interval_day.rb +32 -0
- data/lib/dtn/lookups/historical/interval_timeframe.rb +37 -0
- data/lib/dtn/lookups/historical/monthly_datapoint.rb +13 -0
- data/lib/dtn/lookups/historical/tick.rb +14 -0
- data/lib/dtn/lookups/historical/tick_datapoint.rb +24 -0
- data/lib/dtn/lookups/historical/tick_day.rb +34 -0
- data/lib/dtn/lookups/historical/tick_timeframe.rb +31 -0
- data/lib/dtn/lookups/historical/weekly_datapoint.rb +13 -0
- data/lib/dtn/lookups/news/base.rb +85 -0
- data/lib/dtn/lookups/news/config.rb +25 -0
- data/lib/dtn/lookups/news/headline.rb +40 -0
- data/lib/dtn/lookups/news/story.rb +40 -0
- data/lib/dtn/lookups/news/story_count.rb +36 -0
- data/lib/dtn/lookups/request.rb +92 -0
- data/lib/dtn/lookups/symbol/base.rb +11 -0
- data/lib/dtn/lookups/symbol/by_filter.rb +58 -0
- data/lib/dtn/lookups/symbol/by_naic.rb +26 -0
- data/lib/dtn/lookups/symbol/by_sic.rb +26 -0
- data/lib/dtn/message.rb +29 -0
- data/lib/dtn/messages/bar/base.rb +30 -0
- data/lib/dtn/messages/bar/current_bar.rb +11 -0
- data/lib/dtn/messages/bar/historical_bar.rb +11 -0
- data/lib/dtn/messages/bar/update_bar.rb +11 -0
- data/lib/dtn/messages/catalog/code.rb +22 -0
- data/lib/dtn/messages/catalog/listed_markets.rb +20 -0
- data/lib/dtn/messages/catalog/naic_codes.rb +10 -0
- data/lib/dtn/messages/catalog/security_types.rb +20 -0
- data/lib/dtn/messages/catalog/sic_codes.rb +10 -0
- data/lib/dtn/messages/catalog/trade_conditions.rb +20 -0
- data/lib/dtn/messages/historical/daily_weekly_monthly.rb +25 -0
- data/lib/dtn/messages/historical/interval.rb +28 -0
- data/lib/dtn/messages/historical/tick.rb +31 -0
- data/lib/dtn/messages/level2/level2_update.rb +38 -0
- data/lib/dtn/messages/level2/market_maker_name.rb +20 -0
- data/lib/dtn/messages/message_with_simple_parser.rb +38 -0
- data/lib/dtn/messages/news/base.rb +34 -0
- data/lib/dtn/messages/news/config.rb +24 -0
- data/lib/dtn/messages/news/headline.rb +25 -0
- data/lib/dtn/messages/news/story.rb +20 -0
- data/lib/dtn/messages/news/story_count.rb +21 -0
- data/lib/dtn/messages/quote/level1.rb +150 -0
- data/lib/dtn/messages/quote/level1_fundamental.rb +17 -0
- data/lib/dtn/messages/quote/level1_news.rb +27 -0
- data/lib/dtn/messages/quote/level1_regional.rb +31 -0
- data/lib/dtn/messages/quote/level1_summary.rb +19 -0
- data/lib/dtn/messages/quote/level1_update.rb +21 -0
- data/lib/dtn/messages/symbol/base.rb +35 -0
- data/lib/dtn/messages/symbol/by_filter.rb +11 -0
- data/lib/dtn/messages/symbol/by_naic.rb +22 -0
- data/lib/dtn/messages/symbol/by_sic.rb +22 -0
- data/lib/dtn/messages/system/client_stats.rb +46 -0
- data/lib/dtn/messages/system/customer_info.rb +30 -0
- data/lib/dtn/messages/system/end_of_message_characters.rb +22 -0
- data/lib/dtn/messages/system/error.rb +20 -0
- data/lib/dtn/messages/system/generic.rb +98 -0
- data/lib/dtn/messages/system/no_data_characters.rb +22 -0
- data/lib/dtn/messages/system/stats.rb +38 -0
- data/lib/dtn/messages/system/symbol_not_found.rb +19 -0
- data/lib/dtn/messages/system/timestamp.rb +16 -0
- data/lib/dtn/messages/unknown.rb +15 -0
- data/lib/dtn/registry.rb +57 -0
- data/lib/dtn/streaming/client.rb +105 -0
- data/lib/dtn/streaming/clients/admin.rb +20 -0
- data/lib/dtn/streaming/clients/bar.rb +49 -0
- data/lib/dtn/streaming/clients/level2.rb +25 -0
- data/lib/dtn/streaming/clients/quote.rb +57 -0
- data/lib/dtn/streaming/messages_recorder_observer.rb +26 -0
- data/lib/dtn/streaming/request.rb +27 -0
- data/lib/dtn/streaming/request_builder.rb +57 -0
- data/lib/dtn/streaming/requests/admin/register_client_app.rb +24 -0
- data/lib/dtn/streaming/requests/admin/remove_client_app.rb +22 -0
- data/lib/dtn/streaming/requests/admin/save_login_info.rb +18 -0
- data/lib/dtn/streaming/requests/admin/set_autoconnect.rb +18 -0
- data/lib/dtn/streaming/requests/admin/set_client_stats.rb +20 -0
- data/lib/dtn/streaming/requests/admin/set_loginid.rb +21 -0
- data/lib/dtn/streaming/requests/admin/set_password.rb +21 -0
- data/lib/dtn/streaming/requests/bar/unwatch.rb +18 -0
- data/lib/dtn/streaming/requests/bar/unwatch_all.rb +16 -0
- data/lib/dtn/streaming/requests/bar/watch.rb +81 -0
- data/lib/dtn/streaming/requests/bar/watches.rb +21 -0
- data/lib/dtn/streaming/requests/level2/connect.rb +16 -0
- data/lib/dtn/streaming/requests/level2/disconnect.rb +16 -0
- data/lib/dtn/streaming/requests/level2/market_maker_by_id.rb +18 -0
- data/lib/dtn/streaming/requests/level2/unwatch.rb +18 -0
- data/lib/dtn/streaming/requests/level2/watch.rb +18 -0
- data/lib/dtn/streaming/requests/quote/all_update_fieldnames.rb +16 -0
- data/lib/dtn/streaming/requests/quote/connect.rb +16 -0
- data/lib/dtn/streaming/requests/quote/current_update_fieldnames.rb +16 -0
- data/lib/dtn/streaming/requests/quote/fundamental_fieldnames.rb +16 -0
- data/lib/dtn/streaming/requests/quote/news_switch.rb +18 -0
- data/lib/dtn/streaming/requests/quote/refresh.rb +29 -0
- data/lib/dtn/streaming/requests/quote/regional_switch.rb +26 -0
- data/lib/dtn/streaming/requests/quote/set_client_name.rb +16 -0
- data/lib/dtn/streaming/requests/quote/set_protocol.rb +16 -0
- data/lib/dtn/streaming/requests/quote/timestamp.rb +21 -0
- data/lib/dtn/streaming/requests/quote/timestamp_switch.rb +18 -0
- data/lib/dtn/streaming/requests/quote/trades.rb +21 -0
- data/lib/dtn/streaming/requests/quote/unwatch.rb +22 -0
- data/lib/dtn/streaming/requests/quote/unwatch_all.rb +16 -0
- data/lib/dtn/streaming/requests/quote/update_fields.rb +40 -0
- data/lib/dtn/streaming/requests/quote/watch.rb +22 -0
- data/lib/dtn/streaming/requests/quote/watches.rb +21 -0
- data/lib/dtn/version.rb +1 -1
- data/lib/ext/business_day.rb +15 -0
- data/lib/tasks/spec_date.rake +13 -0
- metadata +126 -6
data/Rakefile
CHANGED
data/docker-compose.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
version: "3.9"
|
2
|
+
services:
|
3
|
+
iqfeed:
|
4
|
+
image: kvokka/iqfeed
|
5
|
+
container_name: iqfeed
|
6
|
+
ports:
|
7
|
+
- "5009:5009"
|
8
|
+
- "9100:9100"
|
9
|
+
- "9200:9200"
|
10
|
+
- "9300:9300"
|
11
|
+
- "9400:9400"
|
12
|
+
- "8088:8080"
|
13
|
+
- "5901:5900"
|
14
|
+
volumes:
|
15
|
+
- ./logs/iqfeed:/root/DTN/IQFeed
|
16
|
+
- ./logs/distdnnd:/var/log/distdnnd
|
17
|
+
healthcheck:
|
18
|
+
test: ["CMD", "python3", "/root/is_iqfeed_running.py"]
|
19
|
+
interval: 30s
|
20
|
+
timeout: 15s
|
21
|
+
retries: 3
|
22
|
+
start_period: 1m
|
23
|
+
env_file:
|
24
|
+
- .env
|
25
|
+
|
26
|
+
autoheal:
|
27
|
+
image: willfarrell/autoheal:latest
|
28
|
+
container_name: autoheal
|
29
|
+
restart: always
|
30
|
+
environment:
|
31
|
+
AUTOHEAL_CONTAINER_LABEL: all
|
32
|
+
volumes:
|
33
|
+
- /var/run/docker.sock:/var/run/docker.sock
|
34
|
+
hostname: autoheal
|
data/dtn.gemspec
CHANGED
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
-
spec.add_dependency "
|
30
|
+
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
31
31
|
spec.add_dependency "zeitwerk", "~> 2.4.0"
|
32
32
|
end
|
data/lib/dtn.rb
CHANGED
@@ -4,8 +4,25 @@ require "zeitwerk"
|
|
4
4
|
loader = Zeitwerk::Loader.for_gem
|
5
5
|
loader.setup # ready!
|
6
6
|
|
7
|
-
require "
|
7
|
+
require "forwardable"
|
8
|
+
require "concurrent/atomic/atomic_fixnum"
|
9
|
+
require "concurrent/map"
|
10
|
+
require "active_support/concern"
|
11
|
+
require "active_support/inflector"
|
12
|
+
require "active_support/core_ext/string"
|
13
|
+
require "active_support/time_with_zone"
|
14
|
+
require "socket"
|
15
|
+
require "securerandom"
|
16
|
+
require "ostruct"
|
17
|
+
require "date"
|
8
18
|
|
19
|
+
# Top level API methods only
|
9
20
|
module Dtn
|
10
21
|
class Error < StandardError; end
|
22
|
+
|
23
|
+
class ValidationError < Error; end
|
24
|
+
|
25
|
+
extend Helpers::Catalog
|
26
|
+
|
27
|
+
mattr_accessor :host, instance_accessor: false, default: ENV.fetch("DTN_HOST", "localhost")
|
11
28
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Concerns
|
5
|
+
# Id creation and management methods
|
6
|
+
module Id
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
extend Forwardable
|
11
|
+
delegate next_id: :"self.class"
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def next_id
|
16
|
+
_id_tvar.increment
|
17
|
+
last_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def last_id
|
21
|
+
_id_tvar.value
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _id_tvar
|
27
|
+
@_id_tvar ||= Concurrent::AtomicFixnum.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
@id ||= next_id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Concerns
|
5
|
+
# Collection of common validations
|
6
|
+
# This gem too small for ActiveModel, so manual approach should be just ok
|
7
|
+
module Validation
|
8
|
+
# Ruby 2.6 support
|
9
|
+
class ::Date
|
10
|
+
class Error < ArgumentError; end
|
11
|
+
end
|
12
|
+
|
13
|
+
DATE_TIME_FORMAT = "%Y%m%d %H%M%S"
|
14
|
+
DATE_FORMAT = "%Y%m%d"
|
15
|
+
MAX_INT16 = 2**16 - 1
|
16
|
+
|
17
|
+
DEFAULT_INTERVAL_TYPE = "s"
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_interval_type(value)
|
22
|
+
return DEFAULT_INTERVAL_TYPE unless value
|
23
|
+
|
24
|
+
it = value.to_s.downcase
|
25
|
+
return it if %w[s v t].include?(it)
|
26
|
+
|
27
|
+
raise ValidationError,
|
28
|
+
"Got #{value}, but interval_type can be only 's' for seconds, 'v' for volume or 't' for ticks"
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_datetime(value)
|
32
|
+
_general_date_validation(value: value, converter: :to_datetime, format: DATE_TIME_FORMAT)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_date(value)
|
36
|
+
_general_date_validation(value: value, converter: :to_date, format: DATE_FORMAT)
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_short_int(value)
|
40
|
+
validate_int(value).yield_self do |v|
|
41
|
+
v > MAX_INT16 ? MAX_INT16 : v
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_int(value)
|
46
|
+
Integer(value)
|
47
|
+
rescue TypeError, ArgumentError
|
48
|
+
raise ValidationError, "Value '#{value}' is not an integer"
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_float(value)
|
52
|
+
Float(value)
|
53
|
+
rescue TypeError, ArgumentError
|
54
|
+
raise ValidationError, "Value '#{value}' is not a float"
|
55
|
+
end
|
56
|
+
|
57
|
+
def _general_date_validation(value:, converter:, format:)
|
58
|
+
return if value.blank?
|
59
|
+
|
60
|
+
_convert_date(value: value, converter: converter, format: format)
|
61
|
+
rescue ArgumentError
|
62
|
+
raise ValidationError, "Value '#{value}' is not a date"
|
63
|
+
end
|
64
|
+
|
65
|
+
def _convert_date(value:, converter:, format:)
|
66
|
+
base = value.is_a?(Date) ? value : value.to_s.public_send(converter)
|
67
|
+
base.strftime(format)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Helpers
|
5
|
+
# Global catalog helpers
|
6
|
+
module Catalog
|
7
|
+
def method_missing(method_name, *, **)
|
8
|
+
return super unless method_name.to_s.end_with?("_catalog")
|
9
|
+
|
10
|
+
get_catalog(catalog: method_name.to_s.sub(/_catalog$/, ""))
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to_missing?(method_name, include_private = false)
|
14
|
+
method_name.to_s.end_with?("_catalog") || super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def get_catalog(catalog:)
|
20
|
+
v = instance_variable_get("@#{catalog}")
|
21
|
+
return v if v
|
22
|
+
|
23
|
+
result = Registry.new(name: catalog).tap do |registry|
|
24
|
+
"Dtn::Lookups::Catalog::#{catalog.camelcase}".constantize.call.each do |message|
|
25
|
+
registry[message.id] = message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
instance_variable_set("@#{catalog}", result)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Lookups
|
5
|
+
module Historical
|
6
|
+
# User historical requests
|
7
|
+
class Base < Request
|
8
|
+
# Maximum data allowed per 1 request and per 1 batch
|
9
|
+
DEFAULT_MAX_DATAPOINTS = 1_000_000
|
10
|
+
DEFAULT_DATAPOINTS_PER_SEND = 500
|
11
|
+
|
12
|
+
# In case we are fetching the data for a few days we can filter all the days
|
13
|
+
# from and to. The pattern means %H%M%S
|
14
|
+
DEFAULT_BEGIN_FILTER_TIME = "093000"
|
15
|
+
DEFAULT_END_FILTER_TIME = "160000"
|
16
|
+
|
17
|
+
# Returned data order
|
18
|
+
DEFAULT_DATA_DIRECTION = 1
|
19
|
+
|
20
|
+
include Dtn::Concerns::Validation
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def defaults(**options)
|
25
|
+
super.merge({
|
26
|
+
max_datapoints: DEFAULT_MAX_DATAPOINTS,
|
27
|
+
begin_filter_time: DEFAULT_BEGIN_FILTER_TIME,
|
28
|
+
data_direction: DEFAULT_DATA_DIRECTION,
|
29
|
+
end_filter_time: DEFAULT_END_FILTER_TIME,
|
30
|
+
datapoints_per_send: DEFAULT_DATAPOINTS_PER_SEND
|
31
|
+
}).merge(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_symbol(value)
|
35
|
+
v = value.to_s
|
36
|
+
return v.upcase if v.length.positive?
|
37
|
+
|
38
|
+
raise ValidationError, "Symbol must be present"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Lookups
|
5
|
+
module Historical
|
6
|
+
# Daily datapoint requests
|
7
|
+
class DailyDatapoint < Datapoint
|
8
|
+
TEMPLATE =
|
9
|
+
"HDX,%<symbol>s,%<max_datapoints>d,%<data_direction>d,%<id>d,%<datapoints_per_send>d"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Lookups
|
5
|
+
module Historical
|
6
|
+
# Daily timeframe requests
|
7
|
+
class DailyTimeframe < Interval
|
8
|
+
TEMPLATE =
|
9
|
+
"HDT,%<symbol>s,%<begin_date>s,%<end_date>s,%<max_datapoints>d,%<data_direction>d,%<id>d"
|
10
|
+
|
11
|
+
# HDT - Retrieves [Days] days of interval data for the specified [Symbol].
|
12
|
+
#
|
13
|
+
# Example message
|
14
|
+
#
|
15
|
+
# HDT,[Symbol],[BeginDate],[EndDate],[MaxDatapoints],[DataDirection],[RequestID],\
|
16
|
+
# [DatapointsPerSend]<CR><LF>
|
17
|
+
def call(symbol:, begin_date:, end_date:, **options)
|
18
|
+
self.combined_options = defaults(**options).merge(
|
19
|
+
{
|
20
|
+
symbol: validate_symbol(symbol),
|
21
|
+
begin_date: validate_date(begin_date),
|
22
|
+
end_date: validate_date(end_date)
|
23
|
+
}
|
24
|
+
)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def expected_messages_class
|
29
|
+
Messages::Historical::DailyWeeklyMonthly
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dtn
|
4
|
+
module Lookups
|
5
|
+
module Historical
|
6
|
+
# Weekly datapoint requests
|
7
|
+
class Datapoint < Base
|
8
|
+
# Retrieves up to [maxDatapoints] datapoints of composite daily/weekly/monthly datapoints
|
9
|
+
# for the specified [Symbol].
|
10
|
+
#
|
11
|
+
# Example messages
|
12
|
+
#
|
13
|
+
# HDX,[Symbol],[MaxDatapoints],[DataDirection],[RequestID],[DatapointsPerSend]<CR><LF>
|
14
|
+
# HWX,[Symbol],[MaxDatapoints],[DataDirection],[RequestID],[DatapointsPerSend]<CR><LF>
|
15
|
+
# HMX,[Symbol],[MaxDatapoints],[DataDirection],[RequestID],[DatapointsPerSend]<CR><LF>
|
16
|
+
def call(symbol:, **options)
|
17
|
+
self.combined_options = defaults(**options).merge(symbol: validate_symbol(symbol))
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def expected_messages_class
|
22
|
+
Messages::Historical::DailyWeeklyMonthly
|
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 Lookups
|
5
|
+
module Historical
|
6
|
+
# Interval datapoint requests
|
7
|
+
class IntervalDatapoint < Interval
|
8
|
+
TEMPLATE =
|
9
|
+
"HIX,%<symbol>s,%<interval>d,%<max_datapoints>d,%<data_direction>d,%<id>d,"\
|
10
|
+
"%<datapoints_per_send>d,%<interval_type>s"
|
11
|
+
|
12
|
+
# HIX - Retrieves [maxDatapoints] number of Intervals of data for the specified [Symbol].
|
13
|
+
#
|
14
|
+
# Example message
|
15
|
+
#
|
16
|
+
# HIX,[Symbol],[Interval],[MaxDatapoints],[DataDirection],[RequestID],[DatapointsPerSend],\
|
17
|
+
# [IntervalType]<CR><LF>
|
18
|
+
def call(symbol:, interval:, **options)
|
19
|
+
self.combined_options = defaults(**options).merge(
|
20
|
+
{
|
21
|
+
symbol: validate_symbol(symbol),
|
22
|
+
interval: validate_int(interval),
|
23
|
+
interval_type: validate_interval_type(options[:interval_type])
|
24
|
+
}
|
25
|
+
)
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|