cotcube-level 0.3.4.1 → 0.3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/VERSION +1 -1
- data/bin/iswaps.rb +45 -0
- data/bin/swaps.rb +45 -0
- data/lib/cotcube-level/eod_stencil.rb +17 -10
- data/lib/cotcube-level/helpers.rb +92 -28
- data/lib/cotcube-level/intraday_stencil.rb +39 -19
- data/lib/cotcube-level.rb +2 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bbf4fc1b2edf64ffee60a29ffe7d72a71a4c0f8fb99b889026dce9dbca7399f
|
4
|
+
data.tar.gz: 59469c2431a20e9e1f07a99e0af61a4fed59ca276a02d654a2f2bd28a8543c04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78a4cb1a3bc80de5086befc5e927cafc86c38ef62410c4fc9d61e716e929f3847ad4a6ac870d30b94168e10dd72d22267aba8d4d4ae2f52a5e7170454a6864f5
|
7
|
+
data.tar.gz: 79ed8087a1936cef86257e24c9909f8923902be09e43868ce29deb5b178d03c2d3bbb2700f2bb76f67b2a58a51df930b3bdd41c4134922ed90bd6b2a1f0990ad
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 0.3.4.2 (November 28, 2021)
|
2
|
+
- added 2 executables to bin to display eod- and intraday-swaps
|
3
|
+
- helpers: adding ignorance to load_swaps and other, adding .mark_ignored
|
4
|
+
- intraday_stencil: allowing absence of :zero in #use
|
5
|
+
|
1
6
|
## 0.3.4.1 (October 09, 2021)
|
2
7
|
- intraday_stencil: fixing @index, that did not work when used outside active hours
|
3
8
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.4.
|
1
|
+
0.3.4.2
|
data/bin/iswaps.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/cotcube-level.rb'
|
4
|
+
|
5
|
+
HELP = <<HEREDOC
|
6
|
+
Display current intraday swaps.
|
7
|
+
> USAGE: iswaps.rb <contract> [json]
|
8
|
+
> contract a contract known to the system
|
9
|
+
> json switch to toggle json output instead of human readable
|
10
|
+
HEREDOC
|
11
|
+
if ARGV.empty?
|
12
|
+
puts HELP
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
contract = ARGV[0].nil? ? nil : ARGV[0].upcase
|
17
|
+
json = ARGV.include? 'json'
|
18
|
+
|
19
|
+
sym = Cotcube::Helpers.get_id_set(contract: contract) rescue "Could not determine contract #{contract}"
|
20
|
+
if sym.is_a? Sring; puts sym; puts HELP; exit 1; end
|
21
|
+
|
22
|
+
swaps = Cotcube::Level::load_swaps(interval: 30.minutes, swap_type: :full, contract: contract, sym: sym).
|
23
|
+
select{|swap| not(swap[:empty]) and
|
24
|
+
not(swap[:ignored]) and
|
25
|
+
not(swap[:exceeded].presence ? (swap[:exceeded] < DateTime.now - 2.days) : false)
|
26
|
+
}
|
27
|
+
stencil = Cotcube::Level::Intraday_Stencil.new( interval: 30.minutes, swap_type: :full, asset: contract[..1])
|
28
|
+
swaps.map!{|swap| stencil.use with: swap, sym: sym}
|
29
|
+
|
30
|
+
if json
|
31
|
+
puts swaps.to_json
|
32
|
+
else
|
33
|
+
puts '<none>' if swaps.empty?
|
34
|
+
swaps.each {|swap|
|
35
|
+
notice = if swap[:exceeded]
|
36
|
+
"EXCEEDED #{swap[:exceeded]}"
|
37
|
+
elsif swap[:ignored]
|
38
|
+
'IGNORED'
|
39
|
+
else
|
40
|
+
"Current: #{format sym[:format], swap[:current_value]}"
|
41
|
+
end
|
42
|
+
Cotcube::Level.puts_swap(swap, format: sym[:format], notice: notice)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
data/bin/swaps.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/cotcube-level.rb'
|
4
|
+
|
5
|
+
HELP = <<HEREDOC
|
6
|
+
swaps.rb: Display current eod swaps.
|
7
|
+
> USAGE: swaps.rb <contract> [json]
|
8
|
+
> contract a contract known to the system
|
9
|
+
> json switch to toggle json output instead of human readable
|
10
|
+
HEREDOC
|
11
|
+
if ARGV.empty?
|
12
|
+
puts HELP
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
contract = ARGV[0].nil? ? nil : ARGV[0].upcase
|
18
|
+
json = ARGV.include? 'json'
|
19
|
+
|
20
|
+
sym = Cotcube::Helpers.get_id_set(contract: contract) rescue "ERROR: Could not determine contract '#{contract}'."
|
21
|
+
|
22
|
+
if sym.is_a? String; puts sym; puts HELP; exit 1; end
|
23
|
+
|
24
|
+
swaps = Cotcube::Level::load_swaps(interval: :daily, swap_type: :full, contract: contract, quiet: true).
|
25
|
+
select{|swap| not(swap[:empty]) and
|
26
|
+
not(swap[:ignored]) and
|
27
|
+
not(swap[:exceeded].presence ? (swap[:exceeded] < DateTime.now - 2.days) : false)
|
28
|
+
}
|
29
|
+
stencil = Cotcube::Level::EOD_Stencil.new( interval: :daily, swap_type: :full)
|
30
|
+
swaps.map!{|swap| stencil.use with: swap, sym: sym}
|
31
|
+
if json
|
32
|
+
puts swaps.to_json
|
33
|
+
else
|
34
|
+
puts '<none>' if swaps.empty?
|
35
|
+
swaps.each {|swap|
|
36
|
+
notice = if swap[:exceeded]
|
37
|
+
"EXCEEDED #{swap[:exceeded].strftime('%Y-%m-%d')}"
|
38
|
+
elsif swap[:ignored]
|
39
|
+
'IGNORED'
|
40
|
+
else
|
41
|
+
"Current: #{format sym[:format], swap[:current_value]}"
|
42
|
+
end
|
43
|
+
Cotcube::Level.puts_swap(swap, format: sym[:format], notice: notice)
|
44
|
+
}
|
45
|
+
end
|
@@ -13,7 +13,7 @@ module Cotcube
|
|
13
13
|
#
|
14
14
|
# Current daily stencils contain dates from 2020-01-01 to 2023-12-31
|
15
15
|
#
|
16
|
-
def self.provide_raw_stencil(type:, interval: :daily, version: nil)
|
16
|
+
def self.provide_raw_stencil(type:, interval: :daily, version: nil, timezone: Cotcube::Helpers::CHICAGO)
|
17
17
|
loading = lambda do |typ|
|
18
18
|
file_base = "/var/cotcube/level/stencils/stencil_#{interval.to_s}_#{typ.to_s}.csv_"
|
19
19
|
if Dir["#{file_base}?*"].empty?
|
@@ -27,7 +27,7 @@ module Cotcube
|
|
27
27
|
raise ArgumentError, "Cannot open stencil from non-existant file #{file}."
|
28
28
|
end
|
29
29
|
end
|
30
|
-
CSV.read(file).map{|x| { datetime:
|
30
|
+
CSV.read(file).map{|x| { datetime: timezone.parse(x.first).freeze, x: x.last.to_i.freeze } }
|
31
31
|
end
|
32
32
|
unless const_defined? :RAW_STENCILS
|
33
33
|
const_set :RAW_STENCILS, { daily:
|
@@ -80,7 +80,7 @@ module Cotcube
|
|
80
80
|
raise ArgumentError, "Each stencil members should contain at least :datetime and :x" unless stencil.nil? or
|
81
81
|
stencil.map{|x| ([:datetime, :x] - x.keys).empty? and [ActiveSupport::TimeWithZone, Day].include?( x[:datetime] ) and x[:x].is_a?(Integer)}.reduce(:&)
|
82
82
|
|
83
|
-
base = stencil || EOD_Stencil.provide_raw_stencil(type: stencil_type, interval: :daily, version: version)
|
83
|
+
base = stencil || EOD_Stencil.provide_raw_stencil(type: stencil_type, interval: :daily, version: version, timezone: timezone)
|
84
84
|
|
85
85
|
# fast rewind to previous trading day
|
86
86
|
date = timezone.parse(date) unless [NilClass, Date, ActiveSupport::TimeWithZone].include? date.class
|
@@ -111,7 +111,12 @@ module Cotcube
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def zero
|
114
|
-
|
114
|
+
index(0)
|
115
|
+
end
|
116
|
+
|
117
|
+
def index(offset = 0)
|
118
|
+
@index ||= @base.index{|b| b[:x].zero? }
|
119
|
+
@base[@index + offset]
|
115
120
|
end
|
116
121
|
|
117
122
|
def apply(to: )
|
@@ -136,7 +141,7 @@ module Cotcube
|
|
136
141
|
to.reject!{|x| x[:x].nil? }
|
137
142
|
end
|
138
143
|
|
139
|
-
def use(with:, sym:, zero
|
144
|
+
def use(with:, sym:, zero: nil, grace: -2)
|
140
145
|
# todo: validate with (check if vslid swap
|
141
146
|
# sym (check keys)
|
142
147
|
# zero (ohlc with x.zero?)
|
@@ -147,14 +152,16 @@ module Cotcube
|
|
147
152
|
start = base.find{|x| swap[:datetime] == x[:datetime]}
|
148
153
|
swap[:current_change] = (swap[:tpi] * start[:x]).round(8)
|
149
154
|
swap[:current_value] = swap[:members].last[ ohlc ] + swap[:current_change] * sym[:ticksize]
|
150
|
-
|
151
|
-
|
152
|
-
|
155
|
+
unless zero.nil?
|
156
|
+
swap[:current_diff] = (swap[:current_value] - zero[ohlc]) * (high ? 1 : -1 )
|
157
|
+
swap[:current_dist] = (swap[:current_diff] / sym[:ticksize]).to_i
|
158
|
+
swap[:exceeded] = zero[:datetime] if swap[:current_dist] < grace
|
159
|
+
end
|
153
160
|
swap
|
154
161
|
end
|
155
|
-
|
162
|
+
end
|
156
163
|
|
157
|
-
|
164
|
+
end
|
158
165
|
|
159
166
|
end
|
160
167
|
|
@@ -23,7 +23,7 @@ module Cotcube
|
|
23
23
|
|
24
24
|
# human readable output
|
25
25
|
# please note the format must be given, that should be taken from :sym
|
26
|
-
def member_to_human(member,side: ,format:, daily: false)
|
26
|
+
def member_to_human(member,side: ,format:, daily: false, tws: false)
|
27
27
|
high = (side == :upper)
|
28
28
|
"#{ member[:datetime].strftime("%a, %Y-%m-%d#{daily ? "" :" %I:%M%p"}")
|
29
29
|
} x: #{format '%-4d', member[:x]
|
@@ -40,33 +40,74 @@ module Cotcube
|
|
40
40
|
# format: e.g. sym[:format]
|
41
41
|
# short: print one line / less verbose
|
42
42
|
# notice: add this to output as well
|
43
|
-
def puts_swap(swap, format: , short: true, notice: nil, hash: 3)
|
44
|
-
return if swap[:empty]
|
43
|
+
def puts_swap(swap, format: , short: true, notice: nil, hash: 3, tws: false)
|
44
|
+
return '' if swap[:empty]
|
45
|
+
|
46
|
+
# if presenting swaps from json, the datetimes need to be parsed first
|
47
|
+
swap[:datetime] = DateTime.parse(swap[:datetime]) if swap[:datetime].is_a? String
|
48
|
+
swap[:exceeded] = DateTime.parse(swap[:exceeded]) if swap[:exceeded].is_a? String
|
49
|
+
swap[:ignored] = DateTime.parse(swap[:ignored]) if swap[:ignored ].is_a? String
|
50
|
+
swap[:side] = swap[:side].to_sym
|
51
|
+
swap[:members].each do |mem|
|
52
|
+
mem[:datetime] = DateTime.parse(mem[:datetime]) if mem[:datetime].is_a? String
|
53
|
+
end
|
54
|
+
|
55
|
+
# TODO: create config-entry to contain 1.hour -- set of contracts ; 7.hours -- set of contracts [...]
|
56
|
+
# instead of hard-coding in here
|
57
|
+
# TODO: add also to :member_to_human
|
58
|
+
# then commit
|
59
|
+
if tws
|
60
|
+
case swap[:contract][0...2]
|
61
|
+
when *%w[ GC SI PL PA HG NG CL HO RB ]
|
62
|
+
delta_datetime = 1.hour
|
63
|
+
when *%w[ GG DX ]
|
64
|
+
delta_datetime = 7.hours
|
65
|
+
else
|
66
|
+
delta_datetime = 0
|
67
|
+
end
|
68
|
+
else
|
69
|
+
delta_datetime = 0
|
70
|
+
end
|
45
71
|
daily = %i[ continuous daily ].include?(swap[:interval].to_sym) rescue false
|
46
72
|
datetime_format = daily ? '%Y-%m-%d' : '%Y-%m-%d %I:%M %p'
|
47
73
|
high = swap[:side] == :high
|
48
74
|
ohlc = high ? :high : :low
|
49
75
|
if notice.nil? and swap[:exceeded]
|
50
|
-
notice = "exceeded #{swap[:exceeded].strftime(datetime_format)}"
|
76
|
+
notice = "exceeded #{(swap[:exceeded] + delta_datetime).strftime(datetime_format)}"
|
77
|
+
end
|
78
|
+
if swap[:ignored]
|
79
|
+
notice += " IGNORED"
|
51
80
|
end
|
52
81
|
if short
|
53
|
-
|
54
|
-
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
|
62
|
-
|
82
|
+
res ="#{format '%7s', swap[:digest][...hash]
|
83
|
+
} #{ swap[:contract]
|
84
|
+
} #{ swap[:side].to_s
|
85
|
+
}".colorize( swap[:side] == :upper ? :light_green : :light_red ) +
|
86
|
+
" (#{ format '%4d', swap[:length]
|
87
|
+
},#{ format '%4d', swap[:rating]
|
88
|
+
},#{ format '%4d', swap[:depth]
|
89
|
+
}) P: #{ format '%6s', (format '%4.2f', swap[:ppi])
|
90
|
+
} #{
|
91
|
+
if swap[:current_value].nil?
|
92
|
+
"I: #{ format '%8s', (format format, swap[:members].last[ ohlc ]) }"
|
93
|
+
else
|
94
|
+
"C: #{ format '%8s', (format format, swap[:current_value]) } "
|
95
|
+
end
|
96
|
+
} [#{ (swap[:members].first[:datetime] + delta_datetime).strftime(datetime_format)
|
97
|
+
} - #{ (swap[:members].last[:datetime] + delta_datetime).strftime(datetime_format)
|
98
|
+
}]#{" NOTE: #{notice}" unless notice.nil?
|
99
|
+
}".colorize(swap[:color] || :white )
|
100
|
+
puts res
|
63
101
|
else
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
swap[:members].each {|x|
|
102
|
+
res = ["side: #{swap[:side] }\tlen: #{swap[:length]} \trating: #{swap[:rating]}".colorize(swap[:color] || :white )]
|
103
|
+
res << "diff: #{swap[:ticks]}\tdif: #{swap[:diff].round(7)}\tdepth: #{swap[:depth]}".colorize(swap[:color] || :white )
|
104
|
+
res << "tpi: #{swap[:tpi] }\tppi: #{swap[:ppi]}".colorize(swap[:color] || :white )
|
105
|
+
res << "NOTE: #{notice}".colorize(:light_white) unless notice.nil?
|
106
|
+
swap[:members].each {|x| res << member_to_human(x, side: swap[:side], format: format, daily: daily) }
|
107
|
+
res = res.join("\n")
|
108
|
+
puts res
|
69
109
|
end
|
110
|
+
res
|
70
111
|
end
|
71
112
|
|
72
113
|
# create a standardized name for the cache files
|
@@ -81,7 +122,9 @@ module Cotcube
|
|
81
122
|
`mkdir -p #{dir}` unless File.exist?(dir)
|
82
123
|
`ln -s #{dir} #{symlink}` unless File.exist?(symlink)
|
83
124
|
file = "#{dir}/#{contract}_#{interval.to_s}_#{swap_type.to_s}.jsonl"
|
84
|
-
|
125
|
+
unless File.exist? file
|
126
|
+
`touch #{file}`
|
127
|
+
end
|
85
128
|
file
|
86
129
|
end
|
87
130
|
|
@@ -117,7 +160,9 @@ module Cotcube
|
|
117
160
|
|
118
161
|
# loading of swaps is also straight forward
|
119
162
|
# it takes few more efforts to normalize the values to their expected format
|
120
|
-
|
163
|
+
#
|
164
|
+
# it is not too nice that some actual interactive process is done here in the load section
|
165
|
+
def load_swaps(interval:, swap_type:, contract:, sym: nil, datetime: nil, recent: false, digest: nil, quiet: false, exceed: false, keep_ignored: false)
|
121
166
|
file = get_jsonl_name(interval: interval, swap_type: swap_type, contract: contract, sym: sym)
|
122
167
|
jsonl = File.read(file)
|
123
168
|
data = jsonl.
|
@@ -128,12 +173,13 @@ module Cotcube
|
|
128
173
|
tap do |sw|
|
129
174
|
sw[:datetime] = DateTime.parse(sw[:datetime]) rescue nil
|
130
175
|
(sw[:exceeded] = DateTime.parse(sw[:exceeded]) rescue nil) if sw[:exceeded]
|
176
|
+
(sw[:ignored] = DateTime.parse(sw[:ignored]) rescue nil) if sw[:ignored]
|
131
177
|
sw[:interval] = interval
|
132
178
|
sw[:swap_type] = swap_type
|
133
179
|
sw[:contract] = contract
|
134
180
|
%i[ side ].each {|key| sw[key] = sw[key].to_sym rescue false }
|
135
|
-
unless sw[:empty] or sw[:exceeded]
|
136
|
-
sw[:color] = sw[:color].to_sym
|
181
|
+
unless sw[:empty] or sw[:exceeded] or sw[:ignored]
|
182
|
+
sw[:color] = sw[:color].to_sym
|
137
183
|
sw[:members].map{|mem| mem[:datetime] = DateTime.parse(mem[:datetime]) }
|
138
184
|
end
|
139
185
|
end
|
@@ -144,13 +190,20 @@ module Cotcube
|
|
144
190
|
raise RuntimeError, "Inconsistent history for '#{exc}'. Origin not found." if swap.nil?
|
145
191
|
swap[:exceeded] = exc[:exceeded]
|
146
192
|
end
|
193
|
+
# assign ignorance data to actual swaps
|
194
|
+
data.select{|swap| swap[:ignored] }.each do |ign|
|
195
|
+
swap = data.find{|ref| ref[:digest] == ign[:ref]}
|
196
|
+
raise RuntimeError, "Inconsistent history for '#{ign}'. Origin not found." if swap.nil?
|
197
|
+
swap[:ignored] = ign[:ignored]
|
198
|
+
end
|
147
199
|
# do not return bare exceedance information
|
148
|
-
data.reject!{|swap| swap[:exceeded] and swap[:members].nil? }
|
200
|
+
data.reject!{|swap| (swap[:ignored] or swap[:exceeded]) and swap[:members].nil? }
|
149
201
|
# do not return swaps that are found 'later'
|
150
202
|
data.reject!{|swap| swap[:datetime] > datetime } unless datetime.nil?
|
151
203
|
# do not return exceeded swaps, that are exceeded in the past
|
152
204
|
recent = 7.days if recent.is_a? TrueClass
|
153
205
|
recent += 5.hours if recent
|
206
|
+
data.reject!{|swap| swap[:ignored] } unless keep_ignored
|
154
207
|
data.reject!{|swap| swap[:exceeded] and swap[:exceeded] < datetime - (recent ? recent : 0) } unless datetime.nil?
|
155
208
|
# remove exceedance information that is found 'later'
|
156
209
|
data.map{|swap| swap.delete(:exceeded) if swap[:exceeded] and swap[:exceeded] > datetime} unless datetime.nil?
|
@@ -202,20 +255,31 @@ module Cotcube
|
|
202
255
|
save_swaps to_save, interval: swap[:interval], swap_type: swap[:swap_type], contract: contract, sym: sym, quiet: (not debug)
|
203
256
|
swap[:exceeded] = update[:exceeded]
|
204
257
|
end
|
205
|
-
%i[ current_change current_value current_diff current_dist ].map{|key| swap[key] = update[key] }
|
258
|
+
%i[ current_change current_value current_diff current_dist alert].map{|key| swap[key] = update[key] }
|
206
259
|
swap
|
207
260
|
end.compact
|
208
261
|
end
|
209
262
|
|
210
|
-
def mark_exceeded(swap:, datetime:, debug: false)
|
263
|
+
def mark_exceeded(swap:, datetime:, debug: false, sym: nil)
|
211
264
|
to_save = {
|
212
265
|
datetime: datetime,
|
213
266
|
ref: swap[:digest],
|
214
267
|
side: swap[:side],
|
215
268
|
exceeded: datetime
|
216
269
|
}
|
217
|
-
|
218
|
-
swap[:
|
270
|
+
sym ||= Cotcube::Helpers.get_id_set(contract: swap[:contract])
|
271
|
+
save_swaps to_save, interval: swap[:interval], swap_type: swap[:swap_type], sym: sym, contract: swap[:contract], quiet: (not debug)
|
272
|
+
swap
|
273
|
+
end
|
274
|
+
|
275
|
+
def mark_ignored(swap:, datetime: DateTime.now, sym: , debug: true)
|
276
|
+
to_save = {
|
277
|
+
datetime: datetime,
|
278
|
+
ref: swap[:digest],
|
279
|
+
side: swap[:side],
|
280
|
+
ignored: datetime
|
281
|
+
}
|
282
|
+
save_swaps to_save, interval: swap[:interval], swap_type: swap[:swap_type], sym: sym, contract: swap[:contract], quiet: (not debug)
|
219
283
|
swap
|
220
284
|
end
|
221
285
|
|
@@ -35,6 +35,7 @@ module Cotcube
|
|
35
35
|
debug: false,
|
36
36
|
weeks: 6,
|
37
37
|
future: 2,
|
38
|
+
measuring: false,
|
38
39
|
version: nil, # when referring to a specicic version of the stencil
|
39
40
|
stencil: nil, # instead of preparing, use this one if set
|
40
41
|
warnings: true # be more quiet
|
@@ -51,10 +52,22 @@ module Cotcube
|
|
51
52
|
@datetime += interval while @datetime <= datetime - interval
|
52
53
|
@datetime -= interval
|
53
54
|
|
54
|
-
|
55
|
+
now = DateTime.now
|
56
|
+
measure = lambda {|x| puts "\nMeasured #{(Time.now - now).to_f.round(2)}: ".colorize(:light_yellow) + x + "\n\n" if measuring }
|
57
|
+
|
58
|
+
measure.call "Starting initialization for asset '#{asset}' "
|
59
|
+
|
60
|
+
const = "RAW_INTRA_STENCIL_#{@shiftset[:nr]}_#{interval.in_minutes.to_i}_#{weeks}_#{future}".to_sym
|
61
|
+
cachefile = "/var/cotcube/level/stencils/cache/#{swap_type.to_s}_#{interval}_#{@datetime.strftime('%Y-%m-%d-%H-%M')}.json"
|
62
|
+
|
55
63
|
if Object.const_defined? const
|
64
|
+
measure.call 'getting cached base from memory'
|
56
65
|
@base = (Object.const_get const).map{|z| z.dup}
|
66
|
+
elsif File.exist? cachefile
|
67
|
+
measure.call 'getting cached base from file'
|
68
|
+
@base = JSON.parse(File.read(cachefile), symbolize_names: true).map{|z| z[:datetime] = DateTime.parse(z[:datetime]); z[:type] = z[:type].to_sym; z }
|
57
69
|
else
|
70
|
+
measure.call 'creating base from shiftset'
|
58
71
|
start_time = lambda {|x| @shiftset[x].split('-').first rescue '' }
|
59
72
|
start_hours = lambda {|x| @shiftset[x].split('-').first[ 0.. 1].to_i.send(:hours) rescue 0 }
|
60
73
|
start_minutes = lambda {|x| @shiftset[x].split('-').first[-2..-1].to_i.send(:minutes) rescue 0 }
|
@@ -62,7 +75,7 @@ module Cotcube
|
|
62
75
|
end_hours = lambda {|x| @shiftset[x].split('-').last [ 0.. 1].to_i.send(:hours) rescue 0 }
|
63
76
|
end_minutes = lambda {|x| @shiftset[x].split('-').last [-2..-1].to_i.send(:minutes) rescue 0 }
|
64
77
|
|
65
|
-
|
78
|
+
runner = (@datetime -
|
66
79
|
weeks * 7.days).beginning_of_week(:sunday)
|
67
80
|
tm_runner = lambda { runner.strftime('%H%M') }
|
68
81
|
@base = []
|
@@ -113,7 +126,9 @@ module Cotcube
|
|
113
126
|
end
|
114
127
|
end
|
115
128
|
Object.const_set(const, @base.map{|z| z.dup})
|
129
|
+
File.open(cachefile, 'w'){|f| f.write(@base.to_json)}
|
116
130
|
end
|
131
|
+
measure.call "base created with #{@base.size} records"
|
117
132
|
|
118
133
|
case swap_type
|
119
134
|
when :full
|
@@ -131,42 +146,44 @@ module Cotcube
|
|
131
146
|
else
|
132
147
|
raise ArgumentError, "Unknown stencil/swap type '#{swap_type}'"
|
133
148
|
end
|
149
|
+
measure.call "swaptype #{swap_type} applied"
|
134
150
|
@base.map!{|z| z.dup}
|
151
|
+
measure.call 'base dupe\'d'
|
135
152
|
|
136
153
|
# zero is, were either x[:datetime] == @datetime (when we are intraday)
|
137
154
|
# or otherwise {x[:datetime] <= @datetime}.last (when on maintenance)
|
138
|
-
|
155
|
+
selector = @base.select{|y| y[:datetime] <= @datetime }.last
|
156
|
+
@index = @base.index{|x| x == selector }
|
157
|
+
measure.call 'index selected'
|
139
158
|
@index -= 1 while %i[sow sod mpre mpost eod eow].include? @base[@index][:type]
|
159
|
+
measure.call 'index adjusted'
|
140
160
|
@datetime = @base[@index][:datetime]
|
141
161
|
@zero = @base[@index]
|
142
162
|
counter = 0
|
163
|
+
measure.call "Applying counter to past"
|
143
164
|
while @base[@index - counter] and @index - counter >= 0
|
144
165
|
@base[@index - counter][:x] = counter
|
145
166
|
counter += 1
|
146
167
|
end
|
147
168
|
counter = 0
|
169
|
+
measure.call "Applying counter to future"
|
148
170
|
while @base[@index + counter] and @index + counter < @base.length
|
149
171
|
@base[@index + counter][:x] = -counter
|
150
172
|
counter += 1
|
151
173
|
end
|
174
|
+
measure.call 'initialization finished'
|
152
175
|
end
|
153
176
|
|
154
|
-
=begin
|
155
|
-
def dup
|
156
|
-
Intraday_Stencil.new(
|
157
|
-
debug: @debug,
|
158
|
-
interval: @interval,
|
159
|
-
swap_type: @swap_type,
|
160
|
-
datetime: @datetime,
|
161
|
-
stencil: @base.map{|x| x.dup}
|
162
|
-
)
|
163
|
-
end
|
164
|
-
=end
|
165
|
-
|
166
177
|
def zero
|
167
178
|
@zero ||= @base.find{|b| b[:x].zero? }
|
168
179
|
end
|
169
180
|
|
181
|
+
def index(offset = 0)
|
182
|
+
@index ||= @base.index{|b| b[:x].zero? }
|
183
|
+
@base[@index + offset]
|
184
|
+
end
|
185
|
+
|
186
|
+
|
170
187
|
def apply(to: )
|
171
188
|
offset = 0
|
172
189
|
@base.each_index do |i|
|
@@ -190,7 +207,7 @@ module Cotcube
|
|
190
207
|
to.reject!{|x| x[:x].nil? }
|
191
208
|
end
|
192
209
|
|
193
|
-
def use(with:, sym:, zero
|
210
|
+
def use(with:, sym:, zero: nil, grace: -2)
|
194
211
|
# todo: validate with (check if vslid swap
|
195
212
|
# sym (check keys)
|
196
213
|
# zero (ohlc with x.zero?)
|
@@ -201,9 +218,12 @@ module Cotcube
|
|
201
218
|
start = base.find{|x| swap[:datetime] == x[:datetime]}
|
202
219
|
swap[:current_change] = (swap[:tpi] * start[:x]).round(8)
|
203
220
|
swap[:current_value] = swap[:members].last[ ohlc ] + swap[:current_change] * sym[:ticksize]
|
204
|
-
|
205
|
-
|
206
|
-
|
221
|
+
unless zero.nil?
|
222
|
+
swap[:current_diff] = (swap[:current_value] - zero[ohlc]) * (high ? 1 : -1 )
|
223
|
+
swap[:current_dist] = (swap[:current_diff] / sym[:ticksize]).to_i
|
224
|
+
swap[:alert] = (swap[:current_diff] / zero[:atr5]).round(2)
|
225
|
+
swap[:exceeded] = zero[:datetime] if swap[:current_dist] < grace
|
226
|
+
end
|
207
227
|
swap
|
208
228
|
end
|
209
229
|
end
|
data/lib/cotcube-level.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotcube-level
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.4.
|
4
|
+
version: 0.3.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin L. Tischendorf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -120,6 +120,8 @@ files:
|
|
120
120
|
- Gemfile
|
121
121
|
- README.md
|
122
122
|
- VERSION
|
123
|
+
- bin/iswaps.rb
|
124
|
+
- bin/swaps.rb
|
123
125
|
- cotcube-level.gemspec
|
124
126
|
- lib/cotcube-level.rb
|
125
127
|
- lib/cotcube-level/detect_slope.rb
|
@@ -149,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
151
|
- !ruby/object:Gem::Version
|
150
152
|
version: '0'
|
151
153
|
requirements: []
|
152
|
-
rubygems_version: 3.1.
|
154
|
+
rubygems_version: 3.1.6
|
153
155
|
signing_key:
|
154
156
|
specification_version: 4
|
155
157
|
summary: A gem to shear a time series
|