Automated trading bot that analyzes even/odd patterns and executes trades on Deriv
Size
22.7 KB
Version
1.1.14
Created
Dec 21, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name Deriv Even/Odd Trading Bot
3// @description Automated trading bot that analyzes even/odd patterns and executes trades on Deriv
4// @version 1.1.14
5// @match https://*.app.deriv.com/*
6// @icon https://app.deriv.com/favicon.ico
7// @grant GM.getValue
8// @grant GM.setValue
9// ==/UserScript==
10(function() {
11 'use strict';
12
13 // Bot configuration
14 let botConfig = {
15 isRunning: false,
16 tradeAmount: 1,
17 patternLength: 15, // Number of ticks to analyze (increased for better accuracy)
18 minConfidence: 70, // Minimum confidence percentage to trade (raised for quality)
19 consecutiveThreshold: 4, // Number of consecutive same results (stronger signals)
20 delayBetweenTrades: 5000, // Delay in ms between trades
21 martingaleEnabled: true, // Enable Martingale recovery
22 martingaleMultiplier: 2, // Multiply stake by this after loss
23 maxMartingaleSteps: 5, // Maximum number of Martingale steps (1->2->4->8->16->32)
24 baseStake: 1 // Base stake amount
25 };
26
27 let digitHistory = [];
28 let lastTradeTime = 0;
29 let lastDigitSnapshot = '';
30 let currentStake = 1;
31 let martingaleStep = 0;
32 let lastResultElement = null;
33 let consecutiveLosses = 0;
34 let botStats = {
35 totalTrades: 0,
36 wins: 0,
37 losses: 0,
38 profit: 0
39 };
40
41 // Utility function to debounce
42 function debounce(func, wait) {
43 let timeout;
44 return function executedFunction(...args) {
45 const later = () => {
46 clearTimeout(timeout);
47 func(...args);
48 };
49 clearTimeout(timeout);
50 timeout = setTimeout(later, wait);
51 };
52 }
53
54 // Analyze pattern and predict next move with improved logic
55 function analyzePattern(digits) {
56 if (digits.length < 10) {
57 return { prediction: null, confidence: 0, reason: 'Insufficient data (need at least 10 ticks)' };
58 }
59
60 const recentDigits = digits.slice(-botConfig.patternLength);
61 let evenCount = 0;
62 let oddCount = 0;
63
64 recentDigits.forEach(digit => {
65 const num = parseInt(digit);
66 if (num % 2 === 0) evenCount++;
67 else oddCount++;
68 });
69
70 // Check for consecutive patterns (most reliable)
71 const lastFew = digits.slice(-botConfig.consecutiveThreshold);
72 const allEven = lastFew.every(d => parseInt(d) % 2 === 0);
73 const allOdd = lastFew.every(d => parseInt(d) % 2 !== 0);
74
75 let prediction = null;
76 let confidence = 0;
77 let reason = '';
78
79 // TREND FOLLOWING STRATEGY - Trade WITH the trend, not against it
80
81 // Strategy 1: Strong consecutive pattern continuation (highest confidence)
82 if (allEven) {
83 prediction = 'even';
84 confidence = 78;
85 reason = `${botConfig.consecutiveThreshold} consecutive EVEN digits - following EVEN trend`;
86 } else if (allOdd) {
87 prediction = 'odd';
88 confidence = 78;
89 reason = `${botConfig.consecutiveThreshold} consecutive ODD digits - following ODD trend`;
90 }
91 // Strategy 2: Very strong statistical dominance (75%+ dominance)
92 else {
93 const total = evenCount + oddCount;
94 const evenPercentage = (evenCount / total) * 100;
95 const oddPercentage = (oddCount / total) * 100;
96
97 if (evenPercentage >= 75) {
98 prediction = 'even';
99 confidence = Math.min(evenPercentage, 85);
100 reason = `Very strong EVEN dominance: ${evenPercentage.toFixed(1)}% - following EVEN trend`;
101 } else if (oddPercentage >= 75) {
102 prediction = 'odd';
103 confidence = Math.min(oddPercentage, 85);
104 reason = `Very strong ODD dominance: ${oddPercentage.toFixed(1)}% - following ODD trend`;
105 }
106 // Strategy 3: Strong dominance (70-74% dominance)
107 else if (evenPercentage >= 70 && evenPercentage < 75) {
108 prediction = 'even';
109 confidence = 72;
110 reason = `Strong EVEN dominance: ${evenPercentage.toFixed(1)}% - following EVEN trend`;
111 } else if (oddPercentage >= 70 && oddPercentage < 75) {
112 prediction = 'odd';
113 confidence = 72;
114 reason = `Strong ODD dominance: ${oddPercentage.toFixed(1)}% - following ODD trend`;
115 }
116 // Strategy 4: Check last 6 digits for strong mini-trends
117 else {
118 const last6 = digits.slice(-6);
119 let even6 = 0;
120 let odd6 = 0;
121 last6.forEach(d => {
122 if (parseInt(d) % 2 === 0) even6++;
123 else odd6++;
124 });
125
126 if (even6 >= 5) {
127 prediction = 'even';
128 confidence = 70;
129 reason = `Last 6 ticks: ${even6} EVEN vs ${odd6} ODD - strong EVEN trend`;
130 } else if (odd6 >= 5) {
131 prediction = 'odd';
132 confidence = 70;
133 reason = `Last 6 ticks: ${odd6} ODD vs ${even6} EVEN - strong ODD trend`;
134 } else {
135 // No clear strong pattern - skip trade
136 return {
137 prediction: null,
138 confidence: 0,
139 reason: `No strong pattern detected (E:${evenCount} O:${oddCount}, ${evenPercentage.toFixed(1)}% vs ${oddPercentage.toFixed(1)}%)`,
140 evenCount,
141 oddCount
142 };
143 }
144 }
145 }
146
147 // Cooling off period: After 2+ losses, require even higher confidence
148 if (consecutiveLosses >= 2 && confidence < 75) {
149 return {
150 prediction: null,
151 confidence: 0,
152 reason: `Cooling off after ${consecutiveLosses} losses - need 75%+ confidence (current: ${confidence}%)`,
153 evenCount,
154 oddCount
155 };
156 }
157
158 return { prediction, confidence, reason, evenCount, oddCount };
159 }
160
161 // Monitor trade results
162 function monitorTradeResults() {
163 const resultOverlay = document.querySelector('.dc-contract-card__result');
164
165 if (resultOverlay && resultOverlay !== lastResultElement) {
166 lastResultElement = resultOverlay;
167
168 const isWon = resultOverlay.classList.contains('dc-contract-card__result--won');
169 const isLost = resultOverlay.classList.contains('dc-contract-card__result--lost');
170
171 if (isWon) {
172 console.log('β
Trade WON - Resetting stake to base');
173 botStats.wins++;
174 currentStake = botConfig.baseStake;
175 martingaleStep = 0;
176 consecutiveLosses = 0; // Reset consecutive losses
177 updateBotUI();
178 } else if (isLost) {
179 console.log('β Trade LOST - Applying Martingale');
180 botStats.losses++;
181 consecutiveLosses++;
182
183 if (botConfig.martingaleEnabled && martingaleStep < botConfig.maxMartingaleSteps) {
184 martingaleStep++;
185 currentStake = botConfig.baseStake * Math.pow(botConfig.martingaleMultiplier, martingaleStep);
186 console.log(`π Martingale activated: Step ${martingaleStep}, New stake: ${currentStake}`);
187
188 if (consecutiveLosses >= 2) {
189 console.log(`β οΈ ${consecutiveLosses} consecutive losses - entering cooling off mode`);
190 }
191 } else if (martingaleStep >= botConfig.maxMartingaleSteps) {
192 console.log('β οΈ Max Martingale steps reached - Resetting to base stake');
193 currentStake = botConfig.baseStake;
194 martingaleStep = 0;
195 }
196 updateBotUI();
197 }
198 }
199 }
200
201 // Execute trade
202 async function executeTrade(type) {
203 const now = Date.now();
204 if (now - lastTradeTime < botConfig.delayBetweenTrades) {
205 console.log('β³ Waiting for delay between trades...');
206 return false;
207 }
208
209 try {
210 // Set stake amount input
211 const stakeInput = document.getElementById('dt_amount_input');
212 if (stakeInput) {
213 stakeInput.value = currentStake;
214 stakeInput.dispatchEvent(new Event('input', { bubbles: true }));
215 stakeInput.dispatchEvent(new Event('change', { bubbles: true }));
216 console.log(`π° Stake set to: ${currentStake} (Martingale step: ${martingaleStep})`);
217
218 // Wait a bit for the input to register
219 await new Promise(resolve => setTimeout(resolve, 300));
220 }
221
222 const buttonId = type === 'even' ? 'dt_purchase_digiteven_button' : 'dt_purchase_digitodd_button';
223 const button = document.getElementById(buttonId);
224
225 if (!button) {
226 console.error('β Trade button not found');
227 return false;
228 }
229
230 // Check if button is disabled
231 if (button.disabled || button.classList.contains('btn-purchase--disabled')) {
232 console.log('βΈοΈ Trade button is disabled, waiting...');
233 return false;
234 }
235
236 console.log(`π― Executing ${type.toUpperCase()} trade...`);
237 button.click();
238
239 lastTradeTime = now;
240 botStats.totalTrades++;
241
242 // Save stats
243 await GM.setValue('botStats', JSON.stringify(botStats));
244
245 updateBotUI();
246 return true;
247 } catch (error) {
248 console.error('β Error executing trade:', error);
249 return false;
250 }
251 }
252
253 // Collect digit history
254 function collectDigitHistory() {
255 // Get only the LATEST digit (the one that's actually changing)
256 const latestDigitEl = document.querySelector('.digits__digit--latest .digits__digit-display-value');
257
258 if (latestDigitEl) {
259 const latestDigit = latestDigitEl.textContent.trim();
260
261 // Add to history if it's new
262 if (digitHistory.length === 0 || digitHistory[digitHistory.length - 1] !== latestDigit) {
263 digitHistory.push(latestDigit);
264 console.log('π New digit collected:', latestDigit, '| History:', digitHistory.slice(-10).join(', '));
265
266 // Keep only last 20 digits to prevent memory issues
267 if (digitHistory.length > 20) {
268 digitHistory = digitHistory.slice(-20);
269 }
270 }
271 }
272 }
273
274 // Main bot logic
275 async function runBotCycle() {
276 if (!botConfig.isRunning) return;
277
278 collectDigitHistory();
279
280 if (digitHistory.length < 10) {
281 console.log('β³ Waiting for more data (need 10+ ticks)...');
282 setTimeout(runBotCycle, 2000);
283 return;
284 }
285
286 // Check if digits have changed since last trade
287 const currentSnapshot = digitHistory.join(',');
288 if (currentSnapshot === lastDigitSnapshot && lastTradeTime > 0) {
289 console.log('βΈοΈ Digits unchanged - waiting for new tick...');
290 setTimeout(runBotCycle, 2000);
291 return;
292 }
293
294 const analysis = analyzePattern(digitHistory);
295 console.log('π Analysis:', analysis);
296
297 if (analysis.prediction && analysis.confidence >= botConfig.minConfidence) {
298 console.log(`β
Trading signal: ${analysis.prediction.toUpperCase()} (${analysis.confidence}% confidence)`);
299 console.log(`π Reason: ${analysis.reason}`);
300
301 const success = await executeTrade(analysis.prediction);
302
303 if (success) {
304 // Save snapshot after successful trade
305 lastDigitSnapshot = currentSnapshot;
306 // Wait for trade to complete before next cycle
307 setTimeout(runBotCycle, botConfig.delayBetweenTrades + 3000);
308 } else {
309 setTimeout(runBotCycle, 2000);
310 }
311 } else {
312 console.log(`βΈοΈ No confident signal (${analysis.confidence}% < ${botConfig.minConfidence}%)`);
313 console.log(`π ${analysis.reason}`);
314 setTimeout(runBotCycle, 3000);
315 }
316 }
317
318 // Create bot UI
319 function createBotUI() {
320 const botPanel = document.createElement('div');
321 botPanel.id = 'trading-bot-panel';
322 botPanel.innerHTML = `
323 <div style="position: fixed; top: 80px; right: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); z-index: 10000; min-width: 320px; font-family: Arial, sans-serif;">
324 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
325 <h3 style="margin: 0; font-size: 18px; font-weight: bold;">π€ Trading Bot</h3>
326 <div style="display: flex; gap: 8px;">
327 <button id="bot-minimize" style="background: rgba(255,255,255,0.2); border: none; color: white; width: 28px; height: 28px; border-radius: 6px; cursor: pointer; font-size: 16px;">β</button>
328 <button id="bot-close" style="background: rgba(255,255,255,0.2); border: none; color: white; width: 28px; height: 28px; border-radius: 6px; cursor: pointer; font-size: 16px;">Γ</button>
329 </div>
330 </div>
331
332 <div id="bot-content">
333 <div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 8px; margin-bottom: 12px;">
334 <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
335 <span style="font-size: 13px; opacity: 0.9;">Status:</span>
336 <span id="bot-status" style="font-weight: bold; font-size: 13px;">βΈοΈ Stopped</span>
337 </div>
338 <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
339 <span style="font-size: 13px; opacity: 0.9;">Trades:</span>
340 <span id="bot-trades" style="font-weight: bold; font-size: 13px;">0</span>
341 </div>
342 <div style="display: flex; justify-content: space-between;">
343 <span style="font-size: 13px; opacity: 0.9;">Win Rate:</span>
344 <span id="bot-winrate" style="font-weight: bold; font-size: 13px;">0%</span>
345 </div>
346 </div>
347
348 <div style="margin-bottom: 12px;">
349 <label style="display: block; font-size: 12px; margin-bottom: 6px; opacity: 0.9;">Min Confidence: <span id="confidence-value">${botConfig.minConfidence}%</span></label>
350 <input type="range" id="confidence-slider" min="50" max="80" value="${botConfig.minConfidence}" style="width: 100%; cursor: pointer;">
351 </div>
352
353 <div style="margin-bottom: 12px;">
354 <label style="display: block; font-size: 12px; margin-bottom: 6px; opacity: 0.9;">Pattern Length: <span id="pattern-value">${botConfig.patternLength}</span></label>
355 <input type="range" id="pattern-slider" min="5" max="20" value="${botConfig.patternLength}" style="width: 100%; cursor: pointer;">
356 </div>
357
358 <div style="margin-bottom: 15px;">
359 <label style="display: block; font-size: 12px; margin-bottom: 6px; opacity: 0.9;">Consecutive Threshold: <span id="consecutive-value">${botConfig.consecutiveThreshold}</span></label>
360 <input type="range" id="consecutive-slider" min="2" max="5" value="${botConfig.consecutiveThreshold}" style="width: 100%; cursor: pointer;">
361 </div>
362
363 <button id="bot-toggle" style="width: 100%; padding: 12px; background: #48bb78; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; cursor: pointer; transition: all 0.3s;">
364 βΆοΈ Start Bot
365 </button>
366
367 <button id="bot-reset" style="width: 100%; padding: 10px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 8px; font-size: 13px; cursor: pointer; margin-top: 8px;">
368 π Reset Stats
369 </button>
370 </div>
371 </div>
372 `;
373
374 document.body.appendChild(botPanel);
375 attachBotListeners();
376 }
377
378 // Update bot UI
379 function updateBotUI() {
380 const statusEl = document.getElementById('bot-status');
381 const tradesEl = document.getElementById('bot-trades');
382 const winrateEl = document.getElementById('bot-winrate');
383 const toggleBtn = document.getElementById('bot-toggle');
384
385 if (statusEl) {
386 statusEl.textContent = botConfig.isRunning ? 'β
Running' : 'βΈοΈ Stopped';
387 }
388
389 if (tradesEl) {
390 tradesEl.textContent = botStats.totalTrades;
391 }
392
393 if (winrateEl) {
394 const winRate = botStats.totalTrades > 0
395 ? ((botStats.wins / botStats.totalTrades) * 100).toFixed(1)
396 : 0;
397 winrateEl.textContent = `${winRate}%`;
398 }
399
400 if (toggleBtn) {
401 if (botConfig.isRunning) {
402 toggleBtn.textContent = 'βΈοΈ Stop Bot';
403 toggleBtn.style.background = '#f56565';
404 } else {
405 toggleBtn.textContent = 'βΆοΈ Start Bot';
406 toggleBtn.style.background = '#48bb78';
407 }
408 }
409 }
410
411 // Attach event listeners
412 function attachBotListeners() {
413 const toggleBtn = document.getElementById('bot-toggle');
414 const resetBtn = document.getElementById('bot-reset');
415 const minimizeBtn = document.getElementById('bot-minimize');
416 const closeBtn = document.getElementById('bot-close');
417 const confidenceSlider = document.getElementById('confidence-slider');
418 const patternSlider = document.getElementById('pattern-slider');
419 const consecutiveSlider = document.getElementById('consecutive-slider');
420
421 toggleBtn.addEventListener('click', async () => {
422 botConfig.isRunning = !botConfig.isRunning;
423 await GM.setValue('botRunning', botConfig.isRunning);
424
425 if (botConfig.isRunning) {
426 console.log('π Bot started!');
427 runBotCycle();
428 } else {
429 console.log('βΈοΈ Bot stopped!');
430 }
431
432 updateBotUI();
433 });
434
435 resetBtn.addEventListener('click', async () => {
436 botStats = { totalTrades: 0, wins: 0, losses: 0, profit: 0 };
437 await GM.setValue('botStats', JSON.stringify(botStats));
438 updateBotUI();
439 console.log('π Stats reset!');
440 });
441
442 closeBtn.addEventListener('click', () => {
443 if (botConfig.isRunning) {
444 alert('Please stop the bot before closing.');
445 return;
446 }
447 const panel = document.getElementById('trading-bot-panel');
448 if (panel) {
449 panel.remove();
450 console.log('π΄ Bot panel closed');
451 }
452 });
453
454 minimizeBtn.addEventListener('click', () => {
455 const content = document.getElementById('bot-content');
456 if (content.style.display === 'none') {
457 content.style.display = 'block';
458 minimizeBtn.textContent = 'β';
459 } else {
460 content.style.display = 'none';
461 minimizeBtn.textContent = '+';
462 }
463 });
464
465 confidenceSlider.addEventListener('input', (e) => {
466 botConfig.minConfidence = parseInt(e.target.value);
467 document.getElementById('confidence-value').textContent = `${botConfig.minConfidence}%`;
468 });
469
470 patternSlider.addEventListener('input', (e) => {
471 botConfig.patternLength = parseInt(e.target.value);
472 document.getElementById('pattern-value').textContent = botConfig.patternLength;
473 });
474
475 consecutiveSlider.addEventListener('input', (e) => {
476 botConfig.consecutiveThreshold = parseInt(e.target.value);
477 document.getElementById('consecutive-value').textContent = botConfig.consecutiveThreshold;
478 });
479 }
480
481 // Monitor for new digits using MutationObserver
482 function setupDigitMonitor() {
483 const debouncedCollect = debounce(() => {
484 collectDigitHistory();
485 }, 500);
486
487 const observer = new MutationObserver(debouncedCollect);
488
489 const digitsContainer = document.querySelector('.digits__digit-container');
490 if (digitsContainer) {
491 observer.observe(digitsContainer, {
492 childList: true,
493 subtree: true,
494 characterData: true
495 });
496 console.log('π Digit monitor active');
497 }
498 }
499
500 // Monitor for trade results
501 function setupResultMonitor() {
502 const observer = new MutationObserver(() => {
503 monitorTradeResults();
504 });
505
506 // Watch the entire body for result overlays
507 observer.observe(document.body, {
508 childList: true,
509 subtree: true,
510 attributes: true,
511 attributeFilter: ['class']
512 });
513
514 console.log('π Result monitor active');
515 }
516
517 // Initialize bot
518 async function init() {
519 console.log('π€ Deriv Trading Bot initializing...');
520
521 // Load saved stats
522 const savedStats = await GM.getValue('botStats', null);
523 if (savedStats) {
524 botStats = JSON.parse(savedStats);
525 }
526
527 // Wait for page to load
528 if (document.readyState === 'loading') {
529 document.addEventListener('DOMContentLoaded', () => {
530 setTimeout(() => {
531 createBotUI();
532 setupDigitMonitor();
533 setupResultMonitor();
534 collectDigitHistory();
535 }, 2000);
536 });
537 } else {
538 setTimeout(() => {
539 createBotUI();
540 setupDigitMonitor();
541 setupResultMonitor();
542 collectDigitHistory();
543 }, 2000);
544 }
545
546 console.log('β
Bot ready! Use the panel to start trading.');
547 }
548
549 init();
550})();