## ## Datapoints are at most daily! ## See Priceitem for intra-day data ## class Iro::Datapoint include Mongoid::Document include Mongoid::Timestamps store_in collection: 'iro_datapoints' field :kind validates :kind, presence: true index({ kind: -1 }) KIND_CRYPTO = 'CRYPTO' KIND_STOCK = 'STOCK' KIND_OPTION = 'OPTION' ## but not PUT or CALL KIND_CURRENCY = 'CURRENCY' KIND_TREASURY = 'TREASURY' field :symbol ## ticker, but use 'symbol' here SYMBOL_BTC = 'BTC' SYMBOL_ETH = 'ETH' SYMBOL_T1MO = 'T1MO' SYMBOL_T2MO = 'T2MO' SYMBOL_T3MO = 'T3MO' SYMBOL_T4MO = 'T4MO' SYMBOL_T6MO = 'T6MO' SYMBOL_T1YR = 'T1YR' SYMBOL_T2YR = 'T2YR' SYMBOL_T3YR = 'T3YR' SYMBOL_T5YR = 'T5YR' SYMBOL_T7YR = 'T7YR' SYMBOL_T10YR = 'T10YR' SYMBOL_T20YR = 'T20YR' SYMBOL_T30YR = 'T30YR' field :date, type: Date index({ kind: -1, date: -1 }) validates :date, uniqueness: { scope: [ :symbol ] } field :quote_at, type: DateTime index({ kind: -1, quote_at: -1 }) validates :quote_at, uniqueness: { scope: [ :kind, :symbol ] } ## scope-by-kind is unnecessary here? _vp_ 2024-08-08 field :open, type: Float field :high, type: Float field :low, type: Float def close; value; end def close= a; value= a; end field :value, type: Float validates :value, presence: true field :volume, type: Integer def self.test_0trash add_fields = { '$addFields': { 'date_string': { '$dateToString': { 'format': "%Y-%m-%d", 'date': "$created_at" } } } } # group = { '$group': { # '_id': "$date_string", # 'my_doc': { '$first': "$$ROOT" } # } } group = { '$group': { '_id': "$date", 'my_doc': { '$first': "$$ROOT" } } } lookup = { '$lookup': { 'from': 'iro_dates', 'localField': 'date_string', 'foreignField': 'date', 'as': 'dates', } } lookup_merge = { '$replaceRoot': { 'newRoot': { '$mergeObjects': [ { '$arrayElemAt': [ "$dates", 0 ] }, "$$ROOT" ] } } } match = { '$match': { 'kind': 'some-type', 'created_at': { '$gte': '2023-12-01'.to_datetime, '$lte': '2023-12-31'.to_datetime, } } } outs = Iro::Datapoint.collection.aggregate([ add_fields, lookup, lookup_merge, match, { '$sort': { 'date_string': 1 } }, group, # { '$replaceRoot': { 'newRoot': "$my_doc" } }, # { '$project': { '_id': 0, 'date_string': 1, 'value': 1 } }, ]) puts! 'result' pp outs.to_a # puts! outs.to_a, 'result' end def self.test lookup = { '$lookup': { 'from': 'iro_datapoints', 'localField': 'date', 'foreignField': 'date', 'pipeline': [ { '$sort': { 'value': -1 } }, ], 'as': 'datapoints', } } lookup_merge = { '$replaceRoot': { 'newRoot': { '$mergeObjects': [ { '$arrayElemAt': [ "$datapoints", 0 ] }, "$$ROOT" ] } } } match = { '$match': { 'date': { '$gte': '2023-12-25', '$lte': '2023-12-31', } } } group = { '$group': { '_id': "$date", 'my_doc': { '$first': "$$ROOT" } } } outs = Iro::Date.collection.aggregate([ match, lookup, lookup_merge, group, { '$replaceRoot': { 'newRoot': "$my_doc" } }, # { '$replaceRoot': { 'newRoot': "$my_doc" } }, { '$project': { '_id': 0, 'date': 1, 'value': 1 } }, { '$sort': { 'date': 1 } }, ]) puts! 'result' pp outs.to_a # puts! outs.to_a, 'result' end def self.import_stock symbol:, path: csv = CSV.read(path, headers: true) csv.each do |row| flag = create({ kind: KIND_STOCK, symbol: symbol, date: row['Date'], quote_at: row['Date'], volume: row['Volume'], open: row['Open'], high: row['High'], low: row['Low'], value: row['Close'], }) if flag.persisted? print '^' else puts flag.errors.messages end end puts 'ok' end end