SiamCafe.net Blog
Technology

Repaint

repaint
Repaint | SiamCafe Blog
2025-06-01· อ. บอม — SiamCafe.net· 1,881 คำ

Repaint คืออะไร — ปัญหา Indicator Repaint ในการเทรด

Repaint (รีเพนท์) คือปรากฏการณ์ที่ indicator บนกราฟเทรดเปลี่ยนแปลงค่าย้อนหลังหลังจากที่ bar ปิดแล้ว ทำให้ signal ที่เห็นในอดีตไม่ตรงกับ signal ที่เกิดขึ้นจริงในขณะนั้น เป็นปัญหาสำคัญที่ทำให้ backtesting ให้ผลดีเกินจริง แต่เมื่อเทรดจริงกลับขาดทุน เพราะ indicator ที่ repaint จะแสดง signal ที่ "สมบูรณ์แบบ" ในอดีต แต่ใน real-time จะให้ signal ที่เปลี่ยนไปมา บทความนี้อธิบายปัญหา repaint วิธีตรวจสอบ และ Python tools สำหรับทดสอบ

ประเภทของ Repaint

# repaint_types.py — Types of indicator repainting
import json

class RepaintTypes:
 TYPES = {
 "future_data": {
 "name": "Future Data Repaint",
 "description": "Indicator ใช้ข้อมูลในอนาคตคำนวณ — ย้อนกลับไปแก้ค่าเก่า",
 "example": "Zigzag indicator — วาด high/low ย้อนหลังหลังจากรู้ว่า peak/trough อยู่ตรงไหน",
 "danger": "สูงมาก — backtest ดีเกินจริง 100%",
 },
 "current_bar": {
 "name": "Current Bar Repaint",
 "description": "Indicator เปลี่ยนค่าบน bar ปัจจุบันก่อนที่ bar จะปิด — ปกติ ไม่อันตราย",
 "example": "Moving Average บน bar ปัจจุบัน — ค่าเปลี่ยนตาม tick ใหม่",
 "danger": "ต่ำ — ปกติของ indicator ทุกตัว (แก้ได้โดยใช้ค่าจาก bar ก่อนหน้า)",
 },
 "historical_repaint": {
 "name": "Historical Repaint",
 "description": "Indicator เปลี่ยนค่าของ bars ที่ปิดแล้ว — อันตรายมาก",
 "example": "บาง RSI/Stochastic versions ที่คำนวณใหม่เมื่อมี data ใหม่",
 "danger": "สูง — signal ในอดีตไม่น่าเชื่อถือ",
 },
 "recalculation": {
 "name": "Recalculation Repaint",
 "description": "Indicator คำนวณใหม่ทั้ง history เมื่อ reload chart หรือเปลี่ยน timeframe",
 "example": "บาง custom indicators ที่ใช้ global variables",
 "danger": "ปานกลาง — ผลจาก backtest ไม่ consistent",
 },
 }

 COMMON_REPAINTERS = {
 "zigzag": "Zigzag — วาด highs/lows ย้อนหลัง (future data)",
 "fractal": "Williams Fractal — ต้องรอ 2 bars ยืนยัน (delayed, ไม่ repaint ถ้า code ถูก)",
 "renko": "Renko Charts — อาจ repaint ถ้า real-time calculation ต่างจาก historical",
 "heiken_ashi": "Heiken Ashi — ใช้ average ของ OHLC → ค่าเปลี่ยนบน current bar",
 "supertrend": "SuperTrend — บาง version repaint, บาง version ไม่",
 }

 NON_REPAINTERS = {
 "sma": "Simple Moving Average — ไม่ repaint (ค่า fixed หลัง bar ปิด)",
 "ema": "Exponential Moving Average — ไม่ repaint",
 "macd": "MACD — ไม่ repaint (based on EMA)",
 "rsi": "RSI (standard) — ไม่ repaint",
 "bollinger": "Bollinger Bands — ไม่ repaint",
 "atr": "ATR — ไม่ repaint",
 }

 def show_types(self):
 print("=== Types of Repaint ===\n")
 for key, t in self.TYPES.items():
 print(f"[{t['name']}]")
 print(f" {t['description']}")
 print(f" Danger: {t['danger']}")
 print()

 def show_indicators(self):
 print("=== Common Repainting Indicators ===")
 for name, desc in self.COMMON_REPAINTERS.items():
 print(f" ⚠️ {desc}")
 print(f"\n=== Non-Repainting Indicators ===")
 for name, desc in self.NON_REPAINTERS.items():
 print(f" ✅ {desc}")

types = RepaintTypes()
types.show_types()
types.show_indicators()

วิธีตรวจสอบ Repaint

# detect_repaint.py — How to detect repainting
import json

class DetectRepaint:
 METHODS = {
 "visual_test": {
 "name": "1. Visual Test (ทดสอบด้วยตา)",
 "steps": [
 "เปิด indicator บน chart real-time",
 "สังเกต signal ล่าสุด — จด position ของ arrow/line",
 "รอ bar ใหม่เปิด — ดูว่า signal เก่าเปลี่ยนหรือไม่",
 "ถ้า signal เก่าเปลี่ยน/หายไป = REPAINT",
 "ทำซ้ำหลายครั้งเพื่อยืนยัน",
 ],
 },
 "bar_replay": {
 "name": "2. Bar Replay Test",
 "steps": [
 "ใช้ Bar Replay ใน TradingView หรือ MT4 Strategy Tester",
 "เล่น bar ทีละ bar — ดู signal ที่เกิดขึ้น",
 "เปรียบเทียบกับ signal ที่เห็นบน historical chart",
 "ถ้าต่างกัน = REPAINT",
 ],
 },
 "code_review": {
 "name": "3. Code Review",
 "steps": [
 "ดู source code ของ indicator",
 "ตรวจสอบว่าใช้ bar[0] (current bar) หรือ bar[1] (previous bar)",
 "ตรวจว่ามีการ reference future bars หรือไม่",
 "ตรวจว่าคำนวณบน closed bars เท่านั้นหรือไม่",
 ],
 "red_flags": [
 "ใช้ iHighest/iLowest แล้ว plot ย้อนหลัง",
 "ใช้ bar index ที่เปลี่ยนตาม data ใหม่",
 "ใช้ future data (lookahead bias)",
 ],
 },
 "dual_chart": {
 "name": "4. Dual Chart Test",
 "steps": [
 "เปิด 2 charts เดียวกัน — chart 1 เปิด indicator, chart 2 ไม่เปิด",
 "Screenshot indicator signals ทุก 1 ชั่วโมง",
 "เปรียบเทียบ screenshots — signal เปลี่ยนหรือไม่",
 ],
 },
 }

 def show_methods(self):
 print("=== วิธีตรวจสอบ Repaint ===\n")
 for key, method in self.METHODS.items():
 print(f"[{method['name']}]")
 for step in method['steps'][:3]:
 print(f" • {step}")
 print()

detect = DetectRepaint()
detect.show_methods()

MQL4 Non-Repaint Indicator

// non_repaint.mq4 — Non-repainting indicator example

// === วิธีเขียน indicator ที่ไม่ repaint ===

// Rule 1: คำนวณเฉพาะ closed bars (bar[1] ขึ้นไป)
// Rule 2: ไม่เปลี่ยนค่าที่ plot แล้วบน closed bars
// Rule 3: ไม่ใช้ future data

// ตัวอย่าง: Non-Repaint Moving Average Crossover Signal
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrGreen // Buy signal
#property indicator_color2 clrRed // Sell signal

input int FastMA = 10;
input int SlowMA = 20;

double BuyBuffer[];
double SellBuffer[];

int OnInit()
{
 SetIndexBuffer(0, BuyBuffer);
 SetIndexBuffer(1, SellBuffer);
 SetIndexStyle(0, DRAW_ARROW);
 SetIndexStyle(1, DRAW_ARROW);
 SetIndexArrow(0, 233); // Up arrow
 SetIndexArrow(1, 234); // Down arrow
 return INIT_SUCCEEDED;
}

int OnCalculate(const int rates_total,
 const int prev_calculated,
 const datetime &time[],
 const double &open[],
 const double &high[],
 const double &low[],
 const double &close[],
 const long &tick_volume[],
 const long &volume[],
 const int &spread[])
{
 int limit = rates_total - prev_calculated;
 if (limit > rates_total - SlowMA - 2) 
 limit = rates_total - SlowMA - 2;
 
 // IMPORTANT: เริ่มจาก bar 1 (closed bar) — ไม่ใช่ bar 0
 for (int i = limit; i >= 1; i--)
 {
 double fastMA_current = iMA(NULL, 0, FastMA, 0, MODE_EMA, PRICE_CLOSE, i);
 double slowMA_current = iMA(NULL, 0, SlowMA, 0, MODE_EMA, PRICE_CLOSE, i);
 double fastMA_prev = iMA(NULL, 0, FastMA, 0, MODE_EMA, PRICE_CLOSE, i + 1);
 double slowMA_prev = iMA(NULL, 0, SlowMA, 0, MODE_EMA, PRICE_CLOSE, i + 1);
 
 BuyBuffer[i] = EMPTY_VALUE;
 SellBuffer[i] = EMPTY_VALUE;
 
 // Buy: Fast MA crosses above Slow MA (on CLOSED bar)
 if (fastMA_prev <= slowMA_prev && fastMA_current > slowMA_current)
 {
 BuyBuffer[i] = Low[i] - 10 * Point;
 }
 
 // Sell: Fast MA crosses below Slow MA (on CLOSED bar)
 if (fastMA_prev >= slowMA_prev && fastMA_current < slowMA_current)
 {
 SellBuffer[i] = High[i] + 10 * Point;
 }
 }
 
 return rates_total;
}

Python Repaint Detector

# repaint_detector.py — Python tool to detect repainting
import json

class RepaintDetector:
 CODE = """
# repaint_test.py — Test indicator for repainting
import pandas as pd
import numpy as np

class RepaintTester:
 def __init__(self, data):
 '''data: DataFrame with OHLCV'''
 self.data = data.copy()
 
 def test_indicator(self, indicator_func, lookback=0):
 '''Test if indicator repaints by comparing incremental vs full calculation'''
 full_result = indicator_func(self.data)
 
 repaints = 0
 total_checks = 0
 repaint_bars = []
 
 # Simulate bar-by-bar calculation
 for i in range(lookback + 10, len(self.data)):
 # Calculate with data up to bar i
 partial_data = self.data.iloc[:i+1].copy()
 partial_result = indicator_func(partial_data)
 
 # Check if previous bars changed
 for j in range(max(0, i-5), i):
 if j < len(partial_result) and j < len(full_result):
 partial_val = partial_result.iloc[j]
 full_val = full_result.iloc[j]
 
 if not np.isnan(partial_val) and not np.isnan(full_val):
 total_checks += 1
 if abs(partial_val - full_val) > 1e-10:
 repaints += 1
 repaint_bars.append({
 'bar': j,
 'calculated_at': i,
 'partial_value': round(partial_val, 5),
 'full_value': round(full_val, 5),
 })
 
 repaint_pct = (repaints / max(total_checks, 1)) * 100
 
 return {
 'repaints': repaints > 0,
 'repaint_count': repaints,
 'total_checks': total_checks,
 'repaint_pct': round(repaint_pct, 2),
 'verdict': 'REPAINTS' if repaints > 0 else 'SAFE (no repaint)',
 'repaint_bars': repaint_bars[:10],
 }
 
 def test_sma(self, period=20):
 '''Test SMA for repainting (should not repaint)'''
 def sma_indicator(data):
 return data['close'].rolling(period).mean()
 
 return self.test_indicator(sma_indicator, lookback=period)
 
 def test_zigzag(self, depth=12, deviation=5):
 '''Test Zigzag (known to repaint)'''
 def zigzag_indicator(data):
 # Simplified zigzag — will repaint
 result = pd.Series(np.nan, index=data.index)
 high_idx = data['high'].rolling(depth).apply(lambda x: x.argmax())
 low_idx = data['low'].rolling(depth).apply(lambda x: x.argmin())
 return result # Placeholder
 
 return {'verdict': 'KNOWN REPAINTER — Zigzag uses future data by design'}
 
 def backtest_comparison(self, indicator_func, signal_func):
 '''Compare backtest results: full data vs incremental'''
 # Full data signals (potentially with repaint)
 full_signals = signal_func(self.data, indicator_func(self.data))
 
 # Incremental signals (real-time simulation)
 incremental_signals = pd.Series(0, index=self.data.index)
 for i in range(50, len(self.data)):
 partial = self.data.iloc[:i+1]
 indicator = indicator_func(partial)
 signal = signal_func(partial, indicator)
 incremental_signals.iloc[i] = signal.iloc[-1] if len(signal) > 0 else 0
 
 # Compare
 matches = (full_signals == incremental_signals).sum()
 total = len(self.data) - 50
 
 return {
 'match_pct': round(matches / max(total, 1) * 100, 1),
 'mismatches': total - matches,
 'reliable': matches / max(total, 1) > 0.99,
 }

# tester = RepaintTester(data)
# sma_test = tester.test_sma(20)
# print(sma_test['verdict']) # SAFE (no repaint)
"""

 def show_code(self):
 print("=== Repaint Detector ===")
 print(self.CODE[:600])

detector = RepaintDetector()
detector.show_code()

วิธีป้องกัน Repaint

# prevention.py — How to avoid repaint issues
import json

class RepaintPrevention:
 RULES = {
 "use_closed_bars": {
 "name": "ใช้เฉพาะ Closed Bars",
 "description": "คำนวณ signal จาก bar[1] ขึ้นไป — ไม่ใช่ bar[0] (current)",
 "mql4": "ใช้ Close[1] แทน Close[0], iMA(..., 1) แทน iMA(..., 0)",
 },
 "no_future_data": {
 "name": "ไม่ใช้ Future Data",
 "description": "ไม่ reference bars ที่ยังไม่เกิดขึ้น — ไม่ใช้ lookahead",
 "mql4": "หลีกเลี่ยง Zigzag, Fractal ที่ไม่ confirm, บาง pivot indicators",
 },
 "confirm_signals": {
 "name": "ยืนยัน Signal",
 "description": "รอ bar ปิดก่อน act — ไม่เทรดจาก signal ที่ยังไม่ confirm",
 "mql4": "if (Volume[0] > 1) return; // รอ bar ใหม่เริ่ม",
 },
 "test_thoroughly": {
 "name": "ทดสอบอย่างละเอียด",
 "description": "ใช้ Bar Replay, forward test, และ Python repaint detector",
 "tools": "TradingView Bar Replay, MT4 Strategy Tester (visual mode)",
 },
 }

 SAFE_INDICATORS = [
 "SMA, EMA, WMA — Moving Averages ทุกแบบ (ไม่ repaint)",
 "RSI, Stochastic, CCI — Oscillators มาตรฐาน",
 "MACD — based on EMA, ไม่ repaint",
 "Bollinger Bands — based on SMA + StdDev",
 "ATR — Average True Range",
 "ADX — Average Directional Index",
 "Ichimoku — ไม่ repaint (แต่มี displacement ที่ดูเหมือน repaint)",
 ]

 def show_rules(self):
 print("=== Prevention Rules ===\n")
 for key, rule in self.RULES.items():
 print(f"[{rule['name']}]")
 print(f" {rule['description']}")
 print()

 def show_safe(self):
 print("=== Safe (Non-Repaint) Indicators ===")
 for ind in self.SAFE_INDICATORS:
 print(f" ✅ {ind}")

prevention = RepaintPrevention()
prevention.show_rules()
prevention.show_safe()

FAQ - คำถามที่พบบ่อย

Q: Indicator ที่ repaint ใช้ไม่ได้เลยหรือ?

A: ใช้ได้ — แต่ต้องเข้าใจข้อจำกัด: ใช้เป็น reference (ดูภาพรวม) ไม่ใช่ signal ตรง Zigzag: ใช้ดู structure ของ market, ไม่ใช้เป็น entry signal Fractal: ใช้ได้ถ้ารอ confirm (2 bars หลัง fractal) สำคัญ: อย่า backtest ด้วย repainting indicator — ผลจะดีเกินจริง

Q: ทำไม backtest ดีแต่เทรดจริงขาดทุน?

A: สาเหตุหลัก 3 ข้อ: 1) Indicator repaint — signal ในอดีตดูดีเพราะใช้ future data 2) Curve fitting — optimize parameters ให้ fit historical data เกินไป 3) Slippage + spread — backtest ไม่รวมค่า execution จริง วิธีแก้: ใช้ non-repaint indicators, forward test 3+ เดือน, รวม realistic costs ใน backtest

Q: TradingView indicators repaint ไหม?

A: ขึ้นกับ indicator: Built-in (SMA, EMA, RSI): ไม่ repaint Community scripts: ต้องตรวจสอบเอง — อ่าน description + comments วิธีตรวจ: ใช้ Bar Replay ใน TradingView — เล่นทีละ bar ดู signal เปลี่ยนไหม Pine Script: ดูว่าใช้ barstate.isrealtime, security() ที่ lookahead=true หรือไม่

Q: Non-repaint indicator ดีกว่า repaint indicator เสมอไหม?

A: ไม่เสมอไป — non-repaint มี lag มากกว่า (สัญญาณช้ากว่า) Tradeoff: Non-repaint = reliable แต่ late signal, Repaint = early signal แต่ไม่ reliable วิธีที่ดี: ใช้ non-repaint เป็น primary signal + repaint เป็น context (ดูภาพรวม) สำหรับ backtest: ต้องใช้ non-repaint เท่านั้น — ไม่งั้นผลไม่น่าเชื่อถือ

📖 บทความที่เกี่ยวข้อง

forex indicator no Repaint free downloadอ่านบทความ → forex indicator no Repaintอ่านบทความ → forex signal indicator no Repaintอ่านบทความ → xmaster formula indicator forex no Repaintอ่านบทความ →

📚 ดูบทความทั้งหมด →