ชุดโค้ดเทรดเดอร์ — Python และ Pine Script
โค้ดหกชุดให้คัดลอก: Python สี่ชุด (โหลดบันทึก CSV ตัวชี้วัด เส้นทุนพร้อม drawdown และกำหนดขนาดด้วย ATR) กับ Pine Script สองชุด (ช่วงราคารายวันเทียบ ATR พร้อมแจ้งเตือน และช่วงเวลาเทรดตามแผนบนกราฟ) ทั้งหมดเป็นเครื่องมือวิเคราะห์ ใช้คำนวณ วาดกราฟ และเตือน ไม่มีชุดใดสร้างสัญญาณซื้อ/ขายหรือตัดสินใจ เพราะเป็นหน้าที่ของคู่มือรูปแบบการเทรด ชื่อตัวระบุและ API คงภาษาอังกฤษเพื่อให้ตรงกับเอกสาร pandas, Pine Script และข้อความผิดพลาด ส่วนความคิดเห็นและป้ายผลลัพธ์สำหรับผู้อ่านแปลเป็นภาษาไทย แนวทางตรวจโค้ดอธิบายใน เวิร์กช็อป AI สำหรับพรอมป์ต์ที่เกี่ยวกับโค้ด ส่วนคำอธิบายรอบโค้ดใช้ภาษาของหลักสูตรและพาคุณทำทีละขั้น สภาพแวดล้อม: การติดตั้ง Python และเริ่มรันอธิบายใน 11.8; หัวคอลัมน์บันทึกอยู่ใน ส่วนเสริม F.
- เครื่องมือ ไม่ใช่กลยุทธ์: โค้ดคำนวณตัวชี้วัดจากข้อมูลของคุณและติดตามแผนของคุณ ขอบเขตเหมือนหัวข้อ “สิ่งที่ AI ไม่ทำ” ในส่วนเสริม H ด้วยเหตุผลเดียวกัน
- รูปแบบข้อมูลร่วมกัน: โค้ด Python อ่านหัวคอลัมน์จากสมุดงานบันทึกการเทรด (ส่วนเสริม F) อัตราชนะรวมเทรดคุ้มทุนในตัวหาร และค่าคาดหวังใช้ผลสุทธิ เป็นนิยามเดียวกับพรอมป์ต์ P5
- Pine Script ที่ไม่มองข้อมูลอนาคต: S5 ดึงข้อมูลรายวันด้วย
lookahead=barmerge.lookahead_off; S6 ไม่ดึงข้อมูลกรอบเวลาสูงกว่าเลย (ใช้เพียงฟังก์ชันtime) การไม่ใช้ข้อมูลอนาคตไม่ได้พิสูจน์ว่าไม่มี repainting คำอธิบาย S5 แยกสิ่งที่เห็นบนแท่งอดีตและเวลาจริง จุดเสี่ยงเหล่านี้คือสิ่งที่ตรวจในการทบทวนโค้ดด้วยพรอมป์ต์ P3 - เครื่องกำหนดขนาดปัดจำนวนลง: เมื่อแปลงความเสี่ยงเป็นล็อต การปัดขึ้นทำให้เสี่ยงเกินแผนอย่างเป็นระบบ โค้ดบรรทัดเดียวนั้นคือความต่างระหว่าง 1% กับ “ประมาณ 1%”
ก่อนรันโค้ด: กฎสามข้อ
- อ่านก่อนรัน ทุกชุดจงใจเขียนให้สั้น คุณควรเข้าใจทุกบรรทัด หากจะแก้โค้ดที่ไม่เข้าใจ ให้ใช้การทบทวน P3 ช่วย ไม่ใช่เดา กฎนี้ยิ่งสำคัญกับโค้ดจากฟอรัมและแชต
- ข้อมูลเข้าจากไฟล์ แต่โค้ดไม่ได้ส่งการตัดสินใจออกมา สคริปต์อ่านไฟล์ส่งออกบันทึก (รูปแบบส่วนเสริม F) และพารามิเตอร์ที่คุณป้อนชัดเจน ผลลัพธ์คือตัวเลขและกราฟสำหรับทบทวนรายสัปดาห์ คุณเป็นผู้สรุปตามกระบวนการตรวจใน 15.10 โดยคำนึงถึงขนาดตัวอย่าง ความสัมพันธ์ระหว่างเทรด และความไม่แน่นอนของผล
- สภาพแวดล้อมจาก 11.8: Python 3.11 ขึ้นไป พร้อม
pandasและmatplotlib(การติดตั้งและสคริปต์แรกอธิบายไว้ที่นั่น) โค้ด S1–S4 รันทดสอบซ้ำเมื่อ 17 กันยายน 2026 ด้วย Python 3.12.10, pandas 3.0.5 และ matplotlib 3.11.1 ไม่ได้ทดสอบเวอร์ชันอื่น วาง Pine Script ในตัวแก้ไขอินดิเคเตอร์ของแพลตฟอร์มกราฟ ทั้งสองตัวใช้เวอร์ชัน 6
ขั้นตอนขั้นต่ำในการรัน: ในสภาพแวดล้อม Python 3.11+ ตรวจเวอร์ชันด้วย python --versionแล้วติดตั้งแพ็กเกจด้วย python -m pip install pandas==3.0.5 matplotlib==3.11.1บันทึก S1+S2+S3 เป็น analiza_dziennika.py และ CSV ทดสอบเป็น dziennik.csvแล้วรัน python analiza_dziennika.pyS4 ใช้เพียงไลบรารีมาตรฐาน วาง S5 และ S6 ใน Pine Editor แยกกัน โค้ด Pine ทั้งสองตรวจเทียบเอกสาร v6 แล้ว แต่ยังไม่ได้คอมไพล์หรือทดสอบแจ้งเตือนใน TradingView
S1 · โหลดบันทึก CSV
Python 3 · pandasจุดเริ่มต้นสำหรับ S2 และ S3: อ่านไฟล์ส่งออกสมุดงานจากส่วนเสริม F ตรวจหัวคอลัมน์และจำนวนช่อง แล้วทำความสะอาดคอลัมน์ r_net ชื่อคอลัมน์ซ้ำ หัวคอลัมน์ว่าง หรือจำนวนช่องมากหรือน้อยเกินไปจะหยุดการนำเข้า สคริปต์ไม่เลือกเองระหว่างคอลัมน์ผลลัพธ์สองคอลัมน์หรือย้ายข้อมูลไปดัชนี แทนช่องว่างด้วยตัวคั่น เช่น ;A+;;0; แถวที่ขาดท้ายเป็นอีกกรณี ตั้งตัวคั่นและอักขระทศนิยมให้ตรงรูปแบบ CSV (PL: อัฒภาคกับจุลภาค; EN: จุลภาคกับจุด) รวมถึงการเข้ารหัส: ไฟล์จากตัวสร้างในส่วนเสริม F ใช้ UTF-8 และรูปแบบ “CSV UTF-8” ที่บันทึกจาก Excel ก็อ่านด้วย UTF-8 ได้ ส่วนไฟล์เก่าที่ใช้รหัสอักขระโปแลนด์อาจต้องใช้ cp1250 ตรวจรูปแบบการบันทึก เพราะภาษาหน้าจออย่างเดียวไม่กำหนดการเข้ารหัส ค่าว่างของ r_net และค่าว่างของ close_time_utc หมายถึงเทรดยังไม่ปิด (และจะข้าม) มีเวลาปิดแต่ไม่มีผลหมายถึงบันทึกไม่สมบูรณ์และหยุดคำนวณ ค่าที่ไม่ว่างแต่อ่านเป็นตัวเลขไม่ได้ (รวม “NA” หรือขีด) หยุดสคริปต์พร้อมแจ้งหมายเลขระเบียน เช่นเดียวกับเทรดปิดแล้วแต่ไม่มีเวลาปิดตามรูปแบบส่วนเสริม F (2026-07-08 13:15) เพราะไม่เช่นนั้นต้องเดาลำดับเส้นทุน
import pandas as pd
CSV_PATH = "dziennik.csv" # ไฟล์ส่งออกบันทึก (โครงสร้างส่วนเสริม F)
CSV_SEP = ";" # ";" สำหรับ CSV รูปแบบ PL, "," สำหรับ EN
DECIMAL = "," # "," สำหรับ CSV รูปแบบ PL, "." สำหรับ EN
ENCODING = "utf-8-sig" # ตัวสร้างส่วนเสริม F เขียน UTF-8 (รองรับ BOM); CSV เก่าที่ใช้รหัสอักขระโปแลนด์อาจต้องใช้ "cp1250"
REQUIRED = ["r_net", "setup", "setup_class", "error_code", "close_time_utc"]
# อ่านไฟล์ในอุปกรณ์และตรวจโครงสร้างก่อนสร้าง DataFrame
# pandas.read_csv อาจเปลี่ยนชื่อหัวคอลัมน์ซ้ำหรืออนุมานดัชนีจากช่องส่วนเกิน
import csv
try:
with open(CSV_PATH, encoding=ENCODING, newline="") as source:
reader = csv.reader(source, delimiter=CSV_SEP, strict=True)
header = next(reader, None)
if not header:
raise SystemExit("อ่าน CSV ไม่ได้: หัวคอลัมน์ว่าง")
if any(not name or name != name.strip() for name in header):
raise SystemExit("ชื่อคอลัมน์ว่างหรือมีช่องว่างล้อมรอบ — แก้หัวคอลัมน์ CSV")
duplicates = sorted({name for name in header if header.count(name) > 1})
if duplicates:
raise SystemExit(f"คอลัมน์ซ้ำ: {duplicates} — แก้ในบันทึกการเทรด")
rows, record_indexes = [], []
for index, row in enumerate(reader):
if not row: # ระเบียน CSV ที่ว่างทั้งระเบียน
continue
if len(row) != len(header):
raise SystemExit(f"ระเบียน {index + 1}: ต้องมี {len(header)} ช่อง แต่พบ {len(row)} "
f"— ตรวจตัวคั่นและเครื่องหมายคำพูด ไม่มีช่องใดถูกทิ้ง")
rows.append(row)
record_indexes.append(index)
df = pd.DataFrame(rows, columns=header, index=record_indexes, dtype="string").replace("", pd.NA)
except UnicodeDecodeError:
raise SystemExit(f"ถอดรหัสไม่ได้: {CSV_PATH} ด้วย {ENCODING} — ตรวจ UTF-8 หรือ cp1250 แบบเก่า") from None
except (OSError, csv.Error) as exc:
raise SystemExit(f"อ่านไม่ได้: {CSV_PATH}: {exc}") from None
missing = [c for c in REQUIRED if c not in df.columns]
if missing:
raise SystemExit(f"คอลัมน์ขาด: {missing} — ตรวจหัวคอลัมน์เทียบส่วนเสริม F และการตั้งค่า CSV_SEP")
raw = df["r_net"] # ผลว่างอนุญาตเฉพาะเมื่อเวลาปิดว่างด้วย
txt = raw.astype("string").str.strip().str.replace(DECIMAL, ".", regex=False) # แปลงคอลัมน์เอง: เซลล์ผิดหนึ่งช่องต้องไม่ซ่อนช่องที่อ่านได้
r = pd.to_numeric(txt, errors="coerce").astype("Float64") # ป้องกันจำนวนเต็มล้นวนกลับในการรวมภายหลัง
r = r.where(r.abs() != float("inf")) # inf ก็ไม่ใช่จำนวนเท่าของ R ที่ถูกต้อง
invalid = raw.notna() & r.isna() # กรอกแล้วแต่อ่านเป็นตัวเลขไม่ได้
if invalid.any():
raise SystemExit(f"r_net ไม่ถูกต้องในระเบียน {(df.index[invalid] + 1).tolist()} "
f"(ระเบียน 1 = แถวแรกใต้หัวคอลัมน์) — ตรวจค่าและ DECIMAL; "
f"NA ขีด และอนันต์ไม่ใช่ตัวเลขที่ใช้ได้")
blank = df.isna().all(axis=1) # ว่างทั้งแถว = แถวสมุดงานที่ไม่ได้ใช้ (ส่งออกทั้งแผ่น XLSX) ไม่ใช่เทรด
df, r = df[~blank], r[~blank]
has_close = df["close_time_utc"].fillna("").str.strip().ne("")
incomplete = r.isna() & has_close
if incomplete.any():
raise SystemExit(f"ไม่มี r_net สำหรับระเบียนที่ปิดแล้ว {(df.index[incomplete] + 1).tolist()} "
f"— เติมผลสุทธิก่อนคำนวณตัวชี้วัด")
open_trades = int(r.isna().sum())
df = df.assign(r_net=r).dropna(subset=["r_net"]) # คงดัชนีไว้เป็นเลขระเบียนสำหรับข้อความด้านล่าง
# เส้นทุนและ drawdown ต้องเรียงเวลา (ส่วนเสริม F: ตามเวลาปิด) เทรดปิดแล้วแต่ไม่มีเวลาปิดที่อ่านได้ทำให้สคริปต์หยุด
close_text = df["close_time_utc"].fillna("").str.strip()
has_time = close_text.str.fullmatch(r"\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?")
closed = pd.to_datetime(close_text.where(has_time), errors="coerce", format="ISO8601", utc=True) # ส่วนเสริม F เขียน 2026-07-08 13:15; รูปแบบคงที่ทำให้การตรวจไม่ขึ้นกับลำดับแถว และ utc=True ป้องกันส่วนท้าย "Z"/"+00:00" ที่หลงมาทำให้ล้มเหลว
if closed.isna().any():
raise SystemExit(f"อ่าน close_time_utc ไม่ได้ในระเบียน {(df.index[closed.isna()] + 1).tolist()} "
f"— ทุกเทรดปิดต้องมีเวลาปิดตามรูปแบบส่วนเสริม F (2026-07-08 13:15); แก้บันทึก ไม่ใช่สคริปต์")
df = df.assign(_t=closed).sort_values("_t", kind="stable").drop(columns="_t").reset_index(drop=True)
print(f"โหลดแล้ว {len(df)} เทรดปิด (ข้าม {open_trades} แถว: ไม่มีเวลาปิดและไม่มี r_net)")
ผลที่ได้: DataFrame ชื่อ dfที่พร้อมใช้กับโค้ดชุดถัดไป เรียงตามเวลาปิด (เส้นทุนและ drawdown คำนวณตามลำดับนี้) พร้อมจำนวนเทรดเปิดที่ข้าม แถวว่างหมายถึงระเบียนที่ว่างทั้งหมด เช่น เมื่อส่งออกทั้งแผ่น XLSX สคริปต์ไม่นับแถวเหล่านี้ แถวที่มีข้อมูลแต่ไม่มีผลและเวลาปิดจะข้ามในฐานะยังไม่ปิด ส่วนรายการปิดแล้วแต่ไม่มีผลจะหยุดสคริปต์ หากหยุดพร้อมรายการระเบียน ให้แก้แถวที่ระบุในบันทึก ไม่ใช่แก้สคริปต์ หากผิดทั้งคอลัมน์ ให้ตรวจค่าคงที่ DECIMAL ด้วย เลขระเบียนเริ่มหลังหัวคอลัมน์และนับระเบียนว่าง ช่องในเครื่องหมายคำพูดมีตัวคั่นหรือขึ้นบรรทัดใหม่ได้โดยยังเป็นระเบียนเดิม แต่ละเทรดควรปรากฏครั้งเดียว ตรวจตัวระบุ trade_id ก่อนรวมไฟล์ส่งออก เพราะ S1 ไม่ลบประวัติซ้ำ วันที่ไม่มีเวลาไม่พอสร้างลำดับ เวลาไม่มีตัวบ่งชี้เขตเวลาถือเป็น UTC ส่วนค่า offset แบบ ISO 8601 จะแปลงเป็น UTC หากเวลาเหมือนกัน สคริปต์คงลำดับระเบียนไว้ ให้กำหนดลำดับนั้นในบันทึก เพราะมีผลต่อเส้นทาง drawdown
S2 · ตัวชี้วัดบันทึกการเทรด
Python 3 · pandasใช้นิยามเดียวกับพรอมป์ต์ P5 และแดชบอร์ดสมุดงาน: อัตราชนะรวมเทรดคุ้มทุนในตัวหาร; profit factor แสดงข้อความแทนการหารด้วยศูนย์; ค่าคาดหวังคือค่าเฉลี่ยของ R สุทธิ (r_net); และ drawdown วัดจากเส้นสะสมที่เริ่ม 0 R (การขาดทุนครั้งแรกเป็น drawdown แล้ว เช่นเดียวกับสูตรในสมุดงาน F) รันหลัง S1 นิยามตัวชี้วัดตรงกัน แต่นโยบายข้อผิดพลาดต่างจาก P5: S1 หยุดวิเคราะห์เมื่อเทรดปิดแล้วมีผลไม่ถูกต้อง ส่วน P5 สั่งข้ามแถวนั้นและนับจำนวน เทียบผลได้หลังระบุว่าแต่ละตัวอย่างรวมเทรดใดบ้างแล้วเท่านั้น
import math
def metrics(r):
"ตัวชี้วัดหลักของบันทึกจากชุดผลสุทธิเป็นจำนวนเท่าของ R\n นิยาม (เหมือนแดชบอร์ดส่วนเสริม F):\n - อัตราชนะ: เทรดที่ r > 0 หารด้วยทุกเทรดที่ถูกต้อง (คุ้มทุนยังอยู่ในตัวหาร)\n - profit factor: ผลรวม r บวกหารด้วย abs(ผลรวม r ลบ)\n - ค่าคาดหวัง: ค่าเฉลี่ยเลขคณิตของ r (สุทธิหลังต้นทุน)\n - drawdown สูงสุด: การลดลงมากที่สุดของเส้น R สะสมจากจุดสูงสุดที่เคยถึง\n เส้นเริ่ม 0 R (ดังนั้นขาดทุนแรก -1 R ก็เป็น drawdown 1 R แล้ว)\n"
n = len(r)
if n == 0:
raise SystemExit("ไม่มีเทรดที่ถูกต้อง — ไม่มีข้อมูลให้วัด")
try:
r = r.astype(float)
except (TypeError, ValueError):
raise SystemExit("ตัวชี้วัดต้องใช้ค่า R สุทธิเป็นตัวเลข — รัน S1 ก่อน") from None
if not r.map(math.isfinite).all():
raise SystemExit("ตัวชี้วัดต้องใช้ค่า R สุทธิที่มีค่าจำกัด — รัน S1 ก่อน")
wins = r[r > 0]
losses = r[r < 0]
equity = r.cumsum()
peak = equity.cummax().clip(lower=0) # ส่วนเสริม F: MAX(0; จุดสูงสุดที่เคยถึง)
drawdown = peak - equity
try:
total = math.fsum(r)
pf_value = None if losses.empty else math.fsum(wins) / abs(math.fsum(losses))
except (OverflowError, ValueError, ZeroDivisionError):
raise SystemExit("เกินช่วงตัวเลข — ตรวจค่า R; ไม่คืนตัวชี้วัด") from None
if (not equity.map(math.isfinite).all() or not drawdown.map(math.isfinite).all()
or (pf_value is not None and not math.isfinite(pf_value))):
raise SystemExit("เกินช่วงตัวเลข — ตรวจค่า R; ไม่คืนตัวชี้วัด")
pf = "ไม่มีขาดทุนในตัวอย่าง" if pf_value is None else round(pf_value, 2)
return {
"trades": n,
"win_rate": round(len(wins) / n, 3),
"profit_factor": pf,
"expectancy_R": round(total / n, 3),
"sum_R": round(total, 2),
"max_drawdown_R": round(drawdown.max(), 2),
}
for key, value in metrics(df["r_net"]).items():
print(f"{key:>16}: {value}")
ผลที่ได้: ตัวชี้วัดหกตัวสำหรับทบทวนรายสัปดาห์ หากไม่มีขาดทุน PF เป็นข้อความแทนตัวเลข หากค่าเกินช่วงคำนวณ สคริปต์หยุดวิเคราะห์แทนการคืนค่า inf, NaN หรือ drawdown ที่ดูเหมือนศูนย์ WR = 0.4 หมายถึง 40% PF คำนวณใน R ไม่ใช่ PLN หากจำนวนเงินเสี่ยงไม่คงที่ ผลสองแบบนี้อาจต่างกัน ต้นทุนรวมใน r_net แล้ว S2 ไม่หักซ้ำ อย่าคาดหวังให้ค่าเฉลี่ยอย่างเดียวหรือการผ่าน 20 หรือ 50 เทรดประเมินความได้เปรียบให้คุณ นิยามแดชบอร์ดอยู่ใน ส่วนเสริม Fส่วนแผนเลือกตัวอย่าง ต้นทุน และการตรวจสอบอยู่ใน ส่วนเสริม N.
S3 · เส้นทุนและ drawdown
Python 3 · pandas + matplotlibวาดเส้น R สะสมพร้อม drawdown จากจุดสูงสุดด้านล่าง และบันทึก PNG ข้างไฟล์ CSV ที่กำหนดด้วย CSV_PATH (S1) ไม่ว่าจะรันจากไดเรกทอรีใด เส้นเผยลำดับผลและกลุ่มการขาดทุนที่หายไปในตัวเลขรวม นี่คือผลรวม R ไม่ใช่ยอดเงินบัญชีเป็น PLN หรือเปอร์เซ็นต์ส่วนของเจ้าของ หากจำนวนเงินเสี่ยงต่างกัน จะไม่จำลองการเปลี่ยนยอดเงิน กราฟไม่รวมฝาก ถอน หรือผลของสถานะเปิด รันหลัง S1
import math
import matplotlib.pyplot as plt
from pathlib import Path
if len(df) == 0:
raise SystemExit("ไม่มีเทรดปิด — ไม่มีข้อมูลให้วาดกราฟ")
equity = df["r_net"].astype(float).cumsum()
drawdown = equity.cummax().clip(lower=0) - equity # นิยามเดียวกับ S2 และส่วนเสริม F (เส้นเริ่ม 0 R)
if not equity.map(math.isfinite).all() or not drawdown.map(math.isfinite).all():
raise SystemExit("เกินช่วงตัวเลข — ตรวจค่า R; ไม่บันทึกกราฟ")
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(9, 6), sharex=True,
gridspec_kw={"height_ratios": [3, 1]})
x = [0, *(equity.index + 1)] # เทรด 0 = จุดเริ่ม 0 R ที่ใช้วัด drawdown
ax1.plot(x, [0.0, *equity.values])
ax1.set_ylabel("ทุน (R)")
ax1.set_title("เส้นทุน — R สุทธิสะสม")
ax1.grid(True, alpha=0.3)
ax2.fill_between(x, [0.0, *(-drawdown.values)], 0, alpha=0.5)
ax2.set_ylabel("drawdown (R)")
ax2.set_xlabel("เทรด #")
ax2.grid(True, alpha=0.3)
fig.tight_layout()
PNG_PATH = Path(CSV_PATH).with_name("equity_curve.png") # ข้างไฟล์บันทึก ไม่ขึ้นกับไดเรกทอรีทำงาน
fig.savefig(PNG_PATH, dpi=150)
plt.close(fig)
print(f"บันทึกแล้ว: {PNG_PATH}")
ผลที่ได้: ไฟล์ equity_curve.png ข้างบันทึก เส้นเริ่ม 0 R (เทรด 0) และวัด drawdown จากจุดนั้น แผงล่างแสดงการลดลงของ R สะสมจากจุดสูงสุด (เริ่ม 0 R เช่น S2 และสมุดงาน F) ตลอดประวัติ ไม่ใช่ขาดทุนรายสัปดาห์ ตรวจขีดจำกัดรายสัปดาห์จากแผน (ส่วนเสริม E) แยกทีละสัปดาห์ โดยใช้หน่วยและกฎเริ่มนับใหม่ตามแผน
บันทึกเป็น dziennik.csv (UTF-8) ข้างสคริปต์ S1+S2+S3 ที่รวมแล้วและรัน ไฟล์มีเฉพาะคอลัมน์ที่ S1 ต้องใช้ ไฟล์ส่งออกเต็มจากส่วนเสริม F (26 คอลัมน์) โหลดด้วยวิธีเดียวกัน แถวสุดท้ายคือเทรดเปิด (r_net ว่าง) เทรดปิดสองครั้งแรกขาดทุน จึงวัด drawdown จาก 0 R
trade_id;close_time_utc;setup;setup_class;r_net;error_code 2026-041;2026-07-08 13:15;wybicie-azja-londyn;A+;-1,00;5 2026-042;2026-07-09 16:40;powrot-do-sredniej;B;-1,08;3 2026-043;2026-07-10 09:05;wybicie-azja-londyn;A+;1,83;0 2026-044;2026-07-13 12:20;wybicie-azja-londyn;B;0,00;0 2026-045;2026-07-14 14:30;powrot-do-sredniej;C;2,40;0 2026-046;;wybicie-azja-londyn;B;;
ผลที่ควรได้ (Python 3.12.10, pandas 3.0.5, matplotlib 3.11.1; รันเมื่อ 17 กันยายน 2026):
โหลดแล้ว 5 เทรดปิด (ข้าม 1 แถว: ไม่มีเวลาปิดและไม่มี r_net)
trades: 5
win_rate: 0.4
profit_factor: 2.03
expectancy_R: 0.43
sum_R: 2.15
max_drawdown_R: 2.08
บันทึกแล้ว: equity_curve.png
คำนวณตรวจด้วยมือ: กำไร 1.83 + 2.40 = 4.23 R, ขาดทุน 1.00 + 1.08 = 2.08 R → PF 2.03; รวม 2.15 R / 5 = 0.43 R; เส้น −1.00 → −2.08 → −0.25 → −0.25 → 2.15 เริ่มจุดสูงสุด 0 R → drawdown 2.08 R (หากไม่มีจุด 0 R จะได้ 1.08 R นี่คือความต่างที่นิยามในส่วนเสริม F เน้น) ผลนี้อ้างถึงไฟล์นี้และเวอร์ชันที่ระบุโดยตรง หากต่าง ให้ตรวจข้อมูล (ตัวคั่น อักขระทศนิยม การเข้ารหัส ลำดับแถว) ค่าคงที่ใน S1 โค้ด และเวอร์ชันแพ็กเกจ ตามลำดับ
S4 · กำหนดขนาดสถานะด้วย ATR
Python 3 · ไลบรารีมาตรฐานขนาดเป็นล็อตสำหรับระยะตัดขาดทุน k × ATR: จำนวนเงินเสี่ยงมาจากเงินทุนกับเปอร์เซ็นต์ ระยะจาก ATR และปัดขนาดลงตามขั้นล็อตของบัญชี (พารามิเตอร์ lot_step และ min_lot ซึ่งค่าเริ่มต้นทั้งคู่เป็น 0.01) เพื่อให้ความเสี่ยงตามแผนไม่เกินงบ ขาดทุนจริงอาจมากกว่า เพราะการคำนวณไม่รวม slippage ช่องว่างราคาข้ามจุดตัดขาดทุน สเปรด ค่าคอมมิชชัน และสวอป เงินทุนกับมูลค่า pip ต้องเป็นสกุลเงินบัญชีเดียวกัน ตรวจขนาดสูงสุดและมาร์จินในข้อกำหนดแยกด้วย กรอก ATR เป็น pip (EURUSD 0.0080 → 80; USDJPY 0.80 → 80; ขนาด pip อยู่ในโปรไฟล์คู่เงิน ส่วนเสริม B) มูลค่า pip สำหรับ 1 ล็อตอยู่ในข้อกำหนดตราสาร หรือคำนวณด้วยเครื่องมือในส่วนเสริม C
import math
from decimal import Decimal
from fractions import Fraction
def atr_position_size(capital, risk_pct, atr_pips, atr_mult, pip_value_per_lot,
lot_step=0.01, min_lot=0.01):
"ขนาดเป็นล็อตเมื่อระยะตัดขาดทุนเท่ากับ atr_mult × ATR\n atr_pips: ATR เป็น PIP ไม่ใช่หน่วยราคา (EURUSD ATR 0.0080 -> 80; USDJPY ATR 0.80 -> 80 ตามขนาด pip ของคู่เงิน)\n pip_value_per_lot: มูลค่า 1 pip สำหรับ 1.00 ล็อต เป็นสกุลเงินบัญชี\n (เช่น ~10 USD สำหรับ EURUSD ในบัญชี USD ตรวจข้อกำหนดตราสาร)\n lot_step / min_lot: ขั้นขนาดและขั้นต่ำของบัญชีคุณ (0.01 พบบ่อย แต่ไม่ใช่ทุกบัญชี)\n ปัดลงเป็นจำนวนเท่าของ lot_step วัดจากศูนย์ เลขคณิต Fraction\n คงค่าทศนิยมที่ป้อนโดยไม่ขึ้นกับความแม่นยำ Decimal ส่วนกลาง\n ความเสี่ยงตามแผนไม่ใช่ขาดทุนสูงสุด: แบบจำลองไม่รวม slippage ช่องว่างราคา สเปรด และสวอป\n"
inputs = (capital, risk_pct, atr_pips, atr_mult, pip_value_per_lot, lot_step, min_lot)
if any(isinstance(x, bool) or not isinstance(x, (int, float, Decimal)) for x in inputs):
raise ValueError("ใช้ค่าตัวเลข ไม่ใช่ข้อความหรือบูลีน (ทศนิยม Python ใช้จุด)")
decimals = tuple(Decimal(str(x)) for x in inputs)
if not all(x.is_finite() and x > 0 for x in decimals):
raise ValueError("ค่าป้อนทั้งหมดต้องเป็นจำนวนบวกที่มีค่าจำกัด")
c, p, a, k, v, step, minimum = map(Fraction, decimals)
if p > 100:
raise ValueError("risk_pct เป็นเปอร์เซ็นต์ของเงินทุน (1.0 = 1%)")
risk_amount = c * p / 100
sl_pips = a * k
lots = math.floor(risk_amount / (sl_pips * v * step)) * step
if lots < minimum:
return {"warning": "ไม่มีขนาดที่อนุญาตตาม lot_step และ min_lot นี้ภายในงบความเสี่ยง"}
exact = {"risk_amount": risk_amount, "sl_pips": sl_pips,
"lots": lots, "planned_risk": lots * sl_pips * v}
try:
result = {key: float(value) for key, value in exact.items()}
except OverflowError:
raise ValueError("ค่าป้อนเกินช่วงตัวเลขของผลลัพธ์") from None
if not all(math.isfinite(value) and value > 0 for value in result.values()):
raise ValueError("ค่าป้อนเกินช่วงตัวเลขของผลลัพธ์")
return result # float สำหรับแสดงผล ไม่ปัดระยะตัดขาดทุนหรือจำนวนเงินเพิ่ม
print(atr_position_size(capital=10_000, risk_pct=1.0,
atr_pips=80, atr_mult=1.5, pip_value_per_lot=10.0))
# {'risk_amount': 100.0, 'sl_pips': 120.0, 'lots': 0.08, 'planned_risk': 96.0}
ผลที่ได้: ล็อตที่ปัดลงตามขั้นและจำนวนเงินเสี่ยงตามแผนที่เกิดขึ้น (แบบจำลองไม่รวมต้นทุนให้ค่า ≤ งบ; ขาดทุนจริงอาจสูงกว่า) ขั้นวัดจากศูนย์ ตรวจว่าบัญชีใช้ตารางขนาดเช่นนี้ ระยะตัดขาดทุนและจำนวนเงินไม่ปัดเพิ่ม เช่น ATR 0.26 pip × 1.25 เท่ากับ 0.325 pip ไม่ใช่ 0.3 ผลคืนเป็นค่า float สำหรับอ่าน ส่วนจำนวนขั้นล็อตคำนวณด้วยเศษส่วนแม่นตรง ยังต้องตรวจการเปลี่ยนราคาขั้นต่ำของตราสารแยก หากปรับจุดตัดขาดทุนตามขั้นราคานั้น ให้คำนวณขนาดใหม่ สิ่งที่ไม่ควรคาดหวังคือการตัดสินใจเข้าเทรด เครื่องกำหนดขนาดตอบ “เท่าไร” ไม่ใช่ “ควรเข้าหรือไม่”
S5 · ช่วงราคารายวันเทียบ ATR พร้อมแจ้งเตือน
Pine Script v6 · อินดิเคเตอร์เครื่องมือรับรู้ความผันผวน (อินดิเคเตอร์ระหว่างวัน): บนแท่งสด แสดงว่าตลาด “ใช้” ATR รายวันไปกี่เปอร์เซ็นต์แล้ว และเตือนเมื่อข้ามเกณฑ์ เพื่อให้เปิดดูแผน ไม่ใช่ไล่ราคา ดึงข้อมูลรายวันโดยไม่ใช้ lookahead (ไม่ใช้ค่าอนาคต) บนแท่งอดีต request.security คงค่ารายวันที่ยืนยันล่าสุดไว้จนสิ้นช่วง D ถัดไป แล้วผลใหม่ของช่วงนั้นจึงปรากฏบนแท่งระหว่างวันสุดท้าย ในเวลาจริง ดึงช่วงราคาและ ATR ที่ยังพัฒนาของแท่ง D ปัจจุบัน ค่าที่เคยเห็นจึงอาจเปลี่ยนหลังโหลดใหม่ (repainting) “วัน” หมายถึงแท่ง D ของผู้ให้ข้อมูล ไม่ใช่วันปฏิทิน Europe/Warsaw ค่า ATR ในตัวหารก็เปลี่ยนระหว่างวันเช่นกัน 100% ไม่ใช่เพดานการเคลื่อนไหวราคา วิธีนี้สร้างลำดับการข้ามเกณฑ์ภายในวันนี้ย้อนหลังบนแท่งอดีตไม่ได้
//@version=6
indicator("ช่วงราคารายวันเทียบ ATR (เครื่องมือรับรู้ความผันผวน)", overlay=false)
if not timeframe.isintraday
runtime.error("ใช้กราฟระหว่างวันที่แบ่งตามเวลาและต่ำกว่า 1D")
atrLen = input.int(14, "ระยะ ATR", minval=2)
warnPct = input.int(80, "แจ้งเตือนเมื่อช่วงราคารายวันถึง % ของ ATR", minval=10, maxval=200)
// อดีต: ค่า D ยืนยันล่าสุด อัปเดตเมื่อสิ้นแต่ละช่วง D
// เวลาจริง: ค่า D ที่ยังพัฒนา ค่าที่เคยเห็นอาจ repaint หลังโหลดใหม่
dayHigh = request.security(syminfo.tickerid, "D", high, lookahead=barmerge.lookahead_off)
dayLow = request.security(syminfo.tickerid, "D", low, lookahead=barmerge.lookahead_off)
dayAtr = request.security(syminfo.tickerid, "D", ta.atr(atrLen), lookahead=barmerge.lookahead_off)
rangePct = dayAtr > 0 ? 100.0 * (dayHigh - dayLow) / dayAtr : na
plot(rangePct, "ช่วงราคารายวันเป็น % ของ ATR", linewidth=2)
hline(100, "100% ของ ATR")
hline(warnPct, "ระดับแจ้งเตือน", linestyle=hline.style_dotted)
alertcondition(ta.crossover(rangePct, warnPct),
title="ช่วงราคาถึง % ของ ATR",
message="ช่วงราคารายวันถึง % ของ ATR ที่ตั้งไว้ — อ่านแผนใหม่ก่อนเปิดเทรดใด ๆ")
ผลที่ได้: แผงใต้กราฟและเงื่อนไขแจ้งเตือน หลังเพิ่มอินดิเคเตอร์ให้สร้างการแจ้งเตือนในแพลตฟอร์มเอง โดยเลือก “ช่วงราคาถึง % ของ ATR” ใช้แท่งระหว่างวันมาตรฐานที่แบ่งตามเวลา ข้อมูล Heikin Ashi/Renko อาจมีลักษณะต่างออกไป ค่าที่ขาด (na) หมายถึงไม่มี ATR หรือข้อมูลที่จำเป็น ไม่ใช่ 0% เงื่อนไขต้องเปลี่ยนจากค่า ≤ เกณฑ์ ไปเป็น > เกณฑ์ การค้างเหนือเกณฑ์ไม่เตือนซ้ำ “Once Per Bar Close” จำกัดการเตือนไว้ตอนปิดแท่งระหว่างวัน แต่ไม่ได้ยืนยันทั้งแท่ง D สร้างการแจ้งเตือนใหม่หลังเปลี่ยนพารามิเตอร์ สิ่งที่ไม่ควรคาดหวังคือสัญญาณทิศทาง ค่าสูงบอกว่า “การเคลื่อนไหวเกิดแล้ว” ส่วนคู่มือบอกว่าจะทำอะไรต่อ
S6 · ช่วงเวลาเทรดตามแผนบนกราฟ
Pine Script v6 · อินดิเคเตอร์วาดส่วน 3 ของแผน (ส่วนเสริม E) บนกราฟ: เน้นช่วงที่อนุญาตให้เปิดสถานะและเตือนเมื่อช่วงปิด กรอกเวลาตามเขต Europe/Warsaw ตัวระบุ IANA จัดการ CET/CEST ให้ ค่าเริ่มต้นใช้วันจันทร์ถึงศุกร์ มีตัวเลือกขยายเป็นทุกวัน แถบสีรับรองเพียงเวลาที่แผนกำหนด ไม่ใช่เงื่อนไขเข้าอื่น ใช้แท่งระหว่างวันมาตรฐานที่แบ่งตามเวลา
//@version=6
indicator("ช่วงเวลาเทรดตามแผน", overlay=true)
if not timeframe.isintraday
runtime.error("ใช้กราฟระหว่างวันที่แบ่งตามเวลาและต่ำกว่า 1D")
tz = input.string("Europe/Warsaw", "เขตเวลาของช่วง")
win1 = input.session("0900-1200", "ช่วง 1")
win2 = input.session("1400-1700", "ช่วง 2")
weekdaysOnly = input.bool(true, "เฉพาะวันจันทร์ถึงศุกร์")
days = weekdaysOnly ? ":23456" : ":1234567"
inWin = not na(time(timeframe.period, win1 + days, tz)) or not na(time(timeframe.period, win2 + days, tz))
bgcolor(inWin ? color.new(color.teal, 88) : na, title="อยู่ในช่วงตามแผน")
windowJustClosed = inWin[1] and not inWin
alertcondition(windowJustClosed,
title="ช่วงเวลาตามแผนปิดแล้ว",
message="ช่วงเวลาเทรดของคุณเพิ่งปิด — จัดการเฉพาะสถานะที่เปิด ห้ามเข้าใหม่ (แผน ส่วน 3)")
ผลที่ได้: สีพื้นหลังในช่วงเวลาเทรดและเงื่อนไขแจ้งเตือนเมื่อช่วงปิด (“ช่วงเวลาตามแผนปิดแล้ว” ให้สร้างในแพลตฟอร์มหลังเพิ่มอินดิเคเตอร์) ตรวจแท่งแรกนอกช่วงที่ตามหลังแท่งในช่วง จึงไม่รับประกันว่าจะเตือนตรงเวลาปิดพอดี หากไม่มีแท่งใหม่และข้อมูลอัปเดต จะไม่ตรวจ ใช้กรอบเวลาที่เวลาเปิดปิดแท่งตรงขอบช่วง เช่น บน M15 ช่วง 09:00–12:00 และมีแท่ง 12:00 สีจบที่ 12:00 และเงื่อนไขทำงานบนแท่งแรกนอกช่วงนั้น “Once Per Bar” อาจเตือนในการอัปเดตแรก ส่วน “Once Per Bar Close” เตือนเมื่อปิดแท่ง ช่วงที่ซ้อนกันรวมเป็นช่วงเดียว จุดจบของช่วงแรกไม่ได้ปิดช่วงที่สอง สร้างการแจ้งเตือนใหม่หลังแก้เวลา สิ่งที่ไม่ควรคาดหวังคือการบล็อกคำสั่ง อินดิเคเตอร์เตือนให้ทำตามแผน ส่วนรายการตรวจสอบก่อนเทรดเป็นตัวบังคับใช้
โค้ดหกชุดพร้อมหัวข้อในไฟล์ข้อความเดียว คำแนะนำเตรียมระบบ และเวอร์ชันไลบรารี ตามด้วย CSV ทดสอบและผลที่ควรได้ เพื่อเก็บกับบันทึกหรือในคลังโค้ดของคุณ TXT รวมหลายภาษาโปรแกรม อย่ารันทั้งไฟล์เป็น Python เลือก S1+S2+S3, S4 ที่ทำงานเดี่ยว หรืออินดิเคเตอร์ Pine ตัวเดียว
คำถามที่พบบ่อย
ทำไมตัวระบุและ API เป็นภาษาอังกฤษ แต่ความคิดเห็นและป้ายผลลัพธ์เป็นภาษาไทย?
ตัวระบุและ API ใช้ภาษาอังกฤษเพื่อให้ตรงเอกสาร pandas, Pine Script และข้อความผิดพลาด ส่วนความคิดเห็นกับป้ายผลลัพธ์ที่ผู้อ่านใช้แปลเป็นภาษาไทย คำอธิบายรอบโค้ดบอกว่าโค้ดทำอะไรและใช้เมื่อไร ส่วนความคิดเห็นภายในอธิบายวิธีทำ เวิร์กช็อป AI ส่วนเสริม H, P3 ช่วยทบทวนโค้ด บริบทของต้นฉบับภาษาโปแลนด์: ชุดไลบรารีนั้นใช้ความคิดเห็นภาษาอังกฤษตามธรรมเนียมร่วมกับ API ขณะที่พรอมป์ต์ P5 ฉบับโปแลนด์ยอมให้โค้ดที่สร้างเฉพาะงานมีความคิดเห็นภาษาโปแลนด์ ข้อยกเว้นนั้นอ้างถึงโค้ดครั้งเดียว ไม่ใช่ไลบรารี
รันโค้ด Python อย่างไร?
11.8 พาคุณติดตั้ง Python สภาพแวดล้อม และแพ็กเกจทีละขั้น สำหรับชุดนี้ใช้เพียง pandas และ matplotlib รวมโค้ด S1–S3 ในไฟล์เดียว analiza_dziennika.py (S1 อยู่บนสุด) แล้วรัน python analiza_dziennika.py ในไดเรกทอรีที่มีไฟล์ส่งออกบันทึก S4 ทำงานแยกต่างหาก เส้นทางสัมพัทธ์ใน CSV_PATH อ้างถึงไดเรกทอรีทำงานของเทอร์มินัล เริ่มด้วย CSV ทดสอบ และวิเคราะห์สำเนาบันทึกจริง เพราะ equity_curve.png ถูกเขียนทับเมื่อรันครั้งถัดไป หากเทอร์มินัลคืนข้อผิดพลาดที่ไม่เข้าใจ ให้วางโค้ดกับข้อความเต็มในการทบทวน P3 จาก ส่วนเสริม Hแทนการเดา
แก้ไขและขยายโค้ดเหล่านี้ได้ไหม?
นั่นคือจุดประสงค์ของชุดเริ่มต้นนี้ ตัวอย่างการขยายคือแยกตัวชี้วัดตามรูปแบบ (จัดกลุ่มคอลัมน์ setup ก่อนเรียกฟังก์ชัน S2) กรองด้วย error_code เพื่อคำนวณค่าคาดหวังเมื่อดำเนินการไม่ผิดพลาด หรือแยกเส้น A+/B มีกฎสองข้อ: ทุกการเปลี่ยนผ่านการทบทวน P3 (โดยเฉพาะ request.security ใน Pine) และกำหนดเวอร์ชันทั้งนิยามตัวชี้วัดกับกฎกลยุทธ์ แก้ข้อผิดพลาดคำนวณทันทีและคำนวณช่วงที่นำมาเทียบใหม่ด้วยวิธีเดียวกัน บันทึกการเปลี่ยนกฎกลยุทธ์ใน ระเบียบการทดสอบ (ส่วนเสริม N) แล้วตรวจด้วยข้อมูลใหม่ การสิ้นไตรมาสเพียงอย่างเดียวไม่ใช่เหตุผลให้เปลี่ยน และการอยู่กลางไตรมาสก็ไม่ใช่เหตุผลให้เก็บข้อผิดพลาดไว้
ทำไมชุดนี้ไม่มีกลยุทธ์หรือ EA?
ด้วยเหตุผลเดียวกับที่เวิร์กช็อป AI ไม่สร้างสัญญาณ: กลยุทธ์สำเร็จรูปจากอินเทอร์เน็ตมีความได้เปรียบและสมมติฐานที่ไม่รู้ และไม่ผูกกับแผนของคุณ ส่วน EA ที่ใช้ตรรกะคนอื่นในบัญชีคือมอบการตัดสินใจที่คุณจ่ายต้นทุนคนเดียว โค้ดในหลักสูตรเสริมกระบวนการ วัดผล แสดงภาพ และทำตามแผน ไม่ได้มาแทน หากต้องการทำกลยุทธ์ของคุณที่ทดสอบแล้วให้เป็นอัตโนมัติ เส้นทางต้องผ่าน การทดสอบย้อนหลังใน 15.4ระเบียบการทดสอบในส่วนเสริม N และการทบทวนโค้ด P3 ไม่ใช่คัดลอกสคริปต์ “ทำกำไร”
math.floor ใน S4 แทน round: เครื่องปัดลงโดยไม่เถียง คนปัดขึ้นพร้อมหาเหตุผลรองรับเนื้อหาหลักสูตรที่เกี่ยวข้อง
- 11.8 Python ในการเทรด — เตรียมสภาพแวดล้อม สคริปต์แรก และการใช้แพ็กเกจ
- ส่วนเสริม F — สมุดงานบันทึกการเทรด — รูปแบบ CSV ที่ S1–S3 อ่าน และตัวชี้วัดเดียวกันในสูตรสมุดงาน
- ส่วนเสริม H, พรอมป์ต์ P3 — ทบทวนทุกการแก้โค้ดนี้ (lookahead, repainting และข้อผิดพลาดตรรกะ)
- หมวดเครื่องมือ — เครื่องคำนวณ — มูลค่า pip และขนาดสถานะในเบราว์เซอร์ สำหรับตรวจผล S4
- ส่วนเสริม E — แม่แบบแผน — ช่วงเวลาเทรดและขีดจำกัดที่ S6 แสดงบนกราฟ
- 15.10 ตรวจทบทวนกลยุทธ์ — ตีความตัวเลขที่ชุดนี้สร้างอย่างมีวินัย
แหล่งอ้างอิง
- เอกสาร: pandas 3.0.5 และ matplotlib 3.11.1 (ฟังก์ชันใน S1–S3); Python: การอ่าน CSV และ เลขคณิตเศษส่วน (ตรวจรูปแบบและคำนวณจำนวนขั้นล็อตอย่างแม่นตรง) โค้ด S1–S4 รันกับข้อมูลสังเคราะห์เมื่อ 17 กันยายน 2026 ด้วย Python 3.12.10; TradingView Pine Script v6: กรอบเวลาอื่นและข้อมูล, ช่วงเวลาเทรด, เวลาและเขตเวลา, การแจ้งเตือน (request.security พร้อมพารามิเตอร์ lookahead และ repainting, hline, input.session, alertcondition) ตรวจ S5–S6 กับเอกสารแล้วโดยยังไม่คอมไพล์
- ภายใน: 11.8 Python ในการเทรด (สภาพแวดล้อม), 15.9 (นิยามตัวชี้วัดและรูปแบบบันทึก), 15.10 (หลักการสรุปผล) ชุดนี้ย่อเนื้อหาเหล่านั้นเป็นการใช้งานจริง
- Van K. Tharp, Trade Your Way to Financial Freedom, ฉบับที่ 2, McGraw-Hill 2007 — จำนวนเท่าของ R และค่าคาดหวัง ภาษาร่วมของ S2 สมุดงาน F และพรอมป์ต์ P5