Quant

Coverage StatusCIElixirOTPLicense

Run in Livebook

High-performance standardized financial data API for Elixir with Explorer DataFrames

Fetch financial data from multiple providers with universal parameters and identical output schemas for seamless analysis and maximum performance.

โœจ Key Features

๐ŸŽฏ Universal API Design

๐Ÿ“Š Mathematical Indicators (Python-Validated)

Indicator Name Accuracy vs Python Key Features
RSI Relative Strength Index 100% (0.0% diff) Wilder's smoothing method
DEMA Double Exponential MA 99.96% (0.04% diff) Enhanced responsiveness
HMA Hull Moving Average 100% (0.0% diff) Reduced lag, 4-step algorithm
KAMA Kaufman Adaptive MA 100% (0.0% diff) Market condition adaptation
TEMA Triple Exponential MA 99.9988% (0.0016 diff) Maximum responsiveness
WMA Weighted Moving Average 100% (0.0% diff) Linear weight distribution

๐ŸŽฏ Trading Strategies & Backtesting

๐Ÿ”ง Parameter Optimization (vectorbt-like)

Systematic parameter tuning with vectorbt-equivalent functionality:

# Get historical data
{:ok, df} = Quant.Explorer.history("AAPL", provider: :yahoo_finance, period: "1y")

# Define parameter ranges (like vectorbt.simulate_all_params())
param_ranges = %{
  fast_period: 5..20,     # SMA fast period range
  slow_period: 20..50     # SMA slow period range  
}

# Run optimization across all combinations - equivalent to vectorbt
{:ok, results} = Quant.Strategy.Optimization.run_combinations(df, :sma_crossover, param_ranges)

# Find best parameters (equivalent to results.idxmax())
best_params = Quant.Strategy.Optimization.find_best_params(results, :total_return)
# => %{fast_period: 12, slow_period: 26, total_return: 0.2847}

๐Ÿš€ Advanced Features:

Feature Description Performance Use Case
Parallel Processing Multi-core parameter testing 4x faster on 4-core Large parameter spaces
Streaming Results Memory-efficient processing Constant memory usage 1000+ combinations
Walk-Forward Analysis Out-of-sample validation Prevents overfitting Robust parameter selection
CSV/JSON Export Results export & analysis Full precision Research & reporting
Performance Benchmarking Optimization profiling Scaling analysis System optimization

๐ŸŽฏ Production-Ready Optimization:

# Parallel processing for speed (uses all CPU cores)
{:ok, results} = Quant.Strategy.Optimization.run_combinations_parallel(
  df, :sma_crossover, param_ranges, 
  concurrency: System.schedulers_online()
)

# Walk-forward optimization for robustness
{:ok, wf_results} = Quant.Strategy.Optimization.walk_forward_optimization(
  df, :sma_crossover, param_ranges,
  window_size: 252,  # 1 year training
  step_size: 63      # Quarterly reoptimization
)

# Memory-efficient streaming for large parameter spaces
results_stream = Quant.Strategy.Optimization.run_combinations_stream(
  df, :sma_crossover, %{period: 5..100}, chunk_size: 20
)

# Export results for analysis
:ok = Quant.Strategy.Optimization.Export.to_csv(results, "optimization_results.csv")
:ok = Quant.Strategy.Optimization.Export.to_json(results, "results.json", precision: 6)

๐Ÿ“Š Comprehensive Analysis Tools:

๐Ÿงช Python Cross-Validation Framework

Validation Type Description Coverage Results
Mathematical Accuracy Final value comparison vs pandas/numpy All 6 indicators 99.96%+ accuracy
Algorithm Verification Step-by-step calculation comparison Core algorithms Perfect methodology match
Behavioral Testing Responsiveness and trend adaptation Market scenarios Expected behavior confirmed
Methodology Confirmation Correct implementation verification Industry standards Wilder's RSI, Hull algorithm
Test Suite Comprehensive cross-language validation Python validation 100% pass rate

๐ŸŒ Multi-Provider Support

Provider Data Types API Key Cost Key Features
Yahoo Finance Stocks, Crypto, Options โŒ No ๐Ÿ†“ Free Historical data, real-time quotes, company info
Alpha Vantage Stocks, Forex โœ… Required ๐Ÿ’ฐ Freemium Premium intraday data, fundamentals
Binance Cryptocurrency โŒ No ๐Ÿ†“ Free Real-time crypto data, all trading pairs
CoinGecko Cryptocurrency โŒ No ๐Ÿ†“ Free Market data, historical prices, market cap
Twelve Data Stocks, Forex, Crypto โœ… Required ๐Ÿ’ฐ Premium High-frequency data, global markets

โšก Performance & Reliability

๐Ÿ›ก๏ธ Production Ready

๐ŸŽฏ Standardized API - Built for Performance

Quant.Explorer provides a completely standardized interface across all financial data providers with identical 12-column output schemas:

# Universal parameters work with ALL providers - identical output schemas!
{:ok, yahoo_df} = Quant.Explorer.history("AAPL", 
  provider: :yahoo_finance, interval: "1d", period: "1y")

{:ok, binance_df} = Quant.Explorer.history("BTCUSDT",
  provider: :binance, interval: "1d", period: "1y")

# Both DataFrames have IDENTICAL 12-column schemas!
#Explorer.DataFrame<
#  Polars[365 x 12]  # โœ… Always exactly 12 columns
#  ["symbol", "timestamp", "open", "high", "low", "close", "volume",
#   "adj_close", "market_cap", "provider", "currency", "timezone"]

# Seamless high-performance cross-asset analysis
DataFrame.concat_rows(yahoo_df, binance_df)
|> DataFrame.group_by("provider") 
|> DataFrame.summarise(avg_price: mean(close), total_volume: sum(volume))

โœ… ACHIEVED: Complete Schema Standardization Across All Providers
โœ… TESTED: Works with Yahoo Finance, Binance, Alpha Vantage, CoinGecko, Twelve Data
โœ… VALIDATED: Cross-asset analysis (stocks + crypto) in unified DataFrames

๐Ÿ“– Complete Standardization Guide โ†’

๐Ÿš€ STANDARDIZATION SUCCESS STORY

Problem Solved: Financial data providers return inconsistent schemas, making cross-provider analysis painful.

Before Quant.Explorer:

# Binance: 16 inconsistent columns  
[\"symbol\", \"open_time\", \"close_time\", \"quote_volume\", \"taker_buy_volume\", ...]

# Yahoo Finance: 7 different columns
[\"Date\", \"Open\", \"High\", \"Low\", \"Close\", \"Adj Close\", \"Volume\"]

# Result: Impossible to combine data sources! ๐Ÿ˜ž

After Quant.Explorer:

# ALL providers: Identical 12-column schema  
[\"symbol\", \"timestamp\", \"open\", \"high\", \"low\", \"close\", \"volume\", 
 \"adj_close\", \"market_cap\", \"provider\", \"currency\", \"timezone\"]

# Result: Seamless cross-asset analysis! ๐ŸŽ‰
combined_df = DataFrame.concat_rows([binance_btc, yahoo_aapl, alpha_msft])

๐Ÿ“Š Standardization Stats:

Installation & Setup

Elixir Library

# Add to mix.exs
def deps do
  [
    {:quant, github: "the-nerd-company/quant"}
  ]
end

Python Dependencies (For Cross-Language Validation)

The library includes comprehensive Python validation tests that compare results against pandas/numpy standards for mathematical accuracy.

Quick Setup with UV (Recommended)

# Run the automated setup script
./scripts/setup_python.sh

# Or use Makefile
make python-setup

Manual UV Installation

# Install UV (much faster than pip)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install dependencies
uv pip install --system -e .
# or
uv pip install --system -r requirements.txt

Traditional pip (Legacy)

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Running Tests

# All tests
mix test

# Python validation tests only (requires Python setup)
mix test --include python_validation
make python-test

# Coverage report
mix coveralls.lcov

Quick Start

โš ๏ธ Important: Provider Must Be Explicit All functions require an explicit provider: parameter. There are no default providers to avoid confusion about which API is being called.

# โœ… Correct - explicit provider
{:ok, df} = Quant.Explorer.history("AAPL", provider: :yahoo_finance, period: "1y")

# โŒ Error - will return {:error, :provider_required}  
{:ok, df} = Quant.Explorer.history("AAPL", period: "1y")
# Add to mix.exs
def deps do
  [
    {:quant, github: "the-nerd-company/quant"}
  ]
end

Standardized API - Universal Parameters

# Universal parameters work with ALL providers - perfect for analysis!

# Stock data - Yahoo Finance (free, no API key)
{:ok, df} = Quant.Explorer.history("AAPL", 
  provider: :yahoo_finance, interval: "1d", period: "1y")

# Stock data - Alpha Vantage (premium, requires API key)
{:ok, df} = Quant.Explorer.history("AAPL",
  provider: :alpha_vantage, interval: "1d", period: "1y", api_key: "your_key")

# Crypto data - Binance (free, no API key)
{:ok, df} = Quant.Explorer.history("BTCUSDT",
  provider: :binance, interval: "1d", period: "1y")

# Crypto data - CoinGecko (free, no API key)
{:ok, df} = Quant.Explorer.history("bitcoin", 
  provider: :coin_gecko, interval: "1d", period: "1y", currency: "usd")

# Stock data - Twelve Data (premium, requires API key)  
{:ok, df} = Quant.Explorer.history("AAPL",
  provider: :twelve_data, interval: "1d", period: "1y", api_key: "your_key")

# ALL DataFrames have IDENTICAL schemas - combine for analysis!
all_data = [yahoo_df, alpha_df, binance_df, coingecko_df, twelve_df]
|> Enum.reduce(&DataFrame.concat_rows/2)
|> DataFrame.group_by("provider") 
|> DataFrame.summarise(avg_price: mean(close), data_points: count())

Universal Parameters - Work with ANY Provider

# Standard intervals (auto-translated per provider)
intervals = ["1m", "5m", "15m", "30m", "1h", "1d", "1w", "1mo"]

# Standard periods (auto-translated per provider)
periods = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "max"]

# Standard currencies (crypto providers)
currencies = ["usd", "eur", "btc", "eth"]

# Real-time quotes with universal parameters
{:ok, df} = Quant.Explorer.quote(["AAPL", "MSFT"], provider: :yahoo_finance)
{:ok, df} = Quant.Explorer.quote(["BTCUSDT", "ETHUSDT"], provider: :binance)
{:ok, df} = Quant.Explorer.quote("AAPL", provider: :alpha_vantage, api_key: "key")

# Symbol search with universal parameters
{:ok, df} = Quant.Explorer.search("Apple", provider: :yahoo_finance)  
{:ok, df} = Quant.Explorer.search("Bitcoin", provider: :coin_gecko)
{:ok, df} = Quant.Explorer.search("Microsoft", provider: :alpha_vantage, api_key: "key")

# Company info
{:ok, info} = Quant.Explorer.info("AAPL", provider: :yahoo_finance)
{:ok, info} = Quant.Explorer.info("bitcoin", provider: :coin_gecko)

Backward Compatibility

# fetch/2 is now an alias for history/2 - same standardized output
{:ok, df} = Quant.Explorer.fetch("AAPL", provider: :yahoo_finance, interval: "1d", period: "1y")
# Identical to:
{:ok, df} = Quant.Explorer.history("AAPL", provider: :yahoo_finance, interval: "1d", period: "1y")
# Multiple symbols at once
{:ok, df} = Quant.Explorer.fetch(["AAPL", "MSFT", "GOOGL"], provider: :yahoo_finance, period: "1mo")
# Real-time quotes

{:ok, df} = Quant.Explorer.quote(["AAPL", "MSFT"], provider: :yahoo_finance)
{:ok, df} = Quant.Explorer.quote(["BTCUSDT", "ETHUSDT"], provider: :binance)
{:ok, df} = Quant.Explorer.quote(["bitcoin", "ethereum"], provider: :coin_gecko)
{:ok, df} = Quant.Explorer.quote("AAPL", provider: :alpha_vantage)
{:ok, df} = Quant.Explorer.quote("AAPL", provider: :twelve_data)
# Symbol search

{:ok, df} = Quant.Explorer.search("Apple", provider: :yahoo_finance)
{:ok, df} = Quant.Explorer.search("BTC", provider: :binance)
{:ok, df} = Quant.Explorer.search("bitcoin", provider: :coin_gecko)
{:ok, df} = Quant.Explorer.search("Microsoft", provider: :alpha_vantage)
{:ok, df} = Quant.Explorer.search("Apple", provider: :twelve_data)

Parameter Optimization Quick Start

vectorbt-like parameter optimization for systematic strategy tuning:

# 1. Get historical data
{:ok, df} = Quant.Explorer.history("AAPL", provider: :yahoo_finance, period: "1y")

# 2. Define parameter ranges to test
param_ranges = %{
  fast_period: 5..15,    # Test fast SMA periods 5-15
  slow_period: 20..30    # Test slow SMA periods 20-30
}

# 3. Run optimization (tests all combinations)
{:ok, results} = Quant.Strategy.Optimization.run_combinations(df, :sma_crossover, param_ranges)

# 4. Find best parameters
best = Quant.Strategy.Optimization.find_best_params(results, :total_return)
# => %{fast_period: 8, slow_period: 24, total_return: 0.187, sharpe_ratio: 1.43}

# 5. Export results for analysis
:ok = Quant.Strategy.Optimization.Export.to_csv(results, "optimization_results.csv")

๐Ÿš€ Advanced optimization features:

# Parallel processing (4x faster)
{:ok, results} = Quant.Strategy.Optimization.run_combinations_parallel(
  df, :sma_crossover, param_ranges, concurrency: 4
)

# Walk-forward optimization (prevents overfitting)
{:ok, wf_results} = Quant.Strategy.Optimization.walk_forward_optimization(
  df, :sma_crossover, param_ranges, window_size: 252, step_size: 63
)

# Memory-efficient streaming (for large parameter spaces)  
results_stream = Quant.Strategy.Optimization.run_combinations_stream(
  df, :sma_crossover, %{period: 5..100}, chunk_size: 20
)

Features

Standardization Benefits - PRODUCTION READY โœ…

๐ŸŽฏ Universal Parameters: interval: "1d" works with ALL providers - TESTED & VERIFIED
๐Ÿ“Š Identical Schemas: All DataFrames have exactly 12 columns regardless of provider
โšก Automatic Translation: Provider-specific formats handled internally (Binance "1h" โ†” Yahoo "1h" โ†” Alpha Vantage "60min")
๐Ÿ” Rich Metadata: Provider, currency, and timezone columns for complete traceability
๐Ÿ›ก๏ธ Type Safety: Strong typing and validation throughout the standardization pipeline
๐Ÿš€ Performance: Optimized transformations for high-throughput analysis (tested with 1000+ data points)

Standardization Achievements:

Identical Output Schemas - GUARANTEED ๐ŸŽฏ

Every provider returns these exact schemas - no exceptions:

Historical Data (12 columns exactly)

["symbol", "timestamp", "open", "high", "low", "close", "volume", 
 "adj_close", "market_cap", "provider", "currency", "timezone"]

# Real example from ANY provider:
#Explorer.DataFrame<
#  Polars[100 x 12]  # Always exactly 12 columns
#  symbol string ["BTCUSDT", "AAPL", ...]
#  timestamp datetime[ฮผs, Etc/UTC] [2025-09-21 19:00:00.000000Z, ...]
#  open f64 [115530.89, 150.25, ...]
#  close f64 [115480.05, 151.30, ...]
#  market_cap null/f64 [nil, 2.5e12, ...]  # nil for crypto historical, populated for stocks
#  provider string ["binance", "yahoo_finance", ...]

Quote Data (12 columns exactly)

["symbol", "price", "change", "change_percent", "volume", "high_24h", 
 "low_24h", "market_cap", "timestamp", "provider", "currency", "market_state"]

Search Results (11 columns exactly)

["symbol", "name", "type", "exchange", "currency", "country", 
 "sector", "industry", "market_cap", "provider", "match_score"]

How Standardization Works ๐Ÿ”ง

Parameter Translation Engine

# Your input: Universal parameters
Quant.Explorer.history("AAPL", provider: :alpha_vantage, interval: "1h")

# Automatic translation:
# Quant.Explorer    โ†’ Alpha Vantage API
# "1h"          โ†’ "60min"
# "1d"          โ†’ "daily" 
# "1w"          โ†’ "weekly"

Schema Standardization Pipeline

# 1. Raw provider data (varies by provider)
Binance: ["symbol", "open_time", "close_time", "quote_volume", ...] # 16 columns
Yahoo:   ["Date", "Open", "High", "Adj Close", ...]                # 7 columns

# 2. Standardization engine processes:
# - Normalizes column names: "open_time" โ†’ "timestamp"
# - Filters provider-specific columns: removes "close_time", "quote_volume"
# - Adds missing columns: ensures "market_cap" exists (nil if not available)
# - Adds metadata: "provider", "currency", "timezone"

# 3. Final output (IDENTICAL across all providers):
["symbol", "timestamp", "open", "high", "low", "close", "volume", 
 "adj_close", "market_cap", "provider", "currency", "timezone"]  # Always 12

Cross-Asset Consistency

# Stocks: market_cap from company data, adj_close properly calculated
# Crypto: market_cap = nil (honest about availability), adj_close = close
# All:    Universal OHLCV structure enables cross-asset analysis

๐Ÿ“Š Supported Data & Endpoints

Provider Historical Real-time Quotes Symbol Search Company Info Options Data Crypto Support
Yahoo Finance โœ… All periods โœ… Multi-symbol โœ… Full search โœ… Fundamentals โœ… Options chains โœ… Major pairs
Alpha Vantage โœ… Premium data โœ… Real-time โœ… Symbol lookup โœ… Company data โŒ Not available โŒ Stocks only
Binance โœ… All intervals โœ… 24hr stats โœ… Pair search โŒ Crypto only โŒ Not applicable โœ… All pairs
CoinGecko โœ… Historical โœ… Live prices โœ… Coin search โœ… Market data โŒ Not applicable โœ… Full coverage
Twelve Data โœ… Global markets โœ… Real-time โœ… Advanced search โœ… Fundamentals โŒ Not available โœ… Major pairs

Cryptocurrency Support

Get crypto data from Binance with full support for:

# Bitcoin historical data
{:ok, df} = Quant.Explorer.fetch("BTCUSDT", provider: :binance, interval: "1h", limit: 100)

# Multiple crypto pairs
{:ok, df} = Quant.Explorer.quote(["BTCUSDT", "ETHUSDT", "ADAUSDT", "DOTUSDT"], provider: :binance)

# Search crypto pairs
{:ok, df} = Quant.Explorer.search("ETH", provider: :binance)

# All available trading pairs
{:ok, df} = Quant.Explorer.Providers.Binance.get_all_symbols()

# Custom time ranges for crypto analysis
{:ok, df} = Quant.Explorer.Providers.Binance.history_range("BTCUSDT", "5m", start_time, end_time)

Supported Binance intervals: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M

Advanced Usage

# Stream large datasets
stream = Quant.Explorer.Providers.YahooFinance.history_stream("AAPL", period: "max")
df = stream |> Enum.to_list() |> List.first()

# Custom date ranges
{:ok, df} = Quant.Explorer.fetch("AAPL", 
  start_date: ~D[2023-01-01], 
  end_date: ~D[2023-12-31],
  interval: "1d"
)

# Options chain
{:ok, options} = Quant.Explorer.Providers.YahooFinance.options("AAPL")

# Alpha Vantage premium data (requires API key)
{:ok, df} = Quant.Explorer.Providers.AlphaVantage.history("MSFT", interval: "5min", outputsize: "full")
{:ok, df} = Quant.Explorer.Providers.AlphaVantage.quote("MSFT")
{:ok, df} = Quant.Explorer.Providers.AlphaVantage.search("Apple")

# Crypto klines with custom intervals
{:ok, df} = Quant.Explorer.Providers.Binance.history("BTCUSDT", interval: "15m", limit: 500)

# All crypto trading pairs
{:ok, df} = Quant.Explorer.Providers.Binance.get_all_symbols()

# Crypto 24hr statistics
{:ok, df} = Quant.Explorer.Providers.Binance.quote(["BTCUSDT", "ETHUSDT", "ADAUSDT"])

Parameter Optimization - Advanced Examples

# Complex multi-parameter optimization
{:ok, df} = Quant.Explorer.history("AAPL", provider: :yahoo_finance, period: "2y")

# Test RSI + Bollinger Bands strategy
param_ranges = %{
  rsi_period: 10..20,
  rsi_overbought: 70..80, 
  rsi_oversold: 20..30,
  bb_period: 15..25,
  bb_std_dev: 1.5..2.5
}

# Parallel optimization across all combinations
{:ok, results} = Quant.Strategy.Optimization.run_combinations_parallel(
  df, :rsi_bollinger, param_ranges, 
  concurrency: System.schedulers_online(),
  initial_capital: 10_000.0,
  commission: 0.001
)

# Analyze parameter correlations
correlations = Quant.Strategy.Optimization.Results.parameter_correlation(
  results, :total_return, [:rsi_period, :bb_period]
)

# Walk-forward optimization for robustness
{:ok, wf_results} = Quant.Strategy.Optimization.walk_forward_optimization(
  df, :rsi_bollinger, param_ranges,
  window_size: 252,  # 1 year training window
  step_size: 21,     # Monthly reoptimization
  min_trades: 10     # Require minimum trades
)

# Export comprehensive results
:ok = Quant.Strategy.Optimization.Export.to_csv(results, "full_optimization.csv", 
  delimiter: ",", precision: 4)

:ok = Quant.Strategy.Optimization.Export.to_json(wf_results, "walkforward_results.json")

# Generate summary report
:ok = Quant.Strategy.Optimization.Export.summary(results, "optimization_summary.txt",
  include_correlations: true, top_n: 10)

# Memory-efficient streaming for very large parameter spaces
large_ranges = %{
  fast_period: 5..50,      # 46 values
  slow_period: 51..200,    # 150 values  
  signal_period: 5..20     # 16 values
}
# Total: 46 * 150 * 16 = 110,400 combinations!

# Process in chunks to avoid memory issues
results_stream = Quant.Strategy.Optimization.run_combinations_stream(
  df, :triple_sma, large_ranges, chunk_size: 1000
)

# Process results as they come in
best_so_far = results_stream
|> Stream.map(fn {:ok, chunk_df} ->
  chunk_df
  |> DataFrame.filter(col("total_return") > 0.1)  # Filter profitable combinations
  |> DataFrame.slice_head(10)  # Keep top 10 per chunk
end)
|> Stream.reject(&(DataFrame.n_rows(&1) == 0))  # Skip empty chunks
|> Enum.reduce(fn chunk, acc -> 
  DataFrame.concat_rows([acc, chunk])
  |> DataFrame.sort_by(desc(col("total_return")))  
  |> DataFrame.slice_head(50)  # Keep top 50 overall
end)

Configuration

Alpha Vantage API Key

To use Alpha Vantage (premium financial data), set your API key:

export ALPHA_VANTAGE_API_KEY="your_api_key_here"
export TWELVE_DATA_API_KEY="your_api_key_here"

Or in your application config:

config :quant,
  api_keys: %{
    alpha_vantage: "your_api_key_here",
    twelve_data: "your_api_key_here"
    # coin_gecko: "your_pro_api_key"  # Optional for CoinGecko Pro
  }

โš ๏ธ Alpha Vantage Free Tier Limitations:

API Keys in Function Calls

For Livebook, multi-client applications, or dynamic API key management, you can pass API keys directly in function calls instead of configuring them globally:

# Pass API keys directly (great for Livebook!)
{:ok, df} = Quant.Explorer.fetch("AAPL", 
  provider: :alpha_vantage,
  api_key: "your_alpha_vantage_key"
)

{:ok, df} = Quant.Explorer.quote("AAPL",
  provider: :twelve_data, 
  api_key: "your_twelve_data_key"
)

{:ok, df} = Quant.Explorer.search("Apple",
  provider: :alpha_vantage,
  api_key: "your_api_key"
)

# Works with all provider functions
{:ok, info} = Quant.Explorer.info("AAPL",
  provider: :twelve_data,
  api_key: "your_api_key" 
)

Multi-Client Scenarios

This is particularly useful when serving multiple clients with different API keys:

defmodule TradingService do
  def get_stock_data(symbol, client_id) do
    api_key = get_api_key_for_client(client_id)
    
    Quant.Explorer.fetch(symbol,
      provider: :alpha_vantage,
      api_key: api_key,
      interval: "daily"
    )
  end
  
  defp get_api_key_for_client(client_id) do
    # Fetch from database, environment, etc.
    MyApp.Repo.get_client_api_key(client_id)
  end
end

Livebook Examples

Perfect for data science and research in Livebook:

# Cell 1: Setup
Mix.install([{:quant, github: "the-nerd-company/quant"}])

# Cell 2: Get data with inline API key
api_key = "your_alpha_vantage_api_key"

{:ok, aapl} = Quant.Explorer.fetch("AAPL", 
  provider: :alpha_vantage,
  api_key: api_key,
  interval: "daily",
  outputsize: "compact"
)

{:ok, msft} = Quant.Explorer.fetch("MSFT",
  provider: :twelve_data, 
  api_key: "your_twelve_data_key",
  interval: "1day",
  outputsize: 50
)

# Cell 3: Analyze with Explorer
Explorer.DataFrame.describe(aapl)

Benefits of inline API keys:

Troubleshooting

Common API Issues

Alpha Vantage {:error, :symbol_not_found}:

Alpha Vantage {:error, {:api_key_error, "Demo API key detected..."}}:

Twelve Data RuntimeError: API key is required:

CoinGecko slow responses:

Rate limiting errors:

Error Handling Examples

# Handle API errors gracefully
case Quant.Explorer.quote("AAPL", provider: :alpha_vantage) do
  {:ok, df} -> 
    IO.puts("Got data!")
    df
  
  {:error, :provider_required} ->
    IO.puts("Provider must be specified explicitly - no defaults!")
    
  {:error, :symbol_not_found} -> 
    IO.puts("Symbol not found - try a different symbol")
    
  {:error, {:api_key_error, msg}} -> 
    IO.puts("API key issue: #{msg}")
    # Common message: "Demo API key detected. Please get a free API key at https://www.alphavantage.co/support/#api-key"
    
  {:error, :rate_limited} -> 
    IO.puts("Rate limited - wait and try again")
    
  {:error, reason} -> 
    IO.puts("Other error: #{inspect(reason)}")
end

# Fallback to different providers
def get_quote(symbol) do
  case Quant.Explorer.quote(symbol, provider: :yahoo_finance) do
    {:ok, df} -> {:ok, df}
    {:error, _} -> 
      # Try Alpha Vantage as fallback
      Quant.Explorer.quote(symbol, provider: :alpha_vantage)
  end
end

Testing

Quant.Explorer includes both fast mocked tests and integration tests:

# Run default tests (mocked, fast, no API calls)
mix test                              # ~0.3s, all mocked tests

# Run integration tests (real API calls, slower)
mix test --include integration        # Real HTTP requests to APIs

# Run specific test type
mix test --only mocked               # Only mocked tests  
mix test --only integration          # Only real API tests

Default behavior:

See TESTING.md for detailed testing documentation.

License

This project is licensed under CC BY-NC 4.0, which means:

โœ… You can:

โŒ You cannot:

This ensures the library remains free for the community while protecting against unauthorized commercial exploitation.