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

Returns a new instance of 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