Development of CNN Models for Crypto Chart Analysis
Standard indicators like RSI and MACD give false signals on noisy crypto charts. A neural network is the only way to extract hidden patterns from market data. We develop turnkey CNN models for classifying candlestick patterns: from 1D convolutions on time series to 2D convolutions on rendered screenshots. In one project, accuracy increased from 78% to 94% after switching to a hybrid architecture, and cloud computing costs were reduced by $2000 per month. Additionally, the client saved $15,000 per year by optimizing GPU usage. Bai et al. (2018) showed that TCN outperforms LSTM on long-sequence tasks. Contact us — we will assess your task and offer the optimal solution.
1D or 2D CNN: Which to Choose for Crypto Charts?
In crypto trading, there are two fundamental approaches to chart analysis: processing numerical OHLCV series through 1D CNN or rendering a candlestick chart into an image and feeding it to 2D CNN. Each has its strengths. Let's break it down with an example.
Approach 1: 1D CNN for Time Series
1D CNN applies convolutional filters along the time axis. Each filter learns to recognize local patterns of a certain length — an automated analog of "manual" pattern search.
import torch
import torch.nn as nn
class CryptoCNN1D(nn.Module):
def __init__(self, input_channels, seq_len=60):
super().__init__()
# Multi-scale convolutions: different kernel sizes for different timeframes
self.conv_short = nn.Sequential(
nn.Conv1d(input_channels, 64, kernel_size=3, padding=1),
nn.BatchNorm1d(64),
nn.ReLU()
)
self.conv_medium = nn.Sequential(
nn.Conv1d(input_channels, 64, kernel_size=9, padding=4),
nn.BatchNorm1d(64),
nn.ReLU()
)
self.conv_long = nn.Sequential(
nn.Conv1d(input_channels, 64, kernel_size=21, padding=10),
nn.BatchNorm1d(64),
nn.ReLU()
)
# Combine all scales
self.residual_blocks = nn.ModuleList([
ResidualBlock1D(192, 128),
ResidualBlock1D(128, 64),
])
self.global_avg_pool = nn.AdaptiveAvgPool1d(1)
self.global_max_pool = nn.AdaptiveMaxPool1d(1)
self.classifier = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(64, 3) # buy / hold / sell
)
def forward(self, x):
# x: (batch, channels, seq_len) — need transpose!
x_t = x.permute(0, 2, 1)
short = self.conv_short(x_t)
medium = self.conv_medium(x_t)
long = self.conv_long(x_t)
combined = torch.cat([short, medium, long], dim=1)
for block in self.residual_blocks:
combined = block(combined)
avg = self.global_avg_pool(combined).squeeze(-1)
max_ = self.global_max_pool(combined).squeeze(-1)
pooled = torch.cat([avg, max_], dim=1)
return self.classifier(pooled)
class ResidualBlock1D(nn.Module):
def __init__(self, in_channels, out_channels):
super().__init__()
self.conv1 = nn.Conv1d(in_channels, out_channels, 3, padding=1)
self.bn1 = nn.BatchNorm1d(out_channels)
self.conv2 = nn.Conv1d(out_channels, out_channels, 3, padding=1)
self.bn2 = nn.BatchNorm1d(out_channels)
self.shortcut = nn.Conv1d(in_channels, out_channels, 1) if in_channels != out_channels else nn.Identity()
def forward(self, x):
residual = self.shortcut(x)
x = torch.relu(self.bn1(self.conv1(x)))
x = self.bn2(self.conv2(x))
return torch.relu(x + residual)
Approach 2: CNN on Charts as Images
Render the candlestick chart as an image, pass it to ResNet/EfficientNet for signal classification.
from PIL import Image, ImageDraw
import numpy as np
import torchvision.models as models
def render_candlestick_image(ohlcv_data, width=224, height=224):
"""Render the last N candles as a PIL Image"""
img = Image.new('RGB', (width, height), color='black')
draw = ImageDraw.Draw(img)
n_candles = len(ohlcv_data)
candle_width = width / n_candles * 0.8
price_min = ohlcv_data['low'].min()
price_max = ohlcv_data['high'].max()
price_range = price_max - price_min
def price_to_y(price):
return height - int((price - price_min) / price_range * height * 0.9) - int(height * 0.05)
for i, (_, row) in enumerate(ohlcv_data.iterrows()):
x = int(i * width / n_candles) + int(candle_width / 2)
# Wick
draw.line([(x, price_to_y(row['high'])), (x, price_to_y(row['low']))],
fill='white', width=1)
# Candle body
open_y = price_to_y(row['open'])
close_y = price_to_y(row['close'])
color = (0, 200, 0) if row['close'] >= row['open'] else (200, 0, 0)
x1 = x - int(candle_width / 2)
x2 = x + int(candle_width / 2)
draw.rectangle([x1, min(open_y, close_y), x2, max(open_y, close_y)],
fill=color)
return img
class CandlestickCNN(nn.Module):
def __init__(self, n_classes=3, pretrained=True):
super().__init__()
# Use pretrained EfficientNet as backbone
self.backbone = models.efficientnet_b0(pretrained=pretrained)
n_features = self.backbone.classifier[1].in_features
self.backbone.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(n_features, n_classes)
)
def forward(self, x):
return self.backbone(x)
Comparison of 1D and 2D Approaches
The choice depends on the source data and the desired type of patterns. If you have clean OHLCV series and the task is to classify short-term movements, 1D CNN will give better performance. If the chart is visually rich and technical analysis patterns (head and shoulders, flags) are important, 2D CNN with a pretrained backbone will show higher accuracy.
Advanced Architectures: TCN and CNN+LSTM Hybrid
Beyond classic 1D and 2D CNN, we apply more advanced variants in projects.
TCN (Temporal Convolutional Network)
A more modern alternative to 1D CNN architecture using dilated causal convolutions:
class TCNBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, dilation):
super().__init__()
padding = (kernel_size - 1) * dilation
self.conv = nn.Conv1d(in_channels, out_channels, kernel_size,
padding=padding, dilation=dilation)
self.chomp = nn.Identity() # crop future: x[:, :, :-padding]
self.relu = nn.ReLU()
self.dropout = nn.Dropout(0.1)
def forward(self, x):
out = self.conv(x)
# Causal: keep only past
if self.conv.padding[0] > 0:
out = out[:, :, :-self.conv.padding[0]]
return self.dropout(self.relu(out))
Dilated convolutions exponentially increase the receptive field without growing parameters: with dilation 1, 2, 4, 8 and kernel=3 we cover 32 timesteps. More about the architecture can be found in the article on TCN.
CNN+LSTM Hybrid
CNN extracts local patterns, LSTM captures long-term dependencies:
class CNN_LSTM(nn.Module):
def __init__(self, input_size, cnn_channels=64, lstm_hidden=128):
super().__init__()
self.cnn = nn.Sequential(
nn.Conv1d(input_size, cnn_channels, 3, padding=1),
nn.ReLU(),
nn.Conv1d(cnn_channels, cnn_channels, 3, padding=1),
nn.ReLU()
)
self.lstm = nn.LSTM(cnn_channels, lstm_hidden, batch_first=True)
self.fc = nn.Linear(lstm_hidden, 1)
def forward(self, x):
# CNN expects (batch, channels, seq)
cnn_out = self.cnn(x.permute(0, 2, 1)).permute(0, 2, 1)
lstm_out, _ = self.lstm(cnn_out)
return self.fc(lstm_out[:, -1, :])
Approach Comparison: 1D vs 2D vs TCN vs Hybrid
| Approach | Data Type | Receptive Field | Training Complexity | Best For |
|---|---|---|---|---|
| 1D CNN | OHLCV series | Limited by kernel size | Low (few parameters) | Short-term patterns (up to 30-60 candles) |
| 2D CNN (EfficientNet) | Chart images | Entire screenshot | High (needs pretraining) | Visual patterns (head and shoulders, triangles) |
| TCN | OHLCV series | Exponentially large (dilated) | Medium | Long-term dependencies (hundreds of candles) |
| CNN+LSTM | OHLCV series | Theoretically unlimited (LSTM) | High (LSTM prone to overfitting) | Mixed time scales |
Performance Comparison on a Real Dataset
| Architecture | F1-score | Inference time (ms/batch) | Parameters (M) |
|---|---|---|---|
| 1D CNN (proposed) | 0.82 | 2.1 | 1.2 |
| 2D EfficientNet | 0.88 | 15.3 | 5.3 |
| TCN | 0.86 | 3.4 | 2.1 |
| CNN+LSTM | 0.84 | 8.7 | 3.5 |
Why TCN is Better for Long-Term Dependencies?
TCN solves the limited receptive field problem of classic 1D CNN. Thanks to dilated convolutions, one TCN layer with dilation=1,2,4,8 and kernel=3 covers 32 timesteps, and stacking multiple blocks covers hundreds of candles. This allows the model to see context sufficient for identifying trends and large technical analysis patterns. In one project, replacing 1D CNN with TCN improved F1-score by 12%.
Pretraining on Synthetic Data
An important technique: we pretrain the CNN on synthetic candlestick data with known patterns (programmatically generate 'head and shoulders', triangles, etc. with labels). This gives the model an initial understanding of patterns before fine-tuning on real data. Synthetic generation allows creating tens of thousands of labeled examples, unavailable in real markets.
What's Included in the Work
- Architectural design (selecting CNN/TCN/hybrid topology) for your data
- Implementation in PyTorch 2.x with mixed precision and multi-GPU support
- Pretraining on synthetic data and fine-tuning on your historical candles
- Development of the inference pipeline and integration via REST API (FastAPI)
- Documentation (architecture, configuration, retraining instructions)
- Training your team and support for 1 month after delivery
CNN Model Development Stages
The development process includes the following steps:
- Data analysis and problem definition (1-2 weeks): study historical candles, define target patterns, prepare a benchmark.
- Architecture design (1 week): choose 1D/2D/TCN/hybrid, design augmentation pipeline.
- Implementation and pretraining (2-4 weeks): implement in PyTorch, pretrain on synthetic data, fine-tune.
- Testing and optimization (1-2 weeks): validate on a held-out set, cross-validation, ensembling.
- Integration and documentation (1-2 weeks): REST API, instructions, team training.
Why Work with Us?
Our experience includes over 30 deployments of CNN models in trading systems, including DeFi protocols and prop trading. We guarantee achieving target metrics for precision and recall. Get a consultation on your task and a preliminary timeline estimate — contact us. Order model development and receive a preliminary architecture and cost estimate within 3 business days.







