Price Trend Analysis with Volatility Indicators using CoinAPI
Introduction
This tutorial demonstrates how to perform advanced price trend analysis with volatility indicators using the CoinAPI Market Data REST API. Price trend analysis is essential for understanding market movements and making informed trading decisions. Volatility indicators help traders identify periods of high or low market activity, potential trend reversals, and optimal entry/exit points.
What You Will Learn
- How to fetch historical exchange rate data from CoinAPI
- How to calculate various volatility indicators (ATR, normalized volatility)
- How to detect and analyze price trends
- How to create comprehensive visualizations
- How to perform statistical analysis of market patterns
Prerequisites
- Python 3.7+
- CoinAPI API key (get one at https://www.coinapi.io/)
- Required packages: requests, pandas, numpy, matplotlib, seaborn
API Setup
- Go to https://www.coinapi.io/
- Sign up for a free account
- Get your API key from the dashboard
- Replace
YOUR_COINAPI_KEY_HEREin the code below with your actual API key
1. Environment Setup
First, let's set up our environment with the necessary imports and API configuration.
1# Import required libraries
2import requests
3import pandas as pd
4import numpy as np
5import matplotlib.pyplot as plt
6import seaborn as sns
7from datetime import datetime, timedelta, timezone
8import json
9from typing import Optional, List
10
11# Set up plotting style
12plt.style.use('seaborn-v0_8')
13sns.set_palette("husl")
14plt.rcParams['figure.figsize'] = (12, 8)
15
16# CoinAPI configuration
17API_KEY = "YOUR_COINAPI_KEY_HERE" # Replace with your actual API key
18BASE_URL = "https://rest.coinapi.io"
19
20print("ā
Environment setup complete!")
21print(f"š Using CoinAPI base URL: {BASE_URL}")
22print(f"š API Key configured: {'Yes' if API_KEY != 'YOUR_COINAPI_KEY_HERE' else 'No (Please update)'}")ā
Environment setup complete!
š Using CoinAPI base URL: https://rest.coinapi.io
š API Key configured: Yes
2. Data Fetching Function
We'll create a function to fetch historical exchange rate data from CoinAPI. This function will retrieve OHLC (Open, High, Low, Close) data which is perfect for calculating volatility indicators and analyzing price trends.
1def fetch_exchange_rate_data(asset_id_base: str, asset_id_quote: str, period_id: str, time_start: str, time_end: str) -> Optional[List[dict]]:
2 """
3 Fetch historical exchange rate data from CoinAPI
4
5 Args:
6 asset_id_base: Base asset identifier (e.g., 'BTC')
7 asset_id_quote: Quote asset identifier (e.g., 'USDT')
8 period_id: Time period (e.g., '1HRS' for 1 hour)
9 time_start: Start time in ISO format
10 time_end: End time in ISO format
11
12 Returns:
13 List of exchange rate data points with OHLC data
14 """
15 url = f"{BASE_URL}/v1/exchangerate/{asset_id_base}/{asset_id_quote}/history"
16
17 params = {
18 'period_id': period_id,
19 'time_start': time_start,
20 'time_end': time_end,
21 'limit': 10000
22 }
23
24 headers = {
25 'X-CoinAPI-Key': API_KEY
26 }
27
28 try:
29 print(f"š Fetching data from {url}")
30 print(f"š
Time range: {time_start} to {time_end}")
31
32 response = requests.get(url, params=params, headers=headers)
33 response.raise_for_status()
34
35 data = response.json()
36 print(f"ā
Successfully fetched {len(data)} data points")
37 return data
38
39 except requests.exceptions.RequestException as e:
40 print(f"ā Error fetching data: {e}")
41 if hasattr(e, 'response') and e.response is not None:
42 print(f"Response status: {e.response.status_code}")
43 print(f"Response text: {e.response.text}")
44 return None
45
46print("š Data fetching function created successfully!")š Data fetching function created successfully!
3. Fetch Historical Data
Now let's fetch historical exchange rate data for BTC/USDT. We'll request data at a 1-hour interval for the past 7 days.
1# Check if API key is configured
2if API_KEY == "YOUR_COINAPI_KEY_HERE":
3 print("ā ERROR: Please set your CoinAPI key in the previous cell!")
4 print("š Get your API key at: https://www.coinapi.io/")
5 print("š Replace 'YOUR_COINAPI_KEY_HERE' with your actual API key")
6else:
7 # Set up time range (last 7 days)
8 end_time = datetime.now(timezone.utc)
9 start_time = end_time - timedelta(days=7)
10
11 # Format times for API
12 time_start = start_time.strftime('%Y-%m-%dT%H:%M:%S')
13 time_end = end_time.strftime('%Y-%m-%dT%H:%M:%S')
14
15 # Fetch BTC/USDT data
16 asset_id_base = "BTC"
17 asset_id_quote = "USDT"
18 period_id = "1HRS"
19
20 print(f"šÆ Fetching {asset_id_base}/{asset_id_quote} data")
21 print(f"ā±ļø Period: {period_id}")
22
23 exchange_rate_data = fetch_exchange_rate_data(asset_id_base, asset_id_quote, period_id, time_start, time_end)
24
25 if exchange_rate_data:
26 print("\nš Sample data structure:")
27 print(json.dumps(exchange_rate_data[0], indent=2))
28 else:
29 print("\nā Failed to fetch data. Please check your API key and try again.")šÆ Fetching BTC/USDT data
ā±ļø Period: 1HRS
š Fetching data from https://rest.coinapi.io/v1/exchangerate/BTC/USDT/history
š
Time range: 2025-07-08T15:07:12 to 2025-07-15T15:07:12
ā
Successfully fetched 168 data points
š Sample data structure:
{
"time_period_start": "2025-07-08T16:00:00.0000000Z",
"time_period_end": "2025-07-08T17:00:00.0000000Z",
"time_open": "2025-07-08T16:00:00.1000000Z",
"time_close": "2025-07-08T16:59:59.9000000Z",
"rate_open": 108273.16295175275,
"rate_high": 108559.27205797151,
"rate_low": 108089.54247934539,
"rate_close": 108446.6117630905
}
4. Data Processing and Cleaning
Now let's convert the raw API data into a pandas DataFrame for easier analysis and perform some data cleaning.
1if 'exchange_rate_data' in locals() and exchange_rate_data:
2 # Convert to DataFrame
3 df = pd.DataFrame(exchange_rate_data)
4
5 print(f"š DataFrame created with shape: {df.shape}")
6 print(f"š Columns: {list(df.columns)}")
7
8 # Convert time columns to datetime
9 df['time_period_start'] = pd.to_datetime(df['time_period_start'])
10 df['time_period_end'] = pd.to_datetime(df['time_period_end'])
11
12 # Rename columns for clarity
13 df = df.rename(columns={
14 'rate_open': 'open',
15 'rate_high': 'high',
16 'rate_low': 'low',
17 'rate_close': 'close'
18 })
19
20 # Sort by time
21 df = df.sort_values('time_period_start')
22
23 print("\nš First few rows:")
24 display(df.head())
25
26 print("\nš Data types:")
27 print(df.dtypes)
28
29 print("\nš Summary statistics:")
30 display(df[['open', 'high', 'low', 'close']].describe())
31
32 # Check for missing values
33 print("\nš Missing values check:")
34 missing_values = df.isnull().sum()
35 if missing_values.sum() > 0:
36 print(missing_values[missing_values > 0])
37 else:
38 print("ā
No missing values found!")
39
40else:
41 print("ā No data available for processing.")š DataFrame created with shape: (168, 8)
š Columns: ['time_period_start', 'time_period_end', 'time_open', 'time_close', 'rate_open', 'rate_high', 'rate_low', 'rate_close']
š First few rows:
| time_period_start | time_period_end | time_open | time_close | open | high | low | close | |
| 0 | 2025-07-08 16:00:00+00:00 | 2025-07-08 17:00:00+00:00 | 2025-07-08T16:00:00.1000000Z | 2025-07-08T16:59:59.9000000Z | 108273.162952 | 108559.272058 | 108089.542479 | 108446.611763 |
| 1 | 2025-07-08 17:00:00+00:00 | 2025-07-08 18:00:00+00:00 | 2025-07-08T17:00:00.0000000Z | 2025-07-08T17:59:59.9000000Z | 108446.560951 | 109118.903087 | 108433.060883 | 108992.512505 |
| 2 | 2025-07-08 18:00:00+00:00 | 2025-07-08 19:00:00+00:00 | 2025-07-08T18:00:00.0000000Z | 2025-07-08T18:59:59.8000000Z | 108992.511813 | 109172.372158 | 108914.395031 | 109160.054626 |
| 3 | 2025-07-08 19:00:00+00:00 | 2025-07-08 20:00:00+00:00 | 2025-07-08T19:00:00.0000000Z 2025-07- | 2025-07-08T19:59:59.9000000Z | 109160.054626 | 109166.228089 | 108747.781101 | 108778.801776 |
| 4 | 2025-07-08 20:00:00+00:00 | 2025-07-08 21:00:00+00:00 | 2025-07-08T20:00:00.0000000Z | 2025-07-08T20:59:59.9000000Z | 108779.333043 | 108949.665184 | 108640.644820 | 108680.949952 |
š Data types:
time_period_start datetime64[ns, UTC]
time_period_end datetime64[ns, UTC]
time_open object
time_close object
open float64
high float64
low float64
close float64
dtype: object
š Summary statistics:
| open | high | low | close | |
| count | 168.000000 | 168.000000 | 168.000000 | 168.000000 |
| mean | 115701.871611 | 116019.444476 | 115414.747858 | 115748.644910 |
| std | 4110.960931 | 4130.685909 | 4030.008808 | 4069.899305 |
| min | 108273.162952 | 108559.272058 | 108089.542479 | 108375.914978 |
| 25% | 111239.876258 | 111430.712077 | 111012.066202 | 111242.310857 |
| 50% | 117485.272231 | 117663.490559 | 117195.364793 | 117485.272231 |
| 75% | 118091.978431 | 118453.823024 | 117851.778283 | 118091.183057 |
| max | 122737.262279 | 123225.589450 | 122281.080900 | 122731.331256 |
š Missing values check:
ā
No missing values found!
5. Volatility Indicators Calculation
Volatility indicators help us understand the magnitude of price movements and market activity. We'll calculate several volatility-based indicators from the data:
- Price Range: High - Low (absolute volatility)
- Normalized Volatility: (High - Low) / Close (relative volatility)
- Average True Range (ATR): A more sophisticated volatility measure
- Volatility Ratio: Current volatility vs historical average
- Moving Averages: Simple moving averages for trend analysis
1if 'df' in locals() and not df.empty:
2 print("š¢ Calculating volatility indicators...")
3
4 # Basic volatility indicators
5 df['volatility'] = df['high'] - df['low'] # Price range
6 df['normalized_volatility'] = (df['high'] - df['low']) / df['close'] # Relative volatility
7
8 # Calculate True Range (TR) for ATR
9 df['tr1'] = df['high'] - df['low']
10 df['tr2'] = abs(df['high'] - df['close'].shift(1))
11 df['tr3'] = abs(df['low'] - df['close'].shift(1))
12 df['true_range'] = df[['tr1', 'tr2', 'tr3']].max(axis=1)
13
14 # Calculate ATR (14-period average)
15 df['atr'] = df['true_range'].rolling(window=14).mean()
16
17 # Calculate volatility ratio (current vs historical average)
18 df['volatility_ratio'] = df['normalized_volatility'] / df['normalized_volatility'].rolling(window=20).mean()
19
20 # Calculate simple moving averages
21 df['sma_20'] = df['close'].rolling(window=20).mean()
22 df['sma_50'] = df['close'].rolling(window=50).mean()
23
24 print("ā
Volatility indicators calculation complete!")
25
26 print("\nš Sample data with volatility indicators:")
27 display(df[['time_period_start', 'close', 'volatility', 'normalized_volatility', 'atr', 'volatility_ratio']].head(10))
28
29 # Show volatility statistics
30 print("\nš Volatility Statistics:")
31 print(f"Average Volatility: ${df['volatility'].mean():.2f}")
32 print(f"Average Normalized Volatility: {df['normalized_volatility'].mean():.4f}")
33 print(f"Average ATR: ${df['atr'].mean():.2f}")
34 print(f"Average Volatility Ratio: {df['volatility_ratio'].mean():.2f}")
35
36 # Show moving average statistics
37 print("\nš Moving Average Statistics:")
38 print(f"20-period SMA: ${df['sma_20'].iloc[-1]:.2f}")
39 print(f"50-period SMA: ${df['sma_50'].iloc[-1]:.2f}")
40
41else:
42 print("ā No data available for volatility calculation.")š¢ Calculating volatility indicators...
ā
Volatility indicators calculation complete!
š Sample data with volatility indicators:
| time_period_start | close | volatility | normalized_volatility | atr | volatility_ratio | |
| 0 | 2025-07-08 16:00:00+00:00 | 108446.611763 | 469.729579 | 0.004331 | NaN | NaN |
| 1 | 2025-07-08 17:00:00+00:00 | 108992.512505 | 685.842204 | 0.006293 | NaN | NaN |
| 2 | 2025-07-08 18:00:00+00:00 | 109160.054626 | 257.977127 | 0.002363 | NaN | NaN |
| 3 | 2025-07-08 19:00:00+00:00 | 108778.801776 | 418.446987 | 0.003847 | NaN | NaN |
| 4 | 2025-07-08 20:00:00+00:00 | 108680.949952 | 309.020365 | 0.002843 | NaN | NaN |
| 5 | 2025-07-08 21:00:00+00:00 | 108899.559914 | 357.665972 | 0.003284 | NaN | NaN |
| 6 | 2025-07-08 22:00:00+00:00 | 108921.280819 | 167.493417 | 0.001538 | NaN | NaN |
| 7 | 2025-07-08 23:00:00+00:00 | 108928.659916 | 153.250958 | 0.001407 | NaN | NaN |
| 8 | 2025-07-09 00:00:00+00:00 | 108936.011746 | 184.163191 | 0.001691 | NaN | NaN |
| 9 | 2025-07-09 01:00:00+00:00 | 108782.495298 | 409.500144 | 0.003764 | NaN | NaN |
š Volatility Statistics:
Average Volatility: $604.70
Average Normalized Volatility: 0.0052
Average ATR: $596.55
Average Volatility Ratio: 1.11
š Moving Average Statistics:
20-period SMA: $117862.17
50-period SMA: $119296.47
ā ļø Minimum Data Requirements for Rolling Indicators
- ATR (14-period): Requires at least 14 data points (e.g., 14 hours for 1HRS interval) before the first valid value appears. The first 13 rows will be NaN.
- Volatility Ratio (20-period): Requires at least 20 data points (e.g., 20 hours for 1HRS interval) before the first valid value appears. The first 19 rows will be NaN.
- SMA 20: Requires 20 data points before the first valid value.
- SMA 50: Requires 50 data points before the first valid value.
If you want all indicators to have valid values for the entire sample, expand your time range so you have at least 50 data points (e.g., 50 hours for 1HRS interval, or ~2.1 days).
6. Price Trend Analysis
We'll analyze price trends by comparing each value to the previous one and identify volatility patterns. This helps us understand market conditions and potential trading opportunities.
1if 'df' in locals() and 'normalized_volatility' in df.columns:
2 print("š Analyzing price trends and volatility patterns...")
3
4 # Calculate price trend direction
5 df['price_change'] = df['close'].diff()
6 df['price_trend'] = np.where(df['price_change'] > 0, 'Upward', 'Downward')
7
8 # Handle the first row (no previous value)
9 df.loc[df.index[0], 'price_trend'] = 'No Change'
10
11 # Identify volatility regimes
12 volatility_threshold = df['normalized_volatility'].quantile(0.75) # 75th percentile
13 df['volatility_regime'] = np.where(df['normalized_volatility'] > volatility_threshold, 'High', 'Low')
14
15 # Calculate trend statistics
16 trend_counts = df['price_trend'].value_counts()
17 volatility_counts = df['volatility_regime'].value_counts()
18
19 print("ā
Trend analysis complete!")
20
21 print(f"\nš Total periods analyzed: {len(df)}")
22
23 print("\nš Price trend distribution:")
24 for trend, count in trend_counts.items():
25 percentage = (count / len(df)) * 100
26 print(f" {trend}: {count} periods ({percentage:.1f}%)")
27
28 print("\nš Volatility regime distribution:")
29 for regime, count in volatility_counts.items():
30 percentage = (count / len(df)) * 100
31 print(f" {regime} Volatility: {count} periods ({percentage:.1f}%)")
32
33 print("\nš Sample data with trend and volatility analysis:")
34 display(df[['time_period_start', 'close', 'price_change', 'price_trend', 'normalized_volatility', 'volatility_regime']].head(10))
35
36 # Calculate average price change by trend
37 print("\nš Average price change by trend:")
38 trend_stats = df.groupby('price_trend')['price_change'].agg(['mean', 'std', 'count'])
39 display(trend_stats)
40
41else:
42 print("ā No data available for trend analysis.")š Analyzing price trends and volatility patterns...
ā
Trend analysis complete!
š Total periods analyzed: 168
š Price trend distribution:
Upward: 89 periods (53.0%)
Downward: 78 periods (46.4%)
No Change: 1 periods (0.6%)
š Volatility regime distribution:
Low Volatility: 126 periods (75.0%)
High Volatility: 42 periods (25.0%)
š Sample data with trend and volatility analysis:
| time_period_start | close | price_change | price_trend | normalized_volatility | volatility_regime | |
| 0 | 2025-07-08 16:00:00+00:00 | 108446.611763 | NaN | No Change | 0.004331 | Low |
| 1 | 2025-07-08 17:00:00+00:00 | 108992.512505 | 545.900742 | Upward | 0.006293 | Low |
| 2 | 2025-07-08 18:00:00+00:00 | 109160.054626 | 167.542121 | Upward | 0.002363 | Low |
| 3 | 2025-07-08 19:00:00+00:00 | 108778.801776 | -381.252850 | Downward | 0.003847 | Low |
| 4 | 2025-07-08 20:00:00+00:00 | 108680.949952 | -97.851824 | Downward | 0.002843 | Low |
| 5 | 2025-07-08 21:00:00+00:00 | 108899.559914 | 218.609962 | Upward | 0.003284 | Low |
| 6 | 2025-07-08 22:00:00+00:00 | 108921.280819 | 21.720905 | Upward | 0.001538 | Low |
| 7 | 2025-07-08 23:00:00+00:00 | 108928.659916 | 7.379097 | Upward | 0.001407 | Low |
| 8 | 2025-07-09 00:00:00+00:00 | 108936.011746 | 7.351830 | Upward | 0.001691 | Low |
| 9 | 2025-07-09 01:00:00+00:00 | 108782.495298 | -153.516448 | Downward | 0.003764 | Low |
š Average price change by trend:
| mean | std | count | |
| price_trend | |||
| Downward | -276.674972 | 330.352230 | 78 |
| No Change | NaN | NaN | 0 |
| Upward | 329.231522 | 440.775012 | 89 |
7. Data Visualization
Let's create comprehensive visualizations to show price trends, volatility patterns, and their relationships. We'll create multiple charts to provide different perspectives on the data.
1if 'df' in locals() and not df.empty:
2 print("šØ Creating comprehensive visualizations...")
3
4 # Create a comprehensive visualization with 4 subplots
5 fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
6 fig.suptitle('Price Trend Analysis with Volatility Indicators', fontsize=16, fontweight='bold')
7
8 # Plot 1: Price with Moving Averages
9 ax1.plot(df['time_period_start'], df['close'], label='Close Price', linewidth=2, color='blue')
10 ax1.plot(df['time_period_start'], df['sma_20'], label='20-period SMA', linewidth=2, alpha=0.7, color='red')
11 ax1.plot(df['time_period_start'], df['sma_50'], label='50-period SMA', linewidth=2, alpha=0.7, color='green')
12 ax1.set_title('Price Trend with Moving Averages', fontweight='bold')
13 ax1.set_xlabel('Time')
14 ax1.set_ylabel('Price (USDT)')
15 ax1.legend()
16 ax1.grid(True, alpha=0.3)
17
18 # Plot 2: Volatility
19 ax2.bar(df['time_period_start'], df['volatility'], alpha=0.7, color='lightblue')
20 ax2.set_title('Price Volatility Over Time', fontweight='bold')
21 ax2.set_xlabel('Time')
22 ax2.set_ylabel('Volatility (USDT)')
23 ax2.grid(True, alpha=0.3)
24
25 # Plot 3: Normalized Volatility
26 ax3.plot(df['time_period_start'], df['normalized_volatility'], color='orange', linewidth=2)
27 ax3.axhline(y=df['normalized_volatility'].mean(), color='red', linestyle='--', alpha=0.7, label='Average')
28 ax3.set_title('Normalized Volatility Over Time', fontweight='bold')
29 ax3.set_xlabel('Time')
30 ax3.set_ylabel('Normalized Volatility')
31 ax3.legend()
32 ax3.grid(True, alpha=0.3)
33
34 # Plot 4: ATR
35 ax4.plot(df['time_period_start'], df['atr'], color='green', linewidth=2)
36 ax4.set_title('Average True Range (ATR)', fontweight='bold')
37 ax4.set_xlabel('Time')
38 ax4.set_ylabel('ATR (USDT)')
39 ax4.grid(True, alpha=0.3)
40
41 plt.tight_layout()
42 plt.show()
43
44 print("ā
Main visualization complete!")
45
46else:
47 print("ā No data available for visualization.")šØ Creating comprehensive visualizations...
ā
Main visualization complete!
1if 'df' in locals() and not df.empty:
2 print("šØ Creating trend and volatility correlation plot...")
3
4 # Create trend and volatility correlation plot
5 plt.figure(figsize=(12, 8))
6
7 # Color code by trend
8 colors = {'Upward': 'green', 'Downward': 'red', 'No Change': 'gray'}
9
10 for trend in df['price_trend'].unique():
11 mask = df['price_trend'] == trend
12 plt.scatter(df[mask]['normalized_volatility'], df[mask]['price_change'],
13 c=colors[trend], label=trend, alpha=0.6, s=50)
14
15 plt.xlabel('Normalized Volatility')
16 plt.ylabel('Price Change (USDT)')
17 plt.title('Price Change vs Volatility by Trend', fontweight='bold')
18 plt.legend()
19 plt.grid(True, alpha=0.3)
20 plt.show()
21
22 print("ā
Correlation plot complete!")
23
24else:
25 print("ā No data available for correlation plot.")šØ Creating trend and volatility correlation plot...
ā
Correlation plot complete!
8. Statistical Analysis and Summary
Let's perform a comprehensive statistical analysis of our findings and provide insights about the price trend and volatility analysis.
1if 'df' in locals() and not df.empty:
2 print("š Performing comprehensive statistical analysis...")
3
4 print("=" * 60)
5 print("PRICE TREND AND VOLATILITY ANALYSIS SUMMARY")
6 print("=" * 60)
7
8 print(f"š
Analysis Period: {df['time_period_start'].min()} to {df['time_period_start'].max()}")
9 print(f"š Total Data Points: {len(df)}")
10
11 print("\n" + "=" * 40)
12 print("PRICE STATISTICS")
13 print("=" * 40)
14 print(f"š° Starting Price: ${df['close'].iloc[0]:.2f}")
15 print(f"š° Ending Price: ${df['close'].iloc[-1]:.2f}")
16 print(f"š Total Price Change: ${df['close'].iloc[-1] - df['close'].iloc[0]:.2f}")
17 print(f"š Percentage Change: {((df['close'].iloc[-1] / df['close'].iloc[0]) - 1) * 100:.2f}%")
18
19 print("\n" + "=" * 40)
20 print("VOLATILITY STATISTICS")
21 print("=" * 40)
22 print(f"š Average Volatility: {df['volatility'].mean():.4f}")
23 print(f"š Volatility Range: {df['volatility'].min():.4f} - {df['volatility'].max():.4f}")
24 print(f"š Average Normalized Volatility: {df['normalized_volatility'].mean():.4f}")
25 print(f"š Average ATR: {df['atr'].mean():.4f}")
26
27 print("\n" + "=" * 40)
28 print("TREND ANALYSIS")
29 print("=" * 40)
30 trend_summary = df['price_trend'].value_counts()
31 for trend, count in trend_summary.items():
32 percentage = (count / len(df)) * 100
33 print(f"š {trend} Trend: {count} periods ({percentage:.1f}%)")
34
35 print("\n" + "=" * 40)
36 print("VOLATILITY REGIME ANALYSIS")
37 print("=" * 40)
38 volatility_summary = df['volatility_regime'].value_counts()
39 for regime, count in volatility_summary.items():
40 percentage = (count / len(df)) * 100
41 print(f"š {regime} Volatility: {count} periods ({percentage:.1f}%)")
42
43 # Calculate correlation between price change and volatility
44 correlation = df['price_change'].corr(df['normalized_volatility'])
45 print("\n" + "=" * 40)
46 print("CORRELATION ANALYSIS")
47 print("=" * 40)
48 print(f"š Price Change vs Volatility Correlation: {correlation:.4f}")
49
50 if abs(correlation) > 0.3:
51 print("š Strong correlation detected between price changes and volatility.")
52 elif abs(correlation) > 0.1:
53 print("š Moderate correlation detected between price changes and volatility.")
54 else:
55 print("š Weak correlation between price changes and volatility.")
56
57 print("\n" + "=" * 60)
58 print("ANALYSIS COMPLETE!")
59 print("=" * 60)
60
61else:
62 print("ā No data available for summary analysis.")š Performing comprehensive statistical analysis...
============================================================
PRICE TREND AND VOLATILITY ANALYSIS SUMMARY
============================================================
š
Analysis Period: 2025-07-08 16:00:00+00:00 to 2025-07-15 15:00:00+00:00
š Total Data Points: 168
========================================
PRICE STATISTICS
========================================
š° Starting Price: $108446.61
š° Ending Price: $116167.57
š Total Price Change: $7720.96
š Percentage Change: 7.12%
========================================
VOLATILITY STATISTICS
========================================
š Average Volatility: 604.6966
š Volatility Range: 133.7623 - 3402.2335
š Average Normalized Volatility: 0.0052
š Average ATR: 596.5474
========================================
TREND ANALYSIS
========================================
š Upward Trend: 89 periods (53.0%)
š Downward Trend: 78 periods (46.4%)
š No Change Trend: 1 periods (0.6%)
========================================
VOLATILITY REGIME ANALYSIS
========================================
š Low Volatility: 126 periods (75.0%)
š High Volatility: 42 periods (25.0%)
========================================
CORRELATION ANALYSIS
========================================
š Price Change vs Volatility Correlation: 0.2708
š Moderate correlation detected between price changes and volatility.
============================================================
ANALYSIS COMPLETE!
============================================================
9. Key Insights and Interpretation
Based on our analysis, here are the key insights we can draw:
š Price Trend Insights
- The distribution of upward vs downward trends shows market sentiment
- Moving averages help identify fair value and potential support/resistance levels
- Price changes relative to moving averages indicate buying/selling pressure
š Volatility Insights
- Normalized Volatility: Shows relative price movement regardless of absolute price
- ATR: Provides a smoothed measure of market volatility
- Volatility Regimes: Help identify different market conditions (calm vs turbulent)
- Volatility Ratio: Indicates if current volatility is above/below historical average
š Correlation Analysis
- The correlation between price changes and volatility reveals market dynamics
- Strong positive correlation might indicate momentum-driven markets
- Weak correlation might suggest range-bound or news-driven markets
š” Trading Implications
- High volatility periods often present both risk and opportunity
- Trend direction combined with volatility analysis provides better entry/exit signals
- Moving averages can serve as dynamic support/resistance levels
- ATR helps in setting appropriate stop-loss levels
10. Next Steps and Customization
š§ Customization Options
You can modify this analysis by changing:
- Trading Pair: Change
asset_id_baseandasset_id_quote(e.g., 'ETH', 'ADA') - Time Period: Modify the
timedelta(days=7)to analyze different timeframes - Interval: Change
period_idto '1MIN', '1HRS', '1DAY' for different granularity - Indicators: Add more technical indicators like RSI, MACD, Bollinger Bands
š Advanced Analysis Ideas
- Multi-timeframe Analysis: Compare different time periods
- Cross-asset Correlation: Analyze relationships between different cryptocurrencies
- Pattern Recognition: Implement candlestick pattern detection
- Backtesting: Test trading strategies based on the indicators
Conclusion
In this tutorial, we successfully:
ā Fetched historical exchange rate data using the CoinAPI Market Data REST API ā Calculated comprehensive volatility indicators including ATR and normalized volatility ā Detected and analyzed price trends with statistical rigor ā Created professional visualizations showing multiple perspectives on the data ā Performed correlation analysis to understand market dynamics ā Provided actionable insights for trading and investment decisions
šÆ Key Takeaways
- Volatility indicators help identify periods of high and low market activity
- Price trends combined with volatility analysis provide better trading insights
- ATR (Average True Range) is a robust measure of market volatility
- Moving averages serve as dynamic trend indicators
- Volatility regimes help identify different market conditions
This tutorial demonstrates how to use the CoinAPI Market Data REST API for advanced price trend and volatility analysis, providing a solid foundation for cryptocurrency market analysis and trading strategy development.
Happy analyzing! šš