Class: Cotcube::Level::EOD_Stencil

Inherits:
Object
  • Object
show all
Defined in:
lib/cotcube-level/eod_stencil.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(range: nil, interval:, swap_type:, date: nil, debug: false, version: nil, timezone: Cotcube::Helpers::CHICAGO, stencil: nil, warnings: true) ⇒ EOD_Stencil



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cotcube-level/eod_stencil.rb', line 42

def initialize(
  range: nil,                 # used to shrink the stencil size, accepts String or Date
  interval:,
  swap_type:,
  date: nil,
  debug: false,
  version: nil,               # when referring to a specicic version of the stencil
  timezone: Cotcube::Helpers::CHICAGO,
  stencil: nil,               # instead of preparing, use this one if set
  warnings: true              # be more quiet
)
  @debug     = debug
  @interval  = interval == :continuous ? :daily : interval
  @swap_type = swap_type
  @warnings = warnings
  step =  case @interval
          when :hours, :hour; 1.hour
          when :quarters, :quarter; 15.minutes
          else; 1.day
          end

  case @interval
  when :day, :days, :daily, :dailies, :synth, :synthetic #, :week, :weeks, :month, :months
    unless range.nil?
      starter = range.begin.is_a?(String) ? timezone.parse(range.begin) : range.begin
      ender   = range.  end.is_a?(String) ? timezone.parse(range.  end) : range.  end
    end

    stencil_type = case swap_type
                 when :rth
                   :full
                 when :rthc
                   :rtc
                 else
                   swap_type
                 end
    # TODO: Check / warn / raise whether stencil (if provided) is a proper data type
    raise ArgumentError, "EOD_Stencil should be nil or Array" unless [NilClass, Array].include? stencil.class
    raise ArgumentError, "Each stencil members should contain at least :datetime and :x" unless stencil.nil? or
      stencil.map{|x| ([:datetime, :x] - x.keys).empty? and [ActiveSupport::TimeWithZone, Day].include?( x[:datetime] ) and x[:x].is_a?(Integer)}.reduce(:&)

    base = stencil || EOD_Stencil.provide_raw_stencil(type: stencil_type, interval: :daily, version: version, timezone: timezone)

    # fast rewind to previous trading day
    date = timezone.parse(date) unless [NilClass, Date, ActiveSupport::TimeWithZone].include? date.class
    @date = date || Date.today
    best_match = base.select{|x| x[:datetime].to_date <= @date}.last[:datetime]
    @date  = best_match

    offset = base.map{|x| x[:datetime]}.index(@date)

    # apply offset to stencil, so zero will match today (or what was provided as 'date')
    @base = base.map.
      each_with_index{|d,i| d[:x] = (offset - i).freeze; d }
    # if range was given, shrink stencil to specified range
    @base.select!{|d| (d[:datetime] >= starter and d[:datetime] <= ender) } unless range.nil?
  else
    raise RuntimeError, "'interval: #{interval}' was provided, what does not match anything this tool can handle (currently :days, :dailies, :synthetic)."
  end
end

Instance Attribute Details

#baseObject

Returns the value of attribute base.



7
8
9
# File 'lib/cotcube-level/eod_stencil.rb', line 7

def base
  @base
end

#intervalObject (readonly)

Returns the value of attribute interval.



8
9
10
# File 'lib/cotcube-level/eod_stencil.rb', line 8

def interval
  @interval
end

Class Method Details

.provide_raw_stencil(type:, interval: :daily, version: nil, timezone: Cotcube::Helpers::CHICAGO) ⇒ Object

Class method that loads the (latest) obligatory stencil for given interval and type. These raw stencils are located in /var/cotcube/level/stencils

Current daily stencils contain dates from 2020-01-01 to 2023-12-31



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/cotcube-level/eod_stencil.rb', line 16

def self.provide_raw_stencil(type:, interval: :daily, version: nil, timezone: Cotcube::Helpers::CHICAGO)
  loading = lambda do |typ|
    file_base = "/var/cotcube/level/stencils/stencil_#{interval.to_s}_#{typ.to_s}.csv_"
    if Dir["#{file_base}?*"].empty?
      raise ArgumentError, "Could not find any stencil matching interval #{interval} and type #{typ}. Check #{file_base} manually!"
    end
    if version.nil? # use latest available version if not given
      file = Dir["#{file_base}?*"].sort.last
    else
      file = "#{file_base}#{version}"
      unless File.exist? file
        raise ArgumentError, "Cannot open stencil from non-existant file #{file}."
      end
    end
    CSV.read(file).map{|x| { datetime: timezone.parse(x.first).freeze, x: x.last.to_i.freeze } }
  end
  unless const_defined? :RAW_STENCILS
    const_set :RAW_STENCILS, { daily:
                               { full: loading.call( :full).freeze,
                                 rtc:  loading.call( :rtc).freeze
      }.freeze
    }.freeze
  end
  RAW_STENCILS[interval][type].map{|x| x.dup}
end

Instance Method Details

#apply(to:) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/cotcube-level/eod_stencil.rb', line 122

def apply(to: )
  offset = 0 
  @base.each_index do |i| 
    begin
      offset += 1 while to[i+offset][:datetime].to_date < @base[i][:datetime].to_date
    rescue
      # appending
      to << @base[i]
      next
    end
    if to[i+offset][:datetime].to_date > @base[i][:datetime].to_date
      # skipping 
      offset -= 1
      next
    end
    # merging
    to[i+offset][:x] = @base[i][:x]
  end
  # finally remove all bars that do not belong to the stencil (i.e. holidays)
  to.reject!{|x| x[:x].nil? }
end

#dupObject



103
104
105
106
107
108
109
110
111
# File 'lib/cotcube-level/eod_stencil.rb', line 103

def dup
  EOD_Stencil.new(
    debug:      @debug,
    interval:   @interval,
    swap_type:  @swap_type,
    date:       @date,
    stencil:    @base.map{|x| x.dup}
  )
end

#index(offset = 0) ⇒ Object



117
118
119
120
# File 'lib/cotcube-level/eod_stencil.rb', line 117

def index(offset = 0)
  @index ||= @base.index{|b| b[:x].zero? }
  @base[@index + offset]
end

#use(with:, sym:, zero: nil, grace: -2)) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/cotcube-level/eod_stencil.rb', line 144

def use(with:, sym:, zero: nil, grace: -2)
  # todo: validate with (check if vslid swap
  #                sym  (check keys)
  #                zero (ohlc with x.zero?)
  #                side ( upper or lower)
  swap  = with.dup
  high  = swap[:side] == :upper
  ohlc  = high ? :high : :low
  start = base.find{|x| swap[:datetime] == x[:datetime]}
  swap[:current_change] = (swap[:tpi] * start[:x]).round(8)
  swap[:current_value]  =  swap[:members].last[ ohlc ] + swap[:current_change] * sym[:ticksize]
  unless zero.nil? 
    swap[:current_diff]   = (swap[:current_value] - zero[ohlc]) * (high ? 1 : -1 )
    swap[:current_dist]   = (swap[:current_diff] / sym[:ticksize]).to_i
    swap[:exceeded]       =  zero[:datetime] if swap[:current_dist] < grace
  end
  swap
end

#zeroObject



113
114
115
# File 'lib/cotcube-level/eod_stencil.rb', line 113

def zero
  index(0)
end