Class: SQA::DataFrame::AlphaVantage
- Inherits:
-
Object
- Object
- SQA::DataFrame::AlphaVantage
- Defined in:
- lib/sqa/data_frame/alpha_vantage.rb
Constant Summary collapse
- CONNECTION =
Faraday.new(url: 'https://www.alphavantage.co')
- HEADERS =
YahooFinance::HEADERS
- HEADER_MAPPING =
The Alpha Vantage CSV format uses these exact column names: timestamp, open, high, low, close, volume We remap them to match Yahoo Finance column names for consistency
{ "timestamp" => HEADERS[0], # :timestamp (already matches, but explicit) "open" => HEADERS[1], # :open_price "high" => HEADERS[2], # :high_price "low" => HEADERS[3], # :low_price "close" => HEADERS[4], # :close_price (AND :adj_close_price - AV doesn't split these) "volume" => HEADERS[6] # :volume }
- TRANSFORMERS =
Transformers applied AFTER column renaming Alpha Vantage CSV doesn’t have adjusted_close, so we only transform what exists
{ HEADERS[1] => -> (v) { v.to_f.round(3) }, # :open_price HEADERS[2] => -> (v) { v.to_f.round(3) }, # :high_price HEADERS[3] => -> (v) { v.to_f.round(3) }, # :low_price HEADERS[4] => -> (v) { v.to_f.round(3) }, # :close_price # HEADERS[5] - :adj_close_price doesn't exist in Alpha Vantage CSV HEADERS[6] => -> (v) { v.to_i } # :volume }
Class Method Summary collapse
-
.recent(ticker, full: false, from_date: nil) ⇒ Object
Get recent data from Alpha Vantage API.
Class Method Details
.recent(ticker, full: false, from_date: nil) ⇒ Object
Get recent data from Alpha Vantage API
ticker String the security to retrieve full Boolean whether to fetch full history or compact (last 100 days) from_date Date optional date to fetch data after (for incremental updates)
Returns: SQA::DataFrame sorted in ASCENDING order (oldest to newest) Note: Alpha Vantage returns data newest-first, but we sort ascending for TA-Lib compatibility
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 |
# File 'lib/sqa/data_frame/alpha_vantage.rb', line 46 def self.recent(ticker, full: false, from_date: nil) response = CONNECTION.get( "/query?" + "function=TIME_SERIES_DAILY&" + "symbol=#{ticker.upcase}&" + "apikey=#{SQA.av.key}&" + "datatype=csv&" + "outputsize=#{full ? 'full' : 'compact'}" ).to_hash unless 200 == response[:status] raise "Bad Response: #{response[:status]}" end # Read CSV into Polars DataFrame directly df = Polars.read_csv( StringIO.new(response[:body]), dtypes: { "open" => :f64, "high" => :f64, "low" => :f64, "close" => :f64, "volume" => :i64 } ) # Handle date criteria if applicable if from_date # Use Polars.col() to create an expression for filtering # Use > (not >=) to exclude the from_date itself and prevent duplicates df = df.filter(Polars.col("timestamp") > from_date.to_s) end # Wrap in SQA::DataFrame with proper transformers # Note: mapping is applied first (renames columns), then transformers sqa_df = SQA::DataFrame.new(df, transformers: TRANSFORMERS, mapping: HEADER_MAPPING) # Alpha Vantage doesn't split close/adjusted_close, so duplicate for compatibility # This ensures adj_close_price exists for strategies that expect it sqa_df.data = sqa_df.data.with_column( sqa_df.data["close_price"].alias("adj_close_price") ) # Sort data in ascending chronological order (oldest to newest) for TA-Lib compatibility # Alpha Vantage returns data newest-first, but TA-Lib expects oldest-first sqa_df.data = sqa_df.data.sort("timestamp", reverse: false) sqa_df end |