
Crypto Ta Analyzer
- 1.5k installs
- 84 repo stars
- Updated April 8, 2026
- dkyazzentwatwa/chatgpt-skills
crypto-ta-analyzer runs deterministic multi-indicator technical analysis on OHLCV market data.
About
The crypto-ta-analyzer skill runs deterministic technical analysis on normalized OHLCV data instead of narrative market opinions. Workflow starts by reshaping source data with scripts/data_converter.py or scripts/coingecko_converter.py, then executes scripts/ta_analyzer.py for the bundled indicator stack and signal scoring. Agents explain indicator agreement, conflicts, and regime sensitivity rather than presenting a single number without context. Guardrails forbid guaranteed-outcome language and keep deterministic output separate from discretionary interpretation. Use when users want RSI, MACD, volume, or divergence reads on crypto or equities with reproducible scripts rather than subjective trading advice. Normalizes OHLCV via data_converter.py or coingecko_converter.py before analysis Runs scripts/ta_analyzer.py for bundled indicators and signal scoring Explains indicator agreement, conflicts, and regime sensitivity in output Separates deterministic indicator output from discretionary interpretation Forbids presenting signals as guaranteed trading outcomes crypto-ta-analyzer runs deterministic multi-indicator technical analysis on OHLCV market data Scored indicator stack with e.
- Normalizes OHLCV via data_converter.py or coingecko_converter.py before analysis.
- Runs scripts/ta_analyzer.py for bundled indicators and signal scoring.
- Explains indicator agreement, conflicts, and regime sensitivity in output.
- Separates deterministic indicator output from discretionary interpretation.
- Forbids presenting signals as guaranteed trading outcomes.
Crypto Ta Analyzer by the numbers
- 1,543 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #93 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
crypto-ta-analyzer capabilities & compatibility
- Capabilities
- ohlcv normalization · multi indicator scoring · regime sensitivity notes · script backed analysis · guardrailed interpretation
- Use cases
- trading · data analysis
npx skills add https://github.com/dkyazzentwatwa/chatgpt-skills --skill crypto-ta-analyzerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 84 |
| Security audit | 3 / 3 scanners passed |
| Last updated | April 8, 2026 |
| Repository | dkyazzentwatwa/chatgpt-skills ↗ |
How do I get reproducible technical indicator analysis on crypto or market price data?
Run multi-indicator technical analysis on crypto or market OHLCV data with deterministic trend, momentum, volume, and divergence scoring.
Who is it for?
Analysts needing script-backed TA on OHLCV rather than narrative market takes.
Skip if: Discretionary trade recommendations without underlying OHLCV data.
When should I use this skill?
User asks for technical analysis, indicator stack, or OHLCV signal scoring on crypto or markets.
What you get
Scored indicator stack with explained agreement, conflicts, and regime context.
- TA summaries
- trading signal reports
- pair analysis notes
Files
Crypto TA Analyzer
Use the bundled indicators when the user needs explicit technical analysis rather than a narrative market opinion.
Workflow
1. Get normalized OHLCV data first. 2. Use scripts/data_converter.py or scripts/coingecko_converter.py when source formats need reshaping. 3. Run scripts/ta_analyzer.py for the actual indicator stack and signal scoring. 4. Explain indicator agreement, conflicts, and regime sensitivity instead of presenting one number without context.
Guardrails
- Do not present signals as guaranteed outcomes.
- Keep the distinction clear between deterministic indicator output and discretionary interpretation.
Bud100� ferenc
referencesph1Scomp0moDDblob @� @� @� @
referencesvSrnlongreportslg1Scomp0reportsmoDDblob�����w�AreportsmodDblob�����w�Areportsph1Scomp@scriptsbwspblob�bplist00�]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{233, 189}, {920, 436}} #/;R_klmno�
�scriptslg1ScompP$scriptslsvCblobbbplist00�
XYZ[\]Z_useRelativeDates_showIconPreviewWcolumns_calculateAllSizes_viewOptionsVersion_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSizeZaxTextSize � %).38=AFJNR�WvisibleUwidthYascendingZidentifier , Tname�UwidthYascendingWvisibleXubiquity#�"$ �\dateModified�"([dateCreated�+- aTsize�02 s Tkind�57d Ulabel�:<K Wversion�@ Xcomments�CE�^dateLastOpened�CIZshareOwner�CM_shareLastEditor�O"YdateAdded�TV�_invitationStatus##@.#�c�Tname#@02DL`u���������������!)2456?@BCPYZ[gpqsty����������������������������)*+4578KL]fox}^�scriptslsvpblob�bplist00�
HIJKLMJ_useRelativeDates_showIconPreviewWcolumns_calculateAllSizes_viewOptionsVersion_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSizeZaxTextSize �!&+059>BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened� UindexUwidthYascendingWvisible, �"# d �'( K �,- ��12 a �6- �:;s �? �CD �##@.#�c�Tname#@02DL`u��������������$-39CKMPQR[]_`ajlnopy{}~�����������������������������NscriptsmoDDblob�����x�AscriptsmodDblob�����x�Ascriptsph1Scomp�scriptsvSrnlong
.venvlg1Scomp�n�.venvmoDDblob��l�w�A.venvmodDblob��l�w�A.venvph1Scomp��
referencesbwspblob�bplist00�]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{313, -17}, {920, 436}} #/;R_klmno�
�
referenceslg1Scomp)�
referenceslsvCblobbbplist00�
XYZ[\]Z_useRelativeDates_showIconPreviewWcolumns_calculateAllSizes_viewOptionsVersion_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSizeZaxTextSize � %).38=AFJNR�WvisibleUwidthYascendingZidentifier , Tname�UwidthYascendingWvisibleXubiquity#�"$ �\dateModified�"([dateCreated�+- aTsize�02 s Tkind�57d Ulabel�:<K Wversion�@ Xcomments�CE�^dateLastOpened�CIZshareOwner�CM_shareLastEditor�O"YdateAdded�TV�_invitationStatus##@.#�c�Tname#@02DL`u���������������!)2456?@BCPYZ[gpqsty����������������������������)*+4578KL]fox}^�
referenceslsvpblob�bplist00�
HIJKLMJ_useRelativeDates_showIconPreviewWcolumns_calculateAllSizes_viewOptionsVersion_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSizeZaxTextSize �!&+059>BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened� UindexUwidthYascendingWvisible, �"# d �'( K �,- ��12 a �6- �:;s �? �CD �##@.#�c�Tname#@02DL`u��������������$-39CKMPQR[]_`ajlnopy{}~�����������������������������N
referencesmoDDblob�I��w�A
referencesmodDblob�I��w�Ablob�����x�Ascriptsph1Scomp�scriptsvSrnlong0E� DSDB `�8@� @� @^�
referenceslsvpblob�bplist00�
HIJKLMJ_useRelativeDates_showIconPreviewWcolumns_calculateAllSizes_viewOptionsVersion_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSizeZaxTextSize �!&+059>BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened� UindexUwidthYascendingWvisible, �"# d �'( K �,- ��12 a �6- �:;s �? �CD �##@.#�c�Tname#@02DL`u��������������$-39CKMPQR[]_`ajlnopy{}~�����������������������������N
refe# Reports (generated analysis output)
reports/
# Virtual environment
.venv/
venv/
env/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Jupyter Notebook
.ipynb_checkpoints
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Temp files
*.tmp
*.temp
/tmp/
display_name: 'Crypto TA Analyzer'
short_description: 'Run multi-indicator technical analysis on OHLCV data.'
default_prompt: 'Help me analyze this market data with technical indicators.'
CLAUDE.md - Crypto TA Analyzer
Overview
This is a comprehensive cryptocurrency and stock technical analysis skill using 29 indicators. It generates 7-tier trading signals (STRONG_BUY to STRONG_SELL) with divergence detection, volume confirmation, and Bollinger Band squeeze alerts.
Quick Start
# Activate virtual environment
source .venv/bin/activate
# Run analysis
from scripts.data_converter import normalize_ohlcv
from scripts.ta_analyzer import TechnicalAnalyzer
ohlcv_df, metadata = normalize_ohlcv(raw_data, source="auto")
analyzer = TechnicalAnalyzer(ohlcv_df)
results = analyzer.analyze_all()Key Files
| File | Purpose |
|---|---|
scripts/ta_analyzer.py | Main analysis engine (29 indicators) |
scripts/data_converter.py | Generic data converter (CoinGecko, exchanges, Yahoo) |
scripts/coingecko_converter.py | Legacy CoinGecko converter (backward compatible) |
SKILL.md | Full skill documentation |
references/indicators.md | Detailed indicator reference |
Dependencies
numpy>=1.21.0
pandas>=1.3.0Output Keys
Primary signals:
tradeSignal7Tier: STRONG_BUY, BUY, WEAK_BUY, NEUTRAL, WEAK_SELL, SELL, STRONG_SELLtradeSignal: Legacy (STRONG_UPTREND, NEUTRAL, DOWNTREND)confidence: 0-1 confidence scoretradeTrigger: Boolean for high-conviction entries
New features:
divergences: RSI/MACD/OBV divergence detectionsqueezeDetected: Bollinger Band squeeze alertvolumeConfirmation: Volume agreement score
Indicators (29)
Core (1.0): RSI, MACD, BB, OBV, ICHIMOKU, EMA, SMA, MFI, KDJ, SAR Strong (0.75): DEMA, MESA, CCI, AROON, APO Supporting (0.5): ADX, DMI, CMO, KAMA, MOMI, PPO, ROC, TRIMA, TRIX, T3, WMA, VWAP, ATR_SIGNAL, CAD
Testing
# Fetch and analyze BTC
source .venv/bin/activate
python3 -c "
from scripts.data_converter import normalize_ohlcv
from scripts.ta_analyzer import TechnicalAnalyzer
import urllib.request, json, ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
url = 'https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=7'
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
data = json.loads(urllib.request.urlopen(req, timeout=30, context=ctx).read())
df, _ = normalize_ohlcv(data, source='coingecko')
results = TechnicalAnalyzer(df).analyze_all()
print(f'Signal: {results[\"tradeSignal7Tier\"]} ({results[\"confidence\"]*100:.0f}% confidence)')
"Development Notes
- Virtual environment in
.venv/(not committed) - Reports saved to
reports/(not committed) - All indicator algorithms are pure numpy/pandas (no TA-Lib dependency)
- Ichimoku uses crypto-optimized periods (10/30/60)
- Backward compatible with legacy output keys
Technical Indicator Reference Guide
Comprehensive guide to all 29 technical indicators used in the crypto-ta-analyzer skill.
Table of Contents
1. New Indicators (v2) 2. Trend Indicators 3. Momentum Indicators 4. Volume Indicators 5. Volatility Indicators 6. Divergence Detection 7. Scoring System
---
New Indicators (v2)
Bollinger Bands (BB)
Weight: 1.0 Parameters: period=20, std_dev=2.0, squeeze_threshold=5.0
Volatility-based bands around a moving average. Key components:
- Middle Band: 20-period SMA
- Upper Band: Middle + 2σ (standard deviations)
- Lower Band: Middle - 2σ
- Bandwidth: (Upper - Lower) / Middle * 100
- %B: (Price - Lower) / (Upper - Lower) — position within bands
Signals:
- In RANGING markets: Mean reversion (buy at lower band, sell at upper)
- In TRENDING markets: Breakout continuation (riding bands is bullish/bearish)
- Squeeze Detection: When bandwidth < threshold for 6+ bars, low volatility may precede breakout
On-Balance Volume (OBV)
Weight: 1.0 Parameters: signal_period=20
Cumulative volume indicator that adds volume on up days, subtracts on down days. Formula: OBV = prev_OBV ± volume (based on close vs prev_close)
Signals:
- OBV above signal line = bullish
- OBV crossing above signal = buy trigger
- Divergence: OBV diverging from price often precedes reversals (volume leads price)
Ichimoku Cloud
Weight: 1.0 Parameters: tenkan=10, kijun=30, senkou_b=60 (crypto-optimized)
Multi-component Japanese trend system. Components:
- Tenkan-sen (Conversion): (10-period high + low) / 2
- Kijun-sen (Base): (30-period high + low) / 2
- Senkou Span A: (Tenkan + Kijun) / 2, shifted forward
- Senkou Span B: (60-period high + low) / 2, shifted forward
- Cloud (Kumo): Area between Senkou A and B
Signals:
- Price above cloud = bullish, below = bearish, inside = neutral
- Tenkan above Kijun = bullish cross
- Cloud color (A > B = green/bullish)
VWAP (Volume Weighted Average Price)
Weight: 0.5
Institutional benchmark price. Formula: Cumulative(TP * Volume) / Cumulative(Volume)
Signals:
- Price above VWAP = bullish bias
- Price below VWAP = bearish bias
- Used by institutions for execution benchmarks
ATR Signal
Weight: 0.5 Parameters: period=14
ATR (Average True Range) exposed as a signal indicator for volatility-based trading.
Signals:
- Low volatility (ATR contraction) = potential breakout setup
- High volatility + trending = trend confirmation
- Combines with regime detection for context-aware signals
---
Trend Indicators
SMA (Simple Moving Average)
Weight: 1.0 Timeframes: 20-period (short), 50-period (long)
Crossover strategy - bullish when short crosses above long, bearish when crosses below.
EMA (Exponential Moving Average)
Weight: 1.0 Timeframes: 12-period (short), 26-period (long)
More responsive than SMA due to exponential weighting of recent prices.
DEMA (Double Exponential Moving Average)
Weight: 1.0 Timeframe: 30-period
Reduces lag compared to SMA/EMA by double smoothing.
TRIMA (Triangular Moving Average)
Weight: 0.5 Timeframe: 30-period
Gives more weight to middle portion of data, smoother than SMA.
WMA (Weighted Moving Average)
Weight: 0.5 Timeframe: 30-period
Linear weighting with most recent data weighted highest.
KAMA (Kaufman Adaptive Moving Average)
Weight: 0.5 Timeframe: 30-period
Adapts to market volatility - fast in trending markets, slow in ranging.
T3 (Tillson T3)
Weight: 0.5 Timeframe: 5-period
Smooth moving average with reduced lag and overshooting.
TRIX (Triple Exponential Moving Average)
Weight: 0.5 Timeframe: 30-period
Triple smoothed to filter out insignificant price movements.
MESA (MESA Adaptive Moving Average)
Weight: 1.0 Parameters: fastlimit=0.5, slowlimit=0.05
Adaptive indicator that adjusts to current market cycle period.
Parabolic SAR
Weight: 1.0 Parameters: acceleration=0.02, maximum=0.2
Stop and Reverse system - dots above price suggest downtrend, below suggests uptrend.
---
Momentum Indicators
RSI (Relative Strength Index)
Weight: 1.0 Timeframe: 14-period Thresholds: Oversold <30, Overbought >70
Measures speed and magnitude of price changes. Classic overbought/oversold indicator.
MACD (Moving Average Convergence Divergence)
Weight: 1.0 Parameters: fast=12, slow=26, signal=9
Trend-following momentum indicator showing relationship between two EMAs.
MOM (Momentum)
Weight: 0.5 Timeframe: 10-period
Simple rate of price change measurement.
ROC (Rate of Change)
Weight: 0.5 Timeframe: 10-period
Percentage change in price over specified period.
CMO (Chande Momentum Oscillator)
Weight: 0.5 Timeframe: 14-period Thresholds: Overbought >50, Oversold <-50
Modified RSI using sum of gains/losses rather than averages.
PPO (Percentage Price Oscillator)
Weight: 0.5 Parameters: fast=12, slow=26
MACD expressed in percentage terms for easier comparison across securities.
APO (Absolute Price Oscillator)
Weight: 1.0 Parameters: fast=12, slow=26
Difference between two moving averages expressed in absolute terms.
CCI (Commodity Channel Index)
Weight: 1.0 Timeframe: 14-period Thresholds: Overbought >100, Oversold <-100
Identifies cyclical trends - how far price deviates from average.
AROON
Weight: 1.0 Timeframe: 14-period Thresholds: Strong trend >70
Two lines (up/down) identify trend presence and direction.
KDJ (Stochastic with J line)
Weight: 1.0 Parameters: K=9, D=3, J=3K-2D Thresholds: Oversold <20, Overbought >80
Enhanced stochastic with J line for earlier signals.
---
Volume Indicators
MFI (Money Flow Index)
Weight: 1.0 Timeframe: 14-period Thresholds: Oversold <20, Overbought >80
Volume-weighted RSI - incorporates both price and volume.
---
Directional Indicators
ADX (Average Directional Index)
Weight: 0.5 Timeframe: 14-period Threshold: Strong trend >25
Measures trend strength regardless of direction.
DMI (Directional Movement Index)
Weight: 0.5 Timeframe: 14-period
Component of ADX - +DI and -DI lines show directional movement.
---
Divergence Detection
Divergences occur when price and indicator move in opposite directions, often signaling reversals.
Types of Divergence
Regular Bullish Divergence:
- Price makes lower low
- Indicator makes higher low
- Signal: Potential reversal upward
Regular Bearish Divergence:
- Price makes higher high
- Indicator makes lower high
- Signal: Potential reversal downward
Hidden Bullish Divergence:
- Price makes higher low
- Indicator makes lower low
- Signal: Trend continuation in uptrend
Hidden Bearish Divergence:
- Price makes lower high
- Indicator makes higher high
- Signal: Trend continuation in downtrend
Indicators with Divergence Detection
1. RSI: Most common divergence indicator 2. MACD Histogram: Reliable for momentum divergences 3. OBV: Most reliable — volume precedes price
Divergence Confidence Ranking
1. OBV divergence: Highest reliability (volume leads price) 2. RSI divergence: High reliability on longer timeframes 3. MACD divergence: Good for momentum shifts
---
Scoring System
Score Weights (Updated v2)
Each indicator contributes to the total score based on its signal:
Core (1.0): RSI, MACD, BB, OBV, ICHIMOKU, EMA, SMA, MFI, KDJ, SAR Strong (0.75): DEMA, MESA, CCI, AROON, APO Supporting (0.5): ADX, DMI, CMO, KAMA, MOMI, PPO, ROC, TRIMA, TRIX, T3, WMA, VWAP, ATR_SIGNAL, CAD
7-Tier Signal System (NEW)
STRONG_BUY: Normalized score >= 0.5, confidence >= 0.7 BUY: Normalized score >= 0.35, confidence >= 0.5 WEAK_BUY: Normalized score >= 0.2 NEUTRAL: -0.2 < normalized < 0.2 WEAK_SELL: Normalized score <= -0.2 SELL: Normalized score <= -0.35, confidence >= 0.5 STRONG_SELL: Normalized score <= -0.5, confidence >= 0.7
Legacy Interpretation (backward compatible)
Total Score >= 7.0: STRONG UPTREND Total Score 3.0 - 6.9: NEUTRAL Total Score < 3.0: DOWNTREND
Confidence Calculation
Confidence is computed from multiple factors:
- Alignment (30%): How much indicators agree
- ADX Score (20%): Trend strength
- Coverage (15%): % of indicators computed
- Volume Confirmation (15%): OBV/MFI agreement
- Divergence Penalty (10%): Lower if divergences detected
- Volatility Adjustment (10%): Lower in extreme volatility
Volume Confirmation
New factor that measures if volume supports price direction:
- OBV trend matching price trend = +1.0
- MFI agreeing with price direction = +0.5 to +1.0
- No OBV divergence = +0.8
Best Practices
1. Never trade on single indicator - the scoring system's strength is in consensus 2. Watch divergences - especially OBV divergences (volume leads price) 3. Monitor BB squeeze - low volatility often precedes breakouts 4. Use Ichimoku cloud - price position vs cloud is strong trend filter 5. Check volume confirmation - ensure volume supports the signal 6. Consider market regime - trending vs ranging affects indicator reliability
High Conviction Setups
Strong Buy Setup:
- 7-tier: STRONG_BUY or BUY
- Confidence >= 0.7
- Price above Ichimoku cloud
- OBV confirms (no bearish divergence)
- No BB squeeze (or just breaking out of squeeze)
Breakout Setup:
- BB squeeze detected
- ADX rising from < 20
- Watch for band expansion with volume
Reversal Warning:
- Bearish divergence on RSI/MACD/OBV
- Price at upper BB (%B > 1.0)
- RSI > 70 or MFI > 80
---
Limitations and Considerations
- Lagging nature: Most indicators are calculated from past data
- False signals: Can generate whipsaws in choppy markets
- Market regime changes: What works in trends may fail in ranges
- Divergences: Not all divergences lead to reversals
- OHLC approximation: Price-only data sources approximate high/low
Recommended Minimum Data
- Absolute minimum: 50 data points
- Recommended: 100+ data points for reliable signals
- Optimal: 200+ data points for comprehensive analysis
- Ichimoku: Requires 60+ bars for full cloud calculation
numpy>=1.21.0
pandas>=1.3.0
#!/usr/bin/env python3
"""
Helper script to convert CoinGecko chart data to TA-compatible OHLCV format.
"""
import json
import pandas as pd
from typing import Any, Dict, List, Tuple, Union
def coingecko_to_ohlcv(chart_data: List[List]) -> pd.DataFrame:
"""
Convert CoinGecko chart data to OHLCV DataFrame.
CoinGecko returns: [[timestamp, price, market_cap, volume], ...]
We need: time, open, high, low, close, volume
Args:
chart_data: Raw chart data from CoinGecko
Returns:
DataFrame with OHLCV data
"""
df = pd.DataFrame(chart_data, columns=['timestamp', 'price', 'market_cap', 'volume'])
# Create OHLCV approximation from price data
# Since CoinGecko doesn't provide OHLC, we approximate:
ohlcv_data = []
for i in range(len(df)):
price = df.iloc[i]['price']
volume = df.iloc[i]['volume'] if not pd.isna(df.iloc[i]['volume']) else 0
# Approximate OHLC from price point
# In real scenarios, adjacent prices give us range
if i > 0:
prev_price = df.iloc[i-1]['price']
high = max(price, prev_price)
low = min(price, prev_price)
open_price = prev_price
else:
high = price
low = price
open_price = price
ohlcv_data.append({
'time': df.iloc[i]['timestamp'],
'open': open_price,
'high': high,
'low': low,
'close': price,
'volume': volume
})
return pd.DataFrame(ohlcv_data)
def prepare_analysis_data(coingecko_json: str) -> pd.DataFrame:
"""
Prepare CoinGecko JSON data for technical analysis.
Args:
coingecko_json: JSON string from CoinGecko chart endpoint
Returns:
DataFrame ready for TechnicalAnalyzer
"""
data = json.loads(coingecko_json)
# Handle different CoinGecko response formats
if 'prices' in data:
chart_data = data['prices']
else:
chart_data = data
return coingecko_to_ohlcv(chart_data)
def validate_data_quality(df: pd.DataFrame) -> Dict[str, Any]:
"""
Validate OHLCV data quality and return statistics.
Args:
df: OHLCV DataFrame
Returns:
Dictionary with data quality metrics
"""
return {
'total_records': len(df),
'missing_values': df.isnull().sum().to_dict(),
'price_range': {
'min': float(df['low'].min()),
'max': float(df['high'].max()),
'current': float(df['close'].iloc[-1])
},
'volume_stats': {
'avg': float(df['volume'].mean()),
'total': float(df['volume'].sum())
},
'data_quality': 'good' if len(df) >= 50 and df.isnull().sum().sum() == 0 else 'insufficient'
}
if __name__ == "__main__":
print("CoinGecko Data Converter")
print("=======================")
print("Use this module to convert CoinGecko API data to OHLCV format.")
#!/usr/bin/env python3
"""
Generic Data Converter for Technical Analysis
Supports multiple data sources:
- CoinGecko API format
- Binance/Exchange OHLCV format
- Yahoo Finance format
- Generic price-only data
- Auto-detection of format
This module converts any supported format to standardized OHLCV DataFrame
ready for TechnicalAnalyzer.
"""
import json
import numpy as np
import pandas as pd
from typing import Any, Dict, List, Optional, Tuple, Union
# =============================================================================
# Format Detection
# =============================================================================
def detect_data_format(data: Union[pd.DataFrame, List, Dict, str]) -> str:
"""
Auto-detect the input data format.
Returns one of:
- "coingecko": CoinGecko chart format [[timestamp, price, market_cap, volume], ...]
- "exchange_ohlcv": Standard exchange OHLCV (5-12 columns)
- "yahoo": Yahoo Finance format (Date, Open, High, Low, Close, Adj Close, Volume)
- "price_only": Simple price data (timestamp, price) or just prices
- "ohlcv_dict": List of dicts with OHLCV keys
- "unknown": Unrecognized format
"""
# Parse JSON if string
if isinstance(data, str):
try:
data = json.loads(data)
except json.JSONDecodeError:
return "unknown"
# Handle dict with 'prices' key (CoinGecko wrapper)
if isinstance(data, dict):
if "prices" in data:
return "coingecko"
if "chart" in data and "result" in data.get("chart", {}):
return "yahoo"
return "unknown"
# Handle list
if isinstance(data, list):
if len(data) == 0:
return "unknown"
first = data[0]
# List of lists (array format)
if isinstance(first, (list, tuple)):
cols = len(first)
if cols == 4:
return "coingecko" # [timestamp, price, market_cap, volume]
elif cols == 2:
return "price_only" # [timestamp, price]
elif cols >= 5:
return "exchange_ohlcv" # [timestamp, o, h, l, c, v, ...]
return "unknown"
# List of dicts
if isinstance(first, dict):
keys = set(first.keys())
lower_keys = {k.lower() for k in keys}
# Check for OHLCV keys
ohlcv_keys = {"open", "high", "low", "close"}
if ohlcv_keys.issubset(lower_keys):
return "ohlcv_dict"
# Check for Yahoo Finance keys
if "adj close" in lower_keys or "adjclose" in lower_keys:
return "yahoo"
# Check for price-only
if "price" in lower_keys or "close" in lower_keys:
return "price_only"
return "unknown"
# Handle DataFrame
if isinstance(data, pd.DataFrame):
cols = {c.lower() for c in data.columns}
# Check for full OHLCV
if {"open", "high", "low", "close"}.issubset(cols):
if "adj close" in cols or "adjclose" in cols:
return "yahoo"
return "exchange_ohlcv"
# Check for price-only
if "close" in cols or "price" in cols:
return "price_only"
return "unknown"
return "unknown"
# =============================================================================
# Format-Specific Converters
# =============================================================================
def coingecko_to_ohlcv(chart_data: List[List], enhanced: bool = True) -> pd.DataFrame:
"""
Convert CoinGecko chart data to OHLCV DataFrame.
CoinGecko returns: [[timestamp, price, market_cap, volume], ...]
We approximate OHLC from price data.
Args:
chart_data: Raw chart data from CoinGecko
enhanced: Use enhanced OHLC approximation (recommended)
Returns:
DataFrame with OHLCV data
"""
if len(chart_data) == 0:
return pd.DataFrame(columns=['time', 'open', 'high', 'low', 'close', 'volume'])
# Handle different CoinGecko formats
if len(chart_data[0]) == 4:
df = pd.DataFrame(chart_data, columns=['timestamp', 'price', 'market_cap', 'volume'])
elif len(chart_data[0]) == 2:
df = pd.DataFrame(chart_data, columns=['timestamp', 'price'])
df['volume'] = 0.0
else:
df = pd.DataFrame(chart_data)
df.columns = ['timestamp', 'price'] + [f'col{i}' for i in range(2, len(df.columns))]
if 'volume' not in df.columns:
df['volume'] = 0.0
prices = df['price'].values
volumes = df['volume'].fillna(0).values if 'volume' in df.columns else np.zeros(len(df))
if enhanced:
ohlcv_df = _enhanced_ohlc_approximation(prices, volumes, df['timestamp'].values)
else:
ohlcv_df = _simple_ohlc_approximation(prices, volumes, df['timestamp'].values)
return ohlcv_df
def _simple_ohlc_approximation(
prices: np.ndarray,
volumes: np.ndarray,
timestamps: np.ndarray
) -> pd.DataFrame:
"""Simple OHLC approximation using adjacent prices."""
ohlcv_data = []
for i in range(len(prices)):
price = prices[i]
volume = volumes[i] if not np.isnan(volumes[i]) else 0
if i > 0:
prev_price = prices[i - 1]
high = max(price, prev_price)
low = min(price, prev_price)
open_price = prev_price
else:
high = price
low = price
open_price = price
ohlcv_data.append({
'time': timestamps[i],
'open': open_price,
'high': high,
'low': low,
'close': price,
'volume': volume
})
return pd.DataFrame(ohlcv_data)
def _enhanced_ohlc_approximation(
prices: np.ndarray,
volumes: np.ndarray,
timestamps: np.ndarray,
window: int = 3
) -> pd.DataFrame:
"""
Enhanced OHLC approximation using rolling window for better high/low estimates.
This method considers nearby prices to better estimate the true range
within each period.
"""
n = len(prices)
if n == 0:
return pd.DataFrame(columns=['time', 'open', 'high', 'low', 'close', 'volume'])
ohlcv_data = []
for i in range(n):
price = prices[i]
volume = volumes[i] if not np.isnan(volumes[i]) else 0
# Use rolling window for high/low estimation
start = max(0, i - window + 1)
end = min(n, i + 2) # Include current and next price if available
window_prices = prices[start:end]
if i > 0:
open_price = prices[i - 1]
else:
open_price = price
# Estimate high/low from window
high = float(np.max(window_prices))
low = float(np.min(window_prices))
# Ensure OHLC consistency
high = max(high, open_price, price)
low = min(low, open_price, price)
ohlcv_data.append({
'time': timestamps[i],
'open': open_price,
'high': high,
'low': low,
'close': price,
'volume': volume
})
return pd.DataFrame(ohlcv_data)
def exchange_ohlcv_to_df(data: Union[List, pd.DataFrame]) -> pd.DataFrame:
"""
Convert exchange OHLCV format to standardized DataFrame.
Handles formats like:
- [timestamp, open, high, low, close, volume, ...]
- DataFrame with OHLCV columns
"""
if isinstance(data, pd.DataFrame):
df = data.copy()
elif isinstance(data, list):
if len(data) == 0:
return pd.DataFrame(columns=['time', 'open', 'high', 'low', 'close', 'volume'])
if isinstance(data[0], dict):
df = pd.DataFrame(data)
else:
# List of lists
cols = len(data[0])
if cols >= 6:
col_names = ['time', 'open', 'high', 'low', 'close', 'volume'] + [f'col{i}' for i in range(6, cols)]
elif cols == 5:
col_names = ['time', 'open', 'high', 'low', 'close']
else:
col_names = [f'col{i}' for i in range(cols)]
df = pd.DataFrame(data, columns=col_names)
else:
raise ValueError(f"Unsupported data type: {type(data)}")
# Normalize column names
df = _normalize_columns(df)
return df
def yahoo_to_ohlcv(data: Union[Dict, pd.DataFrame]) -> pd.DataFrame:
"""Convert Yahoo Finance format to standardized OHLCV."""
if isinstance(data, dict):
# Yahoo API JSON format
if "chart" in data and "result" in data["chart"]:
result = data["chart"]["result"][0]
timestamps = result.get("timestamp", [])
quotes = result.get("indicators", {}).get("quote", [{}])[0]
df = pd.DataFrame({
'time': timestamps,
'open': quotes.get('open', []),
'high': quotes.get('high', []),
'low': quotes.get('low', []),
'close': quotes.get('close', []),
'volume': quotes.get('volume', [])
})
else:
df = pd.DataFrame(data)
else:
df = data.copy()
# Normalize columns
df = _normalize_columns(df)
# Handle Adj Close if present
if 'adj_close' in df.columns or 'adjclose' in df.columns:
adj_col = 'adj_close' if 'adj_close' in df.columns else 'adjclose'
# Use adjusted close as the close price
if df[adj_col].notna().any():
df['close'] = df[adj_col]
df = df.drop(columns=[adj_col], errors='ignore')
return df
def price_only_to_ohlcv(
data: Union[List, pd.DataFrame, pd.Series],
enhanced: bool = True
) -> pd.DataFrame:
"""
Convert price-only data to OHLCV format.
Args:
data: Price data (list, DataFrame with 'price'/'close', or Series)
enhanced: Use enhanced approximation
"""
if isinstance(data, pd.Series):
prices = data.values
timestamps = data.index.values if hasattr(data.index, 'values') else np.arange(len(data))
volumes = np.zeros(len(data))
elif isinstance(data, pd.DataFrame):
df = data.copy()
df = _normalize_columns(df)
if 'close' in df.columns:
prices = df['close'].values
elif 'price' in df.columns:
prices = df['price'].values
else:
raise ValueError("No price/close column found")
timestamps = df.get('time', pd.Series(np.arange(len(df)))).values
volumes = df.get('volume', pd.Series(np.zeros(len(df)))).values
elif isinstance(data, list):
if len(data) == 0:
return pd.DataFrame(columns=['time', 'open', 'high', 'low', 'close', 'volume'])
if isinstance(data[0], (list, tuple)) and len(data[0]) >= 2:
timestamps = np.array([x[0] for x in data])
prices = np.array([x[1] for x in data])
volumes = np.zeros(len(data))
else:
prices = np.array(data)
timestamps = np.arange(len(data))
volumes = np.zeros(len(data))
else:
raise ValueError(f"Unsupported data type: {type(data)}")
if enhanced:
return _enhanced_ohlc_approximation(prices, volumes, timestamps)
return _simple_ohlc_approximation(prices, volumes, timestamps)
# =============================================================================
# Normalization and Validation
# =============================================================================
def _normalize_columns(df: pd.DataFrame) -> pd.DataFrame:
"""Normalize column names to standard format."""
rename_map = {}
for c in df.columns:
cl = str(c).lower().strip()
if cl in ('timestamp', 'datetime', 'date', 't'):
rename_map[c] = 'time'
elif cl in ('o', 'open_price', 'openprice'):
rename_map[c] = 'open'
elif cl in ('h', 'high_price', 'highprice', 'max'):
rename_map[c] = 'high'
elif cl in ('l', 'low_price', 'lowprice', 'min'):
rename_map[c] = 'low'
elif cl in ('c', 'close_price', 'closeprice', 'price', 'last'):
rename_map[c] = 'close'
elif cl in ('v', 'vol', 'volume_24h', 'volume24h', 'base_volume'):
rename_map[c] = 'volume'
elif cl in ('adj close', 'adj_close', 'adjusted_close', 'adjclose'):
rename_map[c] = 'adj_close'
if rename_map:
df = df.rename(columns=rename_map)
return df
def validate_and_repair(df: pd.DataFrame) -> Tuple[pd.DataFrame, List[str]]:
"""
Validate OHLCV data and auto-repair common issues.
Returns:
(cleaned_df, warnings_list)
"""
warnings = []
df = df.copy()
# Ensure required columns exist
required = ['open', 'high', 'low', 'close']
for col in required:
if col not in df.columns:
warnings.append(f"Missing required column: {col}")
return df, warnings
# Ensure volume exists
if 'volume' not in df.columns:
df['volume'] = 0.0
warnings.append("Volume column missing, set to 0")
# Convert to numeric
for col in ['open', 'high', 'low', 'close', 'volume']:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
# Remove duplicate timestamps
if 'time' in df.columns:
before = len(df)
df = df.drop_duplicates(subset=['time'], keep='last')
after = len(df)
if after < before:
warnings.append(f"Removed {before - after} duplicate timestamps")
# Sort by time
if 'time' in df.columns:
df = df.sort_values('time').reset_index(drop=True)
# Fix OHLC inconsistencies
inconsistent_count = 0
for i in range(len(df)):
o, h, l, c = df.loc[i, 'open'], df.loc[i, 'high'], df.loc[i, 'low'], df.loc[i, 'close']
if pd.isna(o) or pd.isna(h) or pd.isna(l) or pd.isna(c):
continue
fixed = False
# High should be >= all others
if h < max(o, l, c):
df.loc[i, 'high'] = max(o, h, l, c)
fixed = True
# Low should be <= all others
if l > min(o, h, c):
df.loc[i, 'low'] = min(o, h, l, c)
fixed = True
if fixed:
inconsistent_count += 1
if inconsistent_count > 0:
warnings.append(f"Fixed {inconsistent_count} OHLC inconsistencies (high < low, etc.)")
# Fill small gaps with interpolation
null_counts = df[['open', 'high', 'low', 'close']].isnull().sum()
total_nulls = null_counts.sum()
if total_nulls > 0 and total_nulls < len(df) * 0.1: # Less than 10% missing
for col in ['open', 'high', 'low', 'close']:
df[col] = df[col].interpolate(method='linear', limit=3)
warnings.append(f"Interpolated {total_nulls} missing values")
# Drop rows with any remaining NaN in OHLC
before = len(df)
df = df.dropna(subset=['open', 'high', 'low', 'close']).reset_index(drop=True)
after = len(df)
if after < before:
warnings.append(f"Dropped {before - after} rows with missing OHLC data")
return df, warnings
def validate_data_quality(df: pd.DataFrame) -> Dict[str, Any]:
"""
Validate OHLCV data quality and return comprehensive statistics.
Args:
df: OHLCV DataFrame
Returns:
Dictionary with data quality metrics
"""
result = {
'total_records': len(df),
'missing_values': df.isnull().sum().to_dict() if len(df) > 0 else {},
'data_quality': 'unknown',
'issues': [],
}
if len(df) == 0:
result['data_quality'] = 'empty'
result['issues'].append('No data')
return result
# Price statistics
if 'close' in df.columns and df['close'].notna().any():
result['price_range'] = {
'min': float(df['low'].min()) if 'low' in df.columns else float(df['close'].min()),
'max': float(df['high'].max()) if 'high' in df.columns else float(df['close'].max()),
'current': float(df['close'].iloc[-1]),
'mean': float(df['close'].mean()),
}
# Volume statistics
if 'volume' in df.columns and df['volume'].notna().any():
result['volume_stats'] = {
'avg': float(df['volume'].mean()),
'total': float(df['volume'].sum()),
'zero_volume_pct': float((df['volume'] == 0).sum() / len(df) * 100),
}
# Time coverage
if 'time' in df.columns:
try:
times = pd.to_datetime(df['time'], errors='coerce')
if times.notna().any():
result['time_range'] = {
'start': str(times.min()),
'end': str(times.max()),
'duration_hours': float((times.max() - times.min()).total_seconds() / 3600),
}
except Exception:
pass
# Quality assessment
issues = []
if len(df) < 50:
issues.append('insufficient_data')
null_count = df[['open', 'high', 'low', 'close']].isnull().sum().sum()
if null_count > 0:
issues.append('missing_values')
if 'volume' in df.columns:
zero_vol_pct = (df['volume'] == 0).sum() / len(df)
if zero_vol_pct > 0.5:
issues.append('mostly_zero_volume')
result['issues'] = issues
if len(issues) == 0 and len(df) >= 100:
result['data_quality'] = 'good'
elif len(issues) == 0 and len(df) >= 50:
result['data_quality'] = 'acceptable'
elif 'insufficient_data' not in issues:
result['data_quality'] = 'fair'
else:
result['data_quality'] = 'insufficient'
return result
# =============================================================================
# Main Interface
# =============================================================================
def normalize_ohlcv(
data: Union[pd.DataFrame, List, Dict, str],
source: str = "auto",
enhanced_approximation: bool = True,
validate: bool = True,
) -> Tuple[pd.DataFrame, Dict[str, Any]]:
"""
Normalize any data source to standard OHLCV format.
This is the main entry point for data conversion.
Args:
data: Input data in any supported format
source: Data source hint ("auto", "coingecko", "exchange", "yahoo", "price_only")
enhanced_approximation: Use enhanced OHLC approximation for price-only data
validate: Run validation and repair
Returns:
(ohlcv_dataframe, metadata_dict)
Supported sources:
- "coingecko": [[timestamp, price, market_cap, volume], ...]
- "exchange": Standard OHLCV with 5-12 columns
- "yahoo": Yahoo Finance format
- "price_only": Any DataFrame/list with price data
- "auto": Auto-detect format
"""
metadata = {
'detected_format': None,
'original_rows': 0,
'final_rows': 0,
'warnings': [],
}
# Parse JSON if string
if isinstance(data, str):
try:
data = json.loads(data)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON: {e}")
# Auto-detect format if needed
if source == "auto":
source = detect_data_format(data)
metadata['detected_format'] = source
if source == "unknown":
raise ValueError("Could not auto-detect data format. Please specify source parameter.")
# Handle dict with 'prices' key (CoinGecko wrapper)
if isinstance(data, dict) and "prices" in data:
data = data["prices"]
source = "coingecko"
# Convert based on source
if source == "coingecko":
df = coingecko_to_ohlcv(data, enhanced=enhanced_approximation)
elif source in ("exchange_ohlcv", "exchange", "ohlcv_dict"):
df = exchange_ohlcv_to_df(data)
elif source == "yahoo":
df = yahoo_to_ohlcv(data)
elif source == "price_only":
df = price_only_to_ohlcv(data, enhanced=enhanced_approximation)
else:
raise ValueError(f"Unsupported source: {source}")
metadata['original_rows'] = len(df)
# Normalize column names
df = _normalize_columns(df)
# Validate and repair
if validate:
df, warnings = validate_and_repair(df)
metadata['warnings'] = warnings
metadata['final_rows'] = len(df)
metadata['quality'] = validate_data_quality(df)
return df, metadata
# =============================================================================
# Backward Compatibility (keep old function names working)
# =============================================================================
def prepare_analysis_data(coingecko_json: str) -> pd.DataFrame:
"""
Prepare CoinGecko JSON data for technical analysis.
(Backward compatible with old coingecko_converter.py)
Args:
coingecko_json: JSON string from CoinGecko chart endpoint
Returns:
DataFrame ready for TechnicalAnalyzer
"""
df, _ = normalize_ohlcv(coingecko_json, source="coingecko")
return df
# Keep the old name as alias
coingecko_to_ohlcv_legacy = coingecko_to_ohlcv
if __name__ == "__main__":
print("Generic Data Converter for Technical Analysis")
print("=" * 50)
print("\nSupported formats:")
print(" - CoinGecko API charts")
print(" - Exchange OHLCV (Binance, etc.)")
print(" - Yahoo Finance")
print(" - Price-only data")
print("\nUsage:")
print(" from data_converter import normalize_ohlcv")
print(" df, metadata = normalize_ohlcv(data, source='auto')")
numpy>=1.21.0
pandas>=1.3.0
Related skills
How it compares
Use Crypto-ta-analyzer for agent-driven crypto TA and signals; choose portfolio accounting or tax tooling for compliance and ledger workflows.
FAQ
What data format is required?
Normalized OHLCV; use data_converter.py or coingecko_converter.py to reshape sources first.
Does this skill guarantee trade signals?
No. It outputs deterministic indicators and requires clear separation from discretionary interpretation.
Which script runs the indicator stack?
scripts/ta_analyzer.py after OHLCV normalization.
Is Crypto Ta Analyzer safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.