Menu

Time Series
Static
12 Python scripts generated for point & figure chart this week

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 Python

Example Visualization

Point and Figure chart with X's and O's showing price patterns

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
Example AI 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

1

Upload Data

Drag & drop your Excel or CSV file. Plotivy securely processes it in your browser.

2

AI Generation

Our AI analyzes your data and generates the Point & Figure Chart code automatically.

3

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.

No spam. Unsubscribe anytime.

Python Code Example

example.py
# === 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

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

how to create point & figure chart in python
point & figure chart matplotlib
point & figure chart seaborn
point & figure chart plotly
point & figure chart scientific visualization
point & figure chart publication figure python

High-intent chart variations

Point & Figure Chart with confidence interval overlays
Point & Figure Chart optimized for publication layouts
Point & Figure Chart with category-specific color encoding
Interactive Point & Figure Chart for exploratory analysis

Library comparison for this chart

mplfinance

Useful in specialized workflows that complement core Python plotting libraries for point-and-figure-chart analysis tasks.

Free Cheat Sheet

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.

Comparison Charts
Distribution Charts
Time Series Data
Common Mistakes
No spam. Unsubscribe anytime.