MavinGC's picture
Add a visual of the chart selected, where the candlestick patterns are detected. Eg eurusd current chart with the pattern detected is highlighted in a warm color.
a83886b verified
Raw History Blame Contribute Delete
13.4 kB
document.addEventListener('DOMContentLoaded', function() {
// Initialize canvas for pattern drawing
const canvas = document.getElementById('drawingCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let lastX = 0;
let lastY = 0;
let drawingHistory = [];
let currentStep = -1;
// Set canvas dimensions
function resizeCanvas() {
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
redrawCanvas();
}
// Redraw canvas from history
function redrawCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#4f46e5';
ctx.lineWidth = 2;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
for(let i = 0; i <= currentStep; i++) {
const path = drawingHistory[i];
if(path.length > 0) {
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
for(let j = 1; j < path.length; j++) {
ctx.lineTo(path[j].x, path[j].y);
}
ctx.stroke();
}
}
}
// Canvas event listeners
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
canvas.addEventListener('touchend', stopDrawing);
function startDrawing(e) {
isDrawing = true;
const pos = getPosition(e);
lastX = pos.x;
lastY = pos.y;
// Start a new path in history
currentStep++;
drawingHistory = drawingHistory.slice(0, currentStep);
drawingHistory.push([]);
drawingHistory[currentStep].push({x: lastX, y: lastY});
}
function draw(e) {
if(!isDrawing) return;
const pos = getPosition(e);
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
lastX = pos.x;
lastY = pos.y;
drawingHistory[currentStep].push({x: lastX, y: lastY});
}
function stopDrawing() {
isDrawing = false;
}
function handleTouchStart(e) {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
function handleTouchMove(e) {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
function getPosition(e) {
const rect = canvas.getBoundingClientRect();
return {
x: (e.clientX || e.touches[0].clientX) - rect.left,
y: (e.clientY || e.touches[0].clientY) - rect.top
};
}
// Clear and Undo buttons
document.getElementById('clearCanvas').addEventListener('click', function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawingHistory = [];
currentStep = -1;
});
document.getElementById('undoCanvas').addEventListener('click', function() {
if(currentStep >= 0) {
currentStep--;
redrawCanvas();
}
});
// File upload handling
document.getElementById('patternUpload').addEventListener('change', function(e) {
const file = e.target.files[0];
if(file) {
document.getElementById('fileName').textContent = file.name;
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
// Clear canvas and draw the image
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Reset drawing history
drawingHistory = [];
currentStep = -1;
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
}
});
// Platform selection
const platformButtons = document.querySelectorAll('.platform-btn');
platformButtons.forEach(button => {
button.addEventListener('click', function() {
platformButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
});
});
// Range sliders
const matchStrength = document.getElementById('matchStrength');
const matchValue = document.getElementById('matchValue');
const lookbackPeriod = document.getElementById('lookbackPeriod');
const lookbackValue = document.getElementById('lookbackValue');
matchStrength.addEventListener('input', function() {
matchValue.textContent = `${this.value}% match`;
});
lookbackPeriod.addEventListener('input', function() {
lookbackValue.textContent = `${this.value} candles`;
});
// Save pattern
document.getElementById('savePattern').addEventListener('click', function() {
const patternName = document.getElementById('patternName').value.trim();
const patternType = document.getElementById('patternType').value;
if(!patternName) {
alert('Please enter a pattern name');
return;
}
if(drawingHistory.length === 0) {
alert('Please draw or upload a pattern first');
return;
}
// In a real app, you would save this to a database
// For demo, we'll just add it to the pattern selector
const option = document.createElement('option');
option.value = patternName.toLowerCase().replace(/\s+/g, '-');
option.textContent = `${patternName} (${patternType})`;
document.getElementById('selectedPattern').appendChild(option);
// Show success message
alert(`Pattern "${patternName}" saved successfully!`);
});
// Start scan
document.getElementById('startScan').addEventListener('click', function() {
const selectedPattern = document.getElementById('selectedPattern').value;
const instrument = document.getElementById('marketInstrument').value.trim();
const timeframe = document.getElementById('timeframe').value;
const timeFilter = document.getElementById('timeFilter').value;
if(selectedPattern === 'Select a saved pattern...') {
alert('Please select a pattern to scan for');
return;
}
if(!instrument) {
alert('Please enter a market instrument');
return;
}
// Show loading state
const resultsContainer = document.getElementById('resultsContainer');
resultsContainer.innerHTML = `
<div class="flex flex-col items-center justify-center py-12">
<div class="w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
<p class="text-gray-600 dark:text-gray-400">Scanning market for patterns...</p>
</div>
`;
// Show results section
document.getElementById('resultsSection').classList.remove('hidden');
// Simulate API call with timeout
setTimeout(() => {
// Mock results with timestamps that respect the time filter
const now = new Date();
const mockResults = [];
// Generate results based on time filter
if (timeFilter === '0' || timeFilter === '24') {
mockResults.push({
match: 92,
date: new Date(now.getTime() - 6 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 16),
timeframe: '1h',
instrument: instrument,
preview: 'http://static.photos/finance/320x240/1'
});
}
if (timeFilter === '0' || timeFilter === '8' || timeFilter === '24') {
mockResults.push({
match: 87,
date: new Date(now.getTime() - 3 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 16),
timeframe: '4h',
instrument: instrument,
preview: 'http://static.photos/finance/320x240/2'
});
}
if (timeFilter === '0') {
mockResults.push({
match: 85,
date: new Date(now.getTime() - 48 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 16),
timeframe: timeframe,
instrument: instrument,
preview: 'http://static.photos/finance/320x240/3'
});
}
// Add recent results if time filter is short
if (timeFilter !== '0') {
const hoursAgo = parseFloat(timeFilter);
const maxAge = hoursAgo * 60 * 60 * 1000;
// Add some recent results
for (let i = 1; i <= 3; i++) {
const minutesAgo = Math.floor(Math.random() * hoursAgo * 60);
if (minutesAgo * 60 * 1000 <= maxAge) {
mockResults.push({
match: 80 + Math.floor(Math.random() * 15),
date: new Date(now.getTime() - minutesAgo * 60 * 1000).toISOString().replace('T', ' ').substring(0, 16),
timeframe: ['5m','15m','30m','1h'][Math.floor(Math.random() * 4)],
instrument: instrument,
preview: `http://static.photos/finance/320x240/${3+i}`
});
}
}
}
// Display results
displayResults(mockResults);
}, 2000);
});
// Display scan results
function displayResults(results) {
const resultsContainer = document.getElementById('resultsContainer');
if(results.length === 0) {
resultsContainer.innerHTML = `
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
<i data-feather="alert-circle" class="w-12 h-12 mx-auto mb-4"></i>
<p>No matching patterns found</p>
</div>
`;
feather.replace();
return;
}
resultsContainer.innerHTML = results.map(result => `
<div class="result-card bg-gray-50 dark:bg-gray-700 p-4 rounded-lg border-l-4 border-indigo-500 shadow-sm">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div class="flex items-center space-x-4">
<div class="w-16 h-16 bg-gray-200 dark:bg-gray-600 rounded overflow-hidden">
<img src="${result.preview}" alt="Pattern preview" class="w-full h-full object-cover">
</div>
<div>
<h3 class="font-medium text-gray-800 dark:text-white">${result.instrument} (${result.timeframe})</h3>
<p class="text-sm text-gray-600 dark:text-gray-300">${result.date}</p>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="text-center">
<span class="block text-2xl font-bold text-indigo-600 dark:text-indigo-400">${result.match}%</span>
<span class="text-xs text-gray-500 dark:text-gray-400">Match</span>
</div>
<button class="view-chart-btn px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 transition text-sm"
data-instrument="${result.instrument}"
data-timeframe="${result.timeframe}"
data-match="${result.match}">
View Chart
</button>
</div>
</div>
</div>
`).join('');
feather.replace();
// Add event listeners to view chart buttons
document.querySelectorAll('.view-chart-btn').forEach(button => {
button.addEventListener('click', () => {
const detail = {
instrument: button.dataset.instrument,
timeframe: button.dataset.timeframe,
match: button.dataset.match
};
const event = new CustomEvent('viewChart', { detail });
document.dispatchEvent(event);
});
});
}
// Initialize
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
});