حزمة شفرة المتداول — Python وPine Script

ستة مقاطع للنسخ: أربعة بلغة Python لتحميل دفتر CSV والمؤشرات ومنحنى رأس المال مع التراجع وحساب حجم المركز بـATR، واثنان بلغة Pine Script للنطاق اليومي نسبةً إلىATR مع تنبيه ولنوافذ جلسات الخطة على الرسم. كلها أدوات تحليل؛ تحسب وترسم وتذكّرك، ولا يولّد أي مقطع إشارات شراء/بيع أو يتخذ قرارات، فذلك عمل دليل الأنماط. تبقى المعرّفات وأسماء واجهات API بالإنجليزية بما يتسق مع توثيق pandas وPine Script ورسائل الخطأ؛ أما التعليقات وتسميات المخرجات الموجّهة للقارئ فتُقدّم بالعربية في هذه النسخة. وتحافظ ورشة الذكاء الاصطناعي على اصطلاح الأسماء التقنية في موجّهات الشفرة؛ والشروح المحيطة بلغة الدورة وتقودك خطوة بخطوة. البيئة: تثبيت Python والتشغيل الأول مشروحان في 11.8؛ وعناوين أعمدة الدفتر في الملحق F.

محطة عمل متداول ومحلل: محرر شفرة ببرامج Python لتحليل الدفتر، ورسم بمؤشرات Pine Script على الشاشة
أهم النقاط في60 ثانية
  • أدوات، وليست استراتيجيات: تحسب الشفرة المؤشرات من بياناتك وتتابع خطتك؛ والحد الفاصل هو نفسه في قسم «ما لا يفعله الذكاء الاصطناعي» من الملحق H وللأسباب نفسها
  • تنسيق بيانات مشترك: تقرأ مقاطع Python عناوين جدول الدفتر، الملحق F؛ ويشمل مقام معدل الفوز صفقات التعادل، وتستخدم القيمة المتوقعة النتيجة الصافية. هي التعريفات نفسها في الموجّه P5
  • Pine Script دون استشراف بيانات مستقبلية: يجلب S5 بيانات يومية باستخدام lookahead=barmerge.lookahead_off؛ ولا يصل S6 إلى إطار أعلى أصلًا، ويستخدم الدالة time فقط. غياب الاستشراف لا يثبت غياب إعادة الرسم؛ ويشرح وصف 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.py. يحتاج S4 المكتبة القياسية فقط. الصق S5 وS6 في Pine Editor كلًا على حدة. جرى فحص مقطعي Pine مقابل توثيق v6، ولم تُجرَ ترجمتهما برمجيًا أو اختبار تنبيهاتهما في TradingView.

مسار التحليل: يدخل تصدير دفتر CSV إلى برامج Python، وتخرج المؤشرات ورسم منحنى رأس المال، وتغذي الاستنتاجات المراجعة الأسبوعية ودليل الأنماط دفتر CSVتنسيق الملحق F Python: S1–S3المؤشرات · المنحنى · التراجع مراجعة نهاية الأسبوع EOWالعينة والاعتماد وعدم اليقين دليل أنماط التداولإصدارات القواعد واختباراتها لا سهم «الشفرة → الأمر» في هذا المسار؛ وليس ذلك سهوًا

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 بالإعدادات المحلية البولندية، و"," للإنجليزية
DECIMAL  = ","              # "," لملف CSV بالإعدادات المحلية البولندية، و"." للإنجليزية
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"])   # الاحتفاظ بالفهرس = أرقام السجلات المستخدمة في الرسالة أدناه

# يتطلب منحنى رأس المال والتراجع ترتيبًا زمنيًا (الملحق 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، جاهز للمقاطع التالية ومرتب بوقت الإغلاق، وبهذا الترتيب يُحسب المنحنى والتراجع، مع عدد الصفقات المفتوحة المتجاوزة. الصف الفارغ سجل فارغ كليًا، كما في تصدير ورقة XLSX كاملة، ولا يعده البرنامج؛ والصف الذي يحوي بيانات بلا نتيجة أو وقت إغلاق يُتجاوز كصفقة لم تغلق بعد؛ أما السجل المغلق بلا نتيجة فيوقف البرنامج. إذا توقف بقائمة سجلات، فأصلح الصفوف المحددة في الدفتر لا البرنامج؛ وإذا تأثر العمود كله، فافحص أيضًا DECIMAL الثابت. يبدأ ترقيم السجلات بعد العنوان ويشمل السجلات الفارغة؛ وقد يحوي الحقل المقتبس فاصلًا أو سطرًا جديدًا ويبقى سجلًا واحدًا. يجب أن تظهر كل صفقة مرة؛ افحص معرّفات trade_id قبل دمج الصادرات، فلا يزيل S1 تكرارات السجل. لا يكفي تاريخ بلا وقت لإعادة بناء التسلسل. يُعامل وقت بلا علامة منطقة زمنية كـUTC؛ ويُحوّل فرق ISO 8601 الصريح إلىUTC. عند تساوي الأوقات يحفظ البرنامج ترتيب السجلات؛ حدّده في الدفتر لأنه يؤثر في مسار التراجع.

S2 · مؤشرات الدفتر

Python 3 · pandas

التعريفات نفسها في الموجّه P5 ولوحة الجدول؛ يشمل مقام معدل الفوز صفقات التعادل؛ ويعيد معامل الربح رسالة بدل القسمة على الصفر؛ والقيمة المتوقعة متوسط صافي R (r_net؛ ويُقاس التراجع من المنحنى التراكمي بدءًا من0 R، فالخسارة الأولى تراجع بالفعل، كما في صيغة الجدول F. شغّله بعد S1. تعريفات المؤشرات مشتركة، لكن سياسة الأخطاء تختلف عن P5: يوقف S1 التحليل إذا كانت نتيجة صفقة مغلقة غير صالحة، بينما يوجّه P5 لتجاوز الصف وعدّه. لا تقارن النتائج قبل تحديد الصفقات التي دخلت العينتين.

import math

def metrics(r):
    "المؤشرات الأساسية لليوميات على سلسلة من مضاعفات R الصافية.\n    التعريفات (كما في لوحة الملحق F):\n    - نسبة الربح: الصفقات ذات r > 0 مقسومة على جميع الصفقات الصالحة (تبقى الصفقات المتعادلة في المقام)\n    - معامل الربح: مجموع r الموجبة مقسومًا على abs لمجموع r السالبة\n    - القيمة المتوقعة: المتوسط الحسابي لقيم r (بعد التكاليف)\n    - أقصى تراجع: أكبر هبوط لمنحنى R التراكمي عن قمته المتحركة،\n      ويبدأ المنحنى عند 0 R (لذلك فإن خسارة أولى بمقدار -1 R تمثل بالفعل تراجعًا بمقدار 1 R)"
    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 أو تراجع يبدو صفريًا. WR = 0.4 يعني40%. يُحسب PF بوحدات R، لاPLN؛ ومع تغير المخاطرة النقدية قد تختلف النتائج. التكاليف مشمولة بالفعل في r_net ؛ فلا يطرحها S2 مجددًا. لا تتوقع تقييم أفضليتك من المتوسط وحده أو مجرد تجاوز20 أو50 صفقة. تعريفات اللوحة في الملحق F، وخطة اختيار العينة والتكاليف والتحقق في الملحق N.

S3 · منحنى رأس المال والتراجع

Python 3 · pandas + matplotlib

يرسم منحنى R التراكمي والتراجع من القمة أسفله، ويحفظ 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 التي يُقاس منها التراجع
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("التراجع (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، ومنه يُقاس التراجع. تُظهر اللوحة السفلى تراجع R التراكمية من قمتها، بدءًا من0 R كما في S2 والجدول F، عبر التاريخ كله؛ وليس خسارة أسبوع. افحص حد الخطة الأسبوعي، الملحق E، منفصلًا أسبوعًا بأسبوع بالوحدة وقاعدة إعادة الضبط المحددتين في الخطة.

ملف الاختبار والمخرجات المتوقعة S1 +S2 +S3 CSV · التنسيق PL

احفظ باسم dziennik.csv بترميز UTF-8 بجانب البرنامج المدمج S1+S2+S3 وشغّله. يتضمن الملف الأعمدة التي يحتاجها S1 فقط؛ ويُحمّل تصدير الملحق F الكامل،26 عمودًا، بالطريقة نفسها. الصف الأخير صفقة مفتوحة ذات r_net فارغ؛ والصفقتان المغلقتان الأوليان خاسرتان، لذا يُقاس التراجع من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 → تراجع2.08 R. دون نقطة0 R كان سيصبح1.08 R؛ وهذا الفرق وراء تعريف الملحق F. تخص المخرجات هذا الملف بالضبط والإصدارات المذكورة؛ وإذا اختلفت، فافحص البيانات، أي الفاصل والرمز العشري والترميز وترتيب الصفوف، ثم ثوابت S1، فالشفرة، فإصدارات الحزم، بهذا الترتيب.

S4 · حساب حجم المركز بـATR

Python 3 · المكتبة القياسية

الحجم باللوت لمسافة وقف k × ATR: يأتي مبلغ المخاطرة من رأس المال والنسبة، والمسافة منATR، ويُقرّب الحجم إلى أسفل وفق خطوة اللوت لحسابك، بالمعلمتين lot_step و min_lot وقيمتهما الافتراضية0.01، كي لا تتجاوز المخاطرة المخططة الميزانية. قد تزيد الخسارة الفعلية؛ يستبعد الحساب الانزلاق والفجوات عبر الوقف والفارق والعمولة والمبادلة. يجب أن يكون رأس المال وقيمة النقطة بعملة الحساب نفسها؛ وافحص منفصلًا الحد الأقصى للحجم ومتطلبات الهامش بالمواصفات. أدخل ATR بالنقاط؛ EURUSD 0.0080 → 80 وUSDJPY 0.80 → 80. يشرح حجم النقطة ملف الزوج، الملحق B؛ وقيمة النقطة لكل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 بالنقاط PIPS، وليس بوحدات السعر (EURUSD ATR 0.0080 -> 80؛ USDJPY ATR 0.80 -> 80 — حجم النقطة للزوج).\n    pip_value_per_lot: قيمة 1 نقطة لكل 1.00 لوت، بعملة الحساب\n    (مثلًا ~10 USD لزوج EURUSD في حساب USD — تحقّق من مواصفات الأداة).\n    lot_step / min_lot: خطوة الحجم والحد الأدنى لحسابك (0.01 شائع وليس عامًا).\n    يُقرّب إلى الأسفل إلى مضاعفات lot_step، مقاسةً من الصفر. حساب الكسور Fraction\n    يحافظ على المدخلات العشرية دون الاعتماد على دقة Decimal العامة.\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  # أعداد عشرية عائمة للعرض؛ دون تقريب إضافي للوقف أو القيم النقدية


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 نقطة ×1.25 يعطي0.325 نقطة، لا0.3. تُعاد المخرجات كقيم float للقراءة، بينما يُحسب عدد خطوات اللوت بكسور دقيقة؛ ولا يزال الحد الأدنى لتحرك سعر الأداة محتاجًا إلى فحص منفصل. إذا عدّلت الوقف لذلك الحد، فأعد حساب الحجم. لا تتوقع قرار الدخول؛ تحسب الأداة «كم»، ولا تحسم «هل أدخل» أبدًا.

S5 · النطاق اليومي مقابل ATR مع تنبيه

Pine Script v6 · مؤشر

أداة وعي بالتقلب، كمؤشر داخل اليوم: تعرض على الأعمدة الحية النسبة التي «استهلكها» السوق من ATR اليومي، وتنبه عند عبور عتبة؛ وهي لحظة للرجوع إلى الخطة، لا للمطاردة. تُجلب البيانات اليومية دون استشراف أو قيم مستقبلية؛ وعلى الأعمدة التاريخية يحتفظ request.security بآخر قيمة يومية مؤكدة حتى نهاية فترة D التالية، وتظهر نتيجة تلك الفترة الجديدة على عمودها الأخير داخل اليوم. في الوقت الفعلي يجلب نطاق شمعة D الحالية وATR قيد التكوّن، لذلك قد تتغير قراءات سابقة بعد إعادة التحميل، أي إعادة الرسم. اليوم هو شمعة 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 قيد التكوّن؛ قد يُعاد رسم القراءات الآنية السابقة بعد إعادة التحميل.
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” عند إغلاقها فقط. تشكل النوافذ المتداخلة اتحادًا؛ فلا تُغلق الثانية بنهاية الأولى. أعد إنشاء التنبيه بعد تغيير المواعيد. لا تتوقع منع الأوامر؛ يذكّرك المؤشر بالخطة، وتفرضها قائمة ما قبل الصفقة.

مؤشرا Pine Script v6 على رسم EUR/USD في TradingView: نوافذ جلسات الخطة المظللة S6 ولوحة النطاق اليومي مقابل ATR أسفل الرسم بمستوى تنبيه80% فيS5
مؤشرا Pine Script v6 على رسم EUR/USD في TradingView: نوافذ جلسات الخطة المظللة S6 ولوحة النطاق اليومي مقابل ATR أسفل الرسم بمستوى تنبيه80% فيS5
الحزمة كلها في ملف واحد تُنشأ محليًا في متصفحك

ستة مقاطع بعناوين في ملف نصي واحد، وتعليمات الإعداد وإصدارات المكتبات، ثم CSV الاختبار والمخرجات المتوقعة؛ لأرشفتها بجانب الدفتر أو حفظها في مستودعك. يجمع TXT لغات مختلفة؛ لا تشغّل الملف كله كـPython. اختر S1+S2+S3 أوS4 المستقل أو مؤشر Pine واحدًا.

المخرجات الفعلية لـS3 لخمس صفقات اختبار: محرر ببرنامج Python ومؤشرات الطرفية، بجانب منحنى رأس المال والتراجع الناتجين
المخرجات الفعلية لـS3 لخمس صفقات اختبار: محرر ببرنامج Python ومؤشرات الطرفية، بجانب منحنى رأس المال والتراجع الناتجين

الأسئلة الشائعة

لماذا تبقى الأسماء التقنية بالإنجليزية، وكيف تُقدّم التعليقات في النسخة العربية من الدورة ذات الأصل البولندي؟

اصطلاح هذه الحزمة إبقاء المعرّفات وأسماء API بالإنجليزية لتتوافق مع توثيق pandas وPine Script ورسائل الخطأ الإنجليزية؛ وتتبع ورشة الذكاء الاصطناعي الاصطلاح نفسه للأسماء في موجّهات الشفرة، كما في الملحق 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 واختبره على بيانات جديدة. لا تبرر نهاية الربع وحدها التغيير، ولا يبرر كونك في منتصفه إبقاء خطأ.

لماذا لا تتضمن الحزمة استراتيجيات أو مستشارين آليين EAs؟

للسبب نفسه الذي يمنع ورشة الذكاء الاصطناعي من توليد إشارات: الاستراتيجية الجاهزة من الإنترنت مجهولة الأفضلية والافتراضات ولا صلة لها بخطتك؛ وEA ينفّذ منطق غيرك في حسابك تفويض لقرارات تدفع تكلفتها وحدك. تقوّي شفرة الدورة العملية بالقياس والتصوير والالتزام بالخطة، بدل استبدالها. إذا أردت أتمتة استراتيجيتك المختبرة، يمر الطريق عبر الاختبار التاريخي في15.4 وبروتوكول الملحق N ومراجعة الشفرة P3، لا بنسخ برامج «رابحة».

سيناريو توضيحي: التقريب «لرقم أجمل». حسبت الأداة0.07 لوت؛ ونقر المتداول0.1 قائلًا «فرق شكلي ورقم مستدير». هذا التعديل الشكلي بالأرقام: +43% مخاطرة في كل صفقة، منهجيًا، طوال الربع؛ وصار الحد اليومي−2R فعليًا−2.9R، وتجاوز تراجع منحنى رأس المال الخطة، مع أنه عدّ كل تعديل شكليًا. لهذا تُستخدم math.floor في S4 بدل round؛ تقرّب الآلة إلى أسفل بلا جدال، ويقرّب الإنسان إلى أعلى مع تبرير.
سيناريو توضيحي: تكتل خسائر لا يظهر إلا في المنحنى. بدت مؤشرات الربع معقولة؛ قيمة متوقعة موجبة وتراجع ضمن النطاق المتوقع. لم يكشف أن التراجع كله من أسبوع واحد إلا رسم S3؛ وحددت تصفية وقت الدخول المضافة إلىS2 الأمر بدقة: كل خسارة بين21:00 و23:00 UTC، إذ يسجل دفتر الملحق F بتوقيت UTC، مع فوارق واسعة «لأنني سأبقى قليلًا». يعتمد توافق تلك الساعات مع التبييت على الوسيط والمنطقة الزمنية؛ فافحص المواصفات بدل الافتراض. يطمس الرقم الإجمالي الفروق بالمتوسط؛ ويُظهر المنحنى متى ينزف النظام. سطر في الخطة يمنع الدخول بعد21:00 UTC، أي23:00 CEST /22:00 CET، مع تدوينه بتوقيت Europe/Warsaw في الخطة ونوافذ S6، استنتاج يُختبر في الأسابيع التالية بهذا السيناريو، لا تحسن جاهز في النتائج؛ وهذا بالضبط نوع الاستنتاج الذي تدعمه الحزمة.
تنويه: هذه الصفحة تعليمية ولا تشكل نصيحة استثمارية. المقاطع أدوات تحليل تشغّلها على مسؤوليتك؛ اقرأ الشفرة قبل الاستخدام وتحقّق من النتائج، مثلًا بحاسبة الملحق C، واتخذ قرارات التداول وفق خطتك. لا تتصل مقاطع Python، أي S1–S4، بالإنترنت أو حسابك؛ تقرأ ملفات محلية وترسم مخططات. وتعمل مؤشرات Pine، أي S5–S6، على منصة رسمك ببياناتها. لا يولّد أي مقطع إشارات شراء/بيع أو يضع أوامر.

المصادر

  1. الوثائق: pandas 3.0.5 و matplotlib 3.11.1 للدوال المستخدمة فيS1–S3؛ Python: قراءة CSV و حساب الكسور Fraction للتحقق من التنسيق وحساب عدد خطوات اللوت بدقة؛ شُغّلت المقاطع S1–S4 على بيانات اصطناعية في17 سبتمبر2026 باستخدام Python 3.12.10؛ وTradingView Pine Script v6: الأطر الزمنية والبيانات الأخرى, الجلسات, الوقت والمناطق الزمنية, التنبيهات request.security مع معلمة lookahead وإعادة الرسم وhline وinput.session وalertcondition؛ روجعت شفرة S5–S6 مقابل الوثائق دون ترجمة برمجية.
  2. من الدورة: 11.8 Python في التداول البيئة، 15.9 تعريفات المؤشرات وتنسيق الدفتر، 15.10 مبادئ استخلاص الاستنتاجات؛ والحزمة تطبيقها العملي المختصر.
  3. Van K. Tharp، Trade Your Way to Financial Freedom، الطبعة2، McGraw-Hill 2007؛ مضاعفات R والقيمة المتوقعة، اللغة المشتركة بين S2 والجدول F والموجّه P5.

Jarosław Wasiński

رئيس التحرير، ForexMechanics.com • محلل اقتصاد كلي وعملات

محلل مستقل ومحترف يتمتع بأكثر من 20 عاما من الخبرة في الأسواق المالية. نشط في سوق الفوركس منذ 2004، مع تركيز على التحليل الأساسي، هيكلية سوق OTC، والإدارة المنضبطة لمخاطر رأس المال.

  • مؤسس ورئيس تحرير MyBank.pl، يقدم تعليما ماليا موثوقا منذ 2004.
  • مؤلف مئات التحليلات المعمّقة للأسواق، والدراسات الهيكلية، والمواد التعليمية للمتداولين والمستثمرين.
  • مناصر لشفافية السوق، يروّج لتعليم مبني على بيانات ملموسة وتقارير مؤسسية (BIS، ESMA، CFTC).

جميع المحتويات ذات طابع تعليمي وإعلامي حصرا – لا تشكل نصيحة استثمارية أو توصية من أي نوع. تذكّر: التداول بالرافعة المالية (فوركس/عقود الفروقات) ينطوي على مخاطر عالية لخسارة رأس المال بسرعة.