Point & Figure Chart
Chart overview
Point and Figure charts use X's and O's to represent price movements, ignoring time entirely.
Key points
- X columns represent rising prices, O columns represent falling prices.
- This technique filters out noise and highlights significant price movements, support, and resistance levels.
Python Tutorial
How to create a point & figure chart in Python
Use the full tutorial for implementation details, troubleshooting, and chart variations in matplotlib, seaborn, and plotly.
How to Create a Bar Chart in PythonExample Visualization

Create This Chart Now
Generate publication-ready point & figure charts with AI in seconds. No coding required – just describe your data and let AI do the work.
View example prompt
"Use mplfinance library to create a Point and Figure chart for asset prices using a box size of 1 and a reversal amount of 3. Generate a proper example dataset with realistic price movements."
How to create this chart in 30 seconds
Upload Data
Drag & drop your Excel or CSV file. Plotivy securely processes it in your browser.
AI Generation
Our AI analyzes your data and generates the Point & Figure Chart code automatically.
Customize & Export
Tweak the design with natural language, then export as high-res PNG, SVG or PDF.
Newsletter
Get one weekly tip for better point & figure charts
Join researchers receiving concise Python plotting techniques to improve chart clarity and reduce revision cycles.
Python Code Example
# === IMPORTS ===
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
# === USER-EDITABLE PARAMETERS ===
title = "Point & Figure Chart — Price Pattern Recognition"
figsize = (14, 10)
# === EXAMPLE DATASET ===
np.random.seed(42)
# Generate price data with clear trends
n_days = 120
start_price = 50
prices = [start_price]
for i in range(1, n_days):
if i < 30:
trend = 0.25 # Strong uptrend
elif i < 55:
trend = -0.18 # Downtrend
elif i < 85:
trend = 0.2 # Uptrend
else:
trend = -0.12 # Mild downtrend
change = trend + np.random.randn() * 0.4
new_price = max(prices[-1] + change, 40)
prices.append(new_price)
prices = np.array(prices)
# P&F chart parameters
box_size = 1.0
reversal = 3
# Build P&F chart
columns = []
current_column = {'type': 'X', 'boxes': []}
current_high = prices[0]
current_low = prices[0]
box_start = int(prices[0] / box_size) * box_size
# Initialize first column
current_column['bottom'] = box_start
current_column['top'] = box_start
current_column['boxes'].append(box_start)
for price in prices[1:]:
box_price = int(price / box_size) * box_size
if current_column['type'] == 'X': # Rising column
if price >= current_column['top'] + box_size:
# Continue rising
new_boxes = int((price - current_column['top']) / box_size)
for i in range(1, new_boxes + 1):
current_column['boxes'].append(current_column['top'] + i * box_size)
current_column['top'] = current_column['boxes'][-1]
elif price <= current_column['top'] - reversal * box_size:
# Reversal to O
columns.append(current_column)
new_bottom = current_column['top'] - box_size
current_column = {'type': 'O', 'boxes': [new_bottom],
'top': new_bottom, 'bottom': new_bottom}
while current_column['bottom'] > box_price:
current_column['bottom'] -= box_size
current_column['boxes'].append(current_column['bottom'])
else: # Falling column
if price <= current_column['bottom'] - box_size:
# Continue falling
new_boxes = int((current_column['bottom'] - price) / box_size)
for i in range(1, new_boxes + 1):
current_column['boxes'].append(current_column['bottom'] - i * box_size)
current_column['bottom'] = current_column['boxes'][-1]
elif price >= current_column['bottom'] + reversal * box_size:
# Reversal to X
columns.append(current_column)
new_top = current_column['bottom'] + box_size
current_column = {'type': 'X', 'boxes': [new_top],
'top': new_top, 'bottom': new_top}
while current_column['top'] < box_price:
current_column['top'] += box_size
current_column['boxes'].append(current_column['top'])
columns.append(current_column)
# Print summary
print("=== Point & Figure Chart ===")
print(f"\nOriginal data points: {len(prices)}")
print(f"P&F columns: {len(columns)}")
print(f"Box size: ${box_size:.2f}")
print(f"Reversal: {reversal} boxes")
x_columns = sum(1 for c in columns if c['type'] == 'X')
o_columns = sum(1 for c in columns if c['type'] == 'O')
print(f"X columns (bullish): {x_columns}")
print(f"O columns (bearish): {o_columns}")
# === CREATE P&F CHART ===
fig, ax = plt.subplots(figsize=figsize, facecolor='#0f0f23')
ax.set_facecolor('#0f0f23')
# Colors
x_color = '#00E676' # Bright green
o_color = '#FF5252' # Bright red
# Draw P&F chart
for col_idx, column in enumerate(columns):
symbol_type = column['type']
for box_price in column['boxes']:
if symbol_type == 'X':
# Draw X
ax.plot([col_idx - 0.35, col_idx + 0.35],
[box_price - 0.35, box_price + 0.35],
color=x_color, linewidth=2.5, solid_capstyle='round')
ax.plot([col_idx - 0.35, col_idx + 0.35],
[box_price + 0.35, box_price - 0.35],
color=x_color, linewidth=2.5, solid_capstyle='round')
else:
# Draw O
circle = plt.Circle((col_idx, box_price), 0.35,
fill=False, color=o_color, linewidth=2.5)
ax.add_patch(circle)
# Add trend lines
# Find highest X column for resistance line
highest_tops = [(i, c['top']) for i, c in enumerate(columns) if c['type'] == 'X']
if len(highest_tops) > 2:
highest_tops.sort(key=lambda x: x[1], reverse=True)
top1, top2 = highest_tops[0], highest_tops[1]
ax.axhline(y=top1[1], color='#FFD700', linestyle='--', linewidth=1.5,
alpha=0.7, label='Resistance')
# Find lowest O column for support line
lowest_bottoms = [(i, c['bottom']) for i, c in enumerate(columns) if c['type'] == 'O']
if len(lowest_bottoms) > 2:
lowest_bottoms.sort(key=lambda x: x[1])
bot1 = lowest_bottoms[0]
ax.axhline(y=bot1[1], color='#00BCD4', linestyle='--', linewidth=1.5,
alpha=0.7, label='Support')
# Styling
ax.set_title(title, fontsize=20, fontweight='bold', color='white', pad=20)
ax.set_xlabel('Column Number', fontsize=12, color='#888', fontweight='bold')
ax.set_ylabel('Price ($)', fontsize=12, color='#888', fontweight='bold')
ax.tick_params(colors='#888')
ax.spines['bottom'].set_color('#333')
ax.spines['left'].set_color('#333')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Grid
all_boxes = []
for col in columns:
all_boxes.extend(col['boxes'])
min_box = min(all_boxes) - box_size
max_box = max(all_boxes) + box_size
ax.set_yticks(np.arange(min_box, max_box + box_size, box_size))
ax.grid(True, alpha=0.1, color='white')
ax.set_axisbelow(True)
ax.set_xlim(-0.5, len(columns) - 0.5)
ax.set_ylim(min_box - 0.5, max_box + 0.5)
ax.set_aspect('equal')
# Legend
legend_elements = [
plt.Line2D([0], [0], marker='x', color=x_color, markersize=12,
label='X (Rising)', linestyle='None', markeredgewidth=3),
plt.Circle((0, 0), 0.1, fill=False, color=o_color, label='O (Falling)'),
]
ax.legend(handles=legend_elements[:1], loc='upper left', facecolor='#1a1a3a',
edgecolor='#444', labelcolor='white', fontsize=11)
# Info box
info_text = f'Box: ${box_size:.2f} | Rev: {reversal} boxes | {len(columns)} columns'
ax.text(0.98, 0.02, info_text, transform=ax.transAxes, fontsize=10,
color='#888', ha='right', va='bottom',
bbox=dict(boxstyle='round', facecolor='#1a1a3a', alpha=0.8, edgecolor='#444'))
plt.tight_layout()
plt.savefig('chart.png', dpi=150, bbox_inches='tight', facecolor='#0f0f23')
print("Saved: chart.png")
plt.show()
# END-OF-CODE
Opens the Analyze page with this code pre-loaded and ready to execute
Console Output
=== Point & Figure Chart === Original data points: 120 P&F columns: 4 Box size: $1.00 Reversal: 3 boxes X columns (bullish): 2 O columns (bearish): 2 Saved: chart.png
Common Use Cases
- 1Long-term trend analysis
- 2Support/resistance identification
- 3Price target calculation
- 4Pattern recognition trading
Pro Tips
Choose box size based on price volatility
Use 3-box reversal for cleaner signals
Look for double-top and double-bottom patterns
Long-tail keyword opportunities
High-intent chart variations
Library comparison for this chart
mplfinance
Useful in specialized workflows that complement core Python plotting libraries for point-and-figure-chart analysis tasks.
Scientific Chart Selection Cheat Sheet
Not sure whether to use a Violin Plot, Box Plot, or Ridge Plot? Download our single-page reference mapping the most-used scientific chart types, exactly when to use them, and the core Matplotlib/Seaborn functions.