cotcube-level 0.3.4.1 → 0.3.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|