الوثائق1.0 (latest)
الصفحاتنموذج الحساب · 9 / 29

نموذج الحساب

تعمل كل استراتيجية في QuantCraft على حساب تداول ورقي مدمج. يتتبّع الحساب نقدك، ويحتفظ بمراكزك المفتوحة، ويسجّل الربح والخسارة المحقّقين عند إغلاق الصفقات، ويحسب مقاييس الأداء في نهاية الاختبار الرجعي.

لا تنشئه بنفسك في الاستخدام المعتاد — إذ ينشئه محرك الاختبار الرجعي من علامة تبويب Simulation في نافذة Run backtest (الرصيد الابتدائي والعمولة / الانزلاق) ويسنده إلى quantcraft.process.runtime.account قبل تشغيل on_init(). ويمكن ربط SL/TP افتراضي اختياري وتحديد حجم قائم على المخاطر عبر الإعدادات / مفاتيح .qcs عند وجودها؛ ولا تعرض النافذة حاليًا مدخلات SL/TP / المخاطر. تتلقى دوال الاستدعاء بيانات السوق كما هو موضح في دورة حياة الاستراتيجية وبيانات OHLCV والأعمدة؛ وتظهر المقاييس في نتائج الاختبار.


بداية سريعة

import quantcraft.process.runtime as rt def on_init(): pass def on_bar(bar_index, bar, fundamentals=None, symbol=None): account = rt.account px = float(bar["close"]) snap = account.open_trade("AAPL", "long", 10, px) if snap["open_positions"]: pid = snap["open_positions"][0]["id"] account.close_trade(pid, px + 1.0) def on_finish(bars, symbol=None, bars_by_symbol=None): last_px = float(bars[-1]["close"]) metrics = rt.account.refresh_metrics({"AAPL": last_px})

هذا هو النمط الكامل: افتح صفقة، واحتفظ بمعرّف المركز، وأغلقها لاحقًا، و(اختياريًا) حدّث المقاييس في النهاية.

ما تقرؤه من الكائن: account.cash وaccount.balance وaccount.realized_pnl وaccount.closed_trades وaccount.open_positions_payload(). لا توجد خاصية account.open_positions — إذ تأتي الأرجل المفتوحة من اللقطة (snap["open_positions"]) أو من open_positions_payload().

عمليات التشغيل الأمامية تضع أوامر حقيقية. في اختبار أمامي على الرسم البياني أو في تشغيل جماعي، يكون runtime.account حساب وسيط حقيقيًا — فئة فرعية من PaperAccount بالواجهة نفسها ترسل أوامر حقيقية إلى حساب الوسيط المتصل لديك (ورقي أو حقيقي)، بما في ذلك أوامر bracket الخاصة بـ SL/TP للأسهم. تعامل مع open_trade / close_trade هناك على أنهما صفقتان حقيقيتان. ينطوي التداول على مخاطر الخسارة.


ما الذي يفعله PaperAccount

يحاكي الحساب حساب تداول نقديًا بسيطًا:

  • يتتبّع الرصيد الابتدائي والنقد والرصيد وحقوق الملكية (equity).
  • يحتفظ بمراكز شراء وبيع على المكشوف في الحساب نفسه.
  • يسجّل الربح/الخسارة المحقّق عند إغلاق صفقة ويتتبّع الربح/الخسارة غير المحقّق للمراكز المفتوحة.
  • يحتفظ بـ سجل لحقوق الملكية يُضاف إليه عند كل تحديث للسعر.
  • يحسب اختياريًا مجموعة كاملة من مقاييس الأداء (شارب وسورتينو والتراجع الأقصى ومعدل النمو السنوي المركّب وغيرها).

ما لا يحاكيه: الهامش أو تكلفة الاقتراض أو التنفيذ التلقائي لـ SL/TP. قيم SL/TP المخزّنة مجرد أرقام — شيفرة استراتيجيتك هي المسؤولة عن فحص الأسعار واستدعاء close_trade عند بلوغ الإيقاف أو الهدف. تُطبَّق العمولة والانزلاق عند ضبط نماذج في علامة تبويب Simulation — انظر أدناه.

تُحاكى توزيعات الأرباح والتجزئات عند تفعيل بيانات التقويم للتشغيل. في تاريخ سريان التجزئة يُضرب عدد الأسهم في النسبة وتُقسم أساس التكلفة عليها (يبقى منحنى حقوق الملكية متصلًا). وفي تاريخ الاستحقاق (ex-date) يُضاف qty × amount إلى النقد في المراكز الشرائية ويُخصم في البيعية. ويتدفق التوزيع إلى balance وحقوق الملكية لكن ليس إلى realized_pnl — اقرأ total_dividend_income وtotal_splits_applied من account.refresh_metrics(). راجع بيانات التقويم.

تُضبط العمولة والانزلاق في علامة تبويب Simulation في نافذة Run backtest. وعند تفعيلهما تُعدَّل عمليات التنفيذ تلقائيًا؛ ولا تحدّدهما في Python. تستخدم نافذة Run افتراضيًا عمولة لكل سهم بقيمة 0.001 $ لكل سهم وحدًّا أدنى 0 $، وانزلاقًا بنمط حصة الحجم. (أما PaperAccount الذي تنشئه بنفسك فلا يملك افتراضيًا أي عمولة وأي انزلاق.)


ضبط الحساب

تضبط الحساب في علامة تبويب Simulation في Run backtest:

الإعدادما يفعله
starting_balanceالنقد الذي يبدأ به الحساب.
نماذج العمولة / الانزلاقكيف تُعدَّل عمليات التنفيذ — انظر أدناه.

لا يزال بالإمكان تطبيق SL/TP افتراضي اختياري وتحديد الحجم بالمخاطر (sl_pct / sl_dollars وtp_pct / tp_dollars وrisk_pct_per_trade / risk_fixed_per_trade) من مساعدات .qcs / الإعدادات عند وجودها، لكن نافذة Run backtest لا تعرض هذه المدخلات حاليًا. عند ضبطها:

  • تقبل حقول النسبة المئوية إما قيمًا مئوية كاملة مثل 2 أو كسورًا مثل 0.02 — وكلاهما يعني الشيء نفسه.
  • في المراكز الشرائية يقع الإيقاف أسفل الدخول والهدف أعلاه. وتعكس المراكز البيعية ذلك.
  • تنطبق القيم الافتراضية على كل صفقة جديدة ما لم تتجاوزها في كل استدعاء.

لـ الدفاتر (notebooks) أو الاختبارات فقط (خارج مسار الاختبار الرجعي في بيئة التطوير)، يمكنك إنشاء حساب يدويًا:

account = PaperAccount( 100_000, sl_pct=2, tp_pct=4, risk_pct_per_trade=1, slippage_model=None, # optional; defaults to no slippage (IDE engine wires from Simulation tab) commission_model=None, # optional; defaults to zero commission (IDE engine wires from Simulation tab) equity_history_cap=None, # optional; keep only the newest N equity_history points )

العمولة والانزلاق (علامة تبويب Simulation)

تضبط علامة تبويب Simulation في نافذة Run backtest كيفية تعديل عمليات التنفيذ بتكاليف التداول. تُطبَّق الإعدادات تلقائيًا على runtime.account — ولا تضبطها في Python. راجع الاختبارات الرجعية وإعدادات التشغيل المسبقة (.qcs) لحفظ هذه الخيارات.

نماذج العمولة

النموذجالمعاملاتالسلوك
لكل سهم (الافتراضي)التكلفة لكل سهم ($)، الحد الأدنى لكل صفقة ($)max(qty × cost, min) عندما يكون الحد الأدنى > 0
لكل صفقةتكلفة ثابتة لكل أمر ($)الرسم نفسه أيًّا كانت الكمية
صفر—دون عمولة — مفيد لاختبار ألفا الصافية

نماذج الانزلاق

النموذجالمعاملاتالسلوك
حصة الحجم (الافتراضي)حد الحجم (الافتراضي 0.025)، أثر السعر (الافتراضي 0.1)يتناسب الأثر مع (qty / bar_volume)²؛ تُنفَّذ المشتريات أعلى والمبيعات أدنى. عند الفتح فقط، يقلّص volume_limit الكمية بصمت إلى volume_limit × bar_volume — تحقّق من اللقطة المُرجَعة لمعرفة الحجم الذي حصلت عليه فعلًا. (إن لم يتبقَّ شيء، يرفع open_trade الخطأ qty after volume_limit cap is not positive.) لا تُقيَّد عمليات الإغلاق أبدًا. حجم عمود صفر / مفقود → لا أثر.
فارق ثابتالفارق الإجمالي لكل سهم ($)يُطبَّق نصف الفارق على كل رِجل تنفيذ
صفر—التنفيذ بالسعر المطلوب بالضبط

تتضمن سجلات الصفقات المغلقة commission و**slippage_cost** و**raw_entry_price** و**raw_exit_price. أما entry_price / exit_price المخزّنان فهما أسعار التنفيذ بعد الانزلاق. ويعرض account.refresh_metrics() كلًّا من total_commission وtotal_slippage_cost**.


المراكز

يمثّل المركز صفقة مفتوحة واحدة في رمز واحد. لا تنشئ المراكز مباشرة — بل ينشئها open_trade(...) ويزيلها close_trade(...)، الذي يمكنه أيضًا إغلاق جزء من مركز وترك الباقي في مكانه.

فتح صفقة

snap = account.open_trade( symbol, # "AAPL" side, # "long" or "short" qty, # integer or float quantity entry_price, # float, the requested price (slippage/commission applied) sl_price=None, # optional stop price (overrides default sl_pct/sl_dollars) tp_price=None, # optional target price (overrides default tp_pct/tp_dollars) position_id=None, )

يُرجع الاستدعاء لقطة (snapshot) للحساب (انظر أدناه). يظهر المركز المفتوح حديثًا في snap["open_positions"] — احصل على id الخاص به إن أردت إغلاقه لاحقًا بالمعرّف. و**entry_price** المخزَّن هو التنفيذ بعد الانزلاق؛ ويُحتفظ بالسعر المطلوب باسم raw_entry_price.

يُرفض فتح مركز شرائي برسالة insufficient cash for long عندما يتجاوز qty × fill_price + commission (سعر التنفيذ بعد الانزلاق) النقد. وتقيّم اللقطة المُرجَعة ذلك الرمز بسعر التنفيذ.

تحديد الحجم بالمخاطرة

إذا أردت أن يحدّد الحساب حجم الصفقة بناءً على ميزانية المخاطرة لديك (أحد risk_pct_per_trade أو risk_fixed_per_trade) ومسافة الإيقاف، فاستخدم:

qty = account.position_size_for_risk(entry_price, stop_price)

يتطلب ذلك ضبط وضع مخاطرة واحد بالضبط وألّا تكون مسافة الإيقاف صفرًا. وافتراضيًا يستخدم risk_pct_per_trade نسبة مئوية من النقد؛ مرّر equity_for_risk=... لتحديد الحجم مقابل حقوق الملكية بدلًا من ذلك.

إغلاق صفقة

account.close_trade(position_id, exit_price) # close the whole position account.close_trade(position_id, exit_price, qty=4) # scale out 4 units, keep the rest

التوقيع الكامل هو close_trade(position_id, exit_price, *, qty=None) — وqty بالكلمة المفتاحية فقط.

يأخذ open_trade / close_trade سعرًا مطلوبًا ثم يطبّقان نموذجي الانزلاق والعمولة الخاصين بالتشغيل. و**exit_price** المخزَّن هو التنفيذ؛ ويُحتفظ بالسعر المطلوب باسم raw_exit_price. ومن الأنماط الشائعة في محاكاة الاختبار الرجعي: طلب تيك الإغلاق الاصطناعي للعمود من on_tick:

def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, calendar=None, symbol=None): if tick_in_bar == 3: account.close_trade(pid, price)

وفي عمليات التشغيل الأمامية، استخدم price المباشر من كل تسعيرة وسيط في on_tick بدلًا من فحص tick_in_bar.

الإغلاق الجزئي (qty=)

تمرير qty يغلق هذا العدد من الوحدات فقط ويترك الباقي مفتوحًا في مكانه — المعرّف نفسه و**entry_price** نفسه و**entry_time** نفسه — فيحتفظ المركز المتبقي بأساس تكلفته ومدة احتفاظه:

pid = account.open_positions_payload()[0]["id"] account.close_trade(pid, px, qty=4) # scale out 4 units, keep the rest

يحمل سجل الإغلاق الجزئي في closed_trades القيمتين partial: True و**remaining_qty**.

  • تُحتسب التكاليف على الوحدات التي جرى تداولها فعلًا، وتُوزَّع عمولة الفتح للدفعة بالتناسب، بحيث يحمّل تسلسل من الإغلاقات الجزئية بالضبط ما يحمّله إغلاق كامل واحد.
  • احذف qty — أو مرّر قيمة تساوي حجم المركز أو تزيد عليه — لإغلاق كامل.
  • يبقى المتبقي مركزًا مفتوحًا عاديًا: يستمر تقييمه في كل خطوة ويظهر في snap["open_positions"] بالمعرّف نفسه، فيمكنك التخفيف منه مجددًا لاحقًا.

ويمكنك أيضًا الإغلاق دفعة واحدة بحسب الرمز. symbol= مطلوب في عمليات التشغيل متعددة الرموز حتى لا تغلق عن غير قصد مراكز كل الرموز دفعة واحدة:

account.close_all_longs(exit_price, symbol="AAPL") account.close_all_shorts(exit_price, symbol="AAPL")

مساعدات أخرى

الدالةالغرض
open_positions_payload(prices=None)الأرجل المفتوحة كقواميس، لكلٍّ منها last_price وunrealized_pnl عند prices (الافتراضي: آخر tick).
open_position_symbols()مجموعة الرموز التي لها رِجل مفتوحة واحدة على الأقل.
position_unrealized_pnl(pos, prices)الربح/الخسارة المفتوح لـ OpenPosition واحد.
apply_split(symbol, ratio, *, when=None) / apply_dividend(symbol, amount_per_share, *, estimated=False, when=None)الإجراءات المؤسسية — يستدعيهما المحرك عند تفعيل بيانات التقويم؛ راجع بيانات التقويم.
corporate_actionsسجل التجزئات / توزيعات الأرباح المطبَّقة حتى الآن.
set_bar_volume(symbol, volume)حجم العمود الذي يستخدمه انزلاق حصة الحجم؛ يضبطه المحرك في كل عمود.

سعر الخروج مقابل المؤشرات: مرّر السعر الذي تستهدفه (مثل float(bar["close"]) من on_bar)؛ ولا يزال المحرك يطبّق الانزلاق/العمولة. ولا يعيد كتابة طلبك إلى SMA أو مؤشر آخر. في الاختبار الرجعي، يكون tick_in_bar == 3 في on_tick تيك إغلاق اصطناعيًا (محاكاة OHLC للعمود، وليست بيانات تيك حقيقية). وفي عمليات التشغيل الأمامية أغلق باستخدام price المباشر من on_tick عند كل تحديث من الوسيط.


النقد والرصيد وحقوق الملكية والربح/الخسارة

هذه هي حقول تتبّع المال الأساسية المتاحة على الحساب وفي كل لقطة:

الحقلالمعنى
starting_balanceالنقد الذي بدأ به الحساب.
cash / balanceالنقد المسوّى بعد عمليات الفتح/الإغلاق. تُخصم في فتح المراكز الشرائية القيمة qty × fill_price + commission؛ وتُضاف في المراكز البيعية القيمة qty × fill_price − commission. أما entry_price / exit_price المخزَّنان فهما أسعار التنفيذ بعد الانزلاق.
equityالإجمالي بالتقييم السوقي: cash + Σ(long qty × mark) − Σ(short qty × mark).
unrealized_pnlمجموع الربح/الخسارة في المراكز المفتوحة، مقيّمة بالعلامات الحالية.
realized_pnlالربح/الخسارة التراكمي من الصفقات المغلقة.

كيف تُحدَّث العلامات:

  • يستدعي المحرك tick(prices, time=...) على الحساب عند كل خطوة سعر محاكاة، فتبقى equity وunrealized_pnl وسجل حقوق الملكية محدّثة تلقائيًا.
  • إذا غاب سعر العلامة لرمز ما من خريطة prices، يُقيَّم ذلك المركز بـسعر دخوله — فيكون الربح/الخسارة غير المحقّق لتلك الرِّجل 0 إلى أن تمرّر تسعيرة حقيقية.
  • يقوم close_trade بـترحيل العلامات السابقة ويضع علامة الرمز المغلق عند تنفيذ خروجه، فتبقى المراكز المتروكة مفتوحة — بما فيها باقي الإغلاق الجزئي — مقيَّمة. ويعيد refresh_metrics() دون وسيط استخدام العلامات من أحدث لقطة (open_trade / close_trade / tick)، ولا يرجع إلى آخر حقوق ملكية / نقد إلا عندما تكون تلك الخريطة فارغة.

داخل on_bar وon_tick لا تحتاج إلى استدعاء account.tick(...) بنفسك — فالمحرك يتولى ذلك. يعمل التيك قبل كل on_tick، لكن on_bar يعمل قبل تيكات تلك الخطوة، ولذا تكون أحدث العلامات داخل on_bar ما تزال إغلاق العمود السابق. ويضيف tick زائد نقطة إضافية إلى equity_history. ويفضَّل تمرير last_prices صريحة إلى refresh_metrics({symbol: last_px}) في on_finish عندما قد يبقى شيء مفتوحًا.

الصفقات البيعية والنقد

يُدعم كلٌّ من الشراء والبيع على المكشوف في الحساب نفسه:

  • فتح مركز بيعي — تؤدي العوائد إلى زيادة النقد (ناقص العمولة عند ضبطها).
  • إغلاق مركز بيعي (شراء للتغطية) — يخفض النقد.

لا يوجد نموذج هامش ولا تكلفة اقتراض، لذا فالمراكز البيعية محاكاة نقدية وتقييمية بحتة (إضافةً إلى العمولة/الانزلاق عند ضبط النماذج).


اللقطات (Snapshots)

يُرجع كل استدعاء لـ open_trade وclose_trade وtick قاموس اللقطة نفسه الذي يصف حالة الحساب بعد الاستدعاء مباشرة. المفاتيح المفيدة:

المفتاحمحتواه
starting_balanceالنقد الابتدائي.
cash / balanceالنقد الحالي.
equityحقوق الملكية الحالية بالتقييم السوقي.
unrealized_pnlالربح/الخسارة في المراكز المفتوحة عند العلامات الحالية.
realized_pnlالربح/الخسارة المحقّق التراكمي.
open_positionsقائمة قواميس المراكز المفتوحة: id وsymbol وside وqty وentry_price وsl_price وtp_price وraw_entry_price وcommission_at_open وentry_time (قد يكون null). لا يوجد حقل ربح/خسارة لكل رِجل في صفوف المراكز المفتوحة — اقرأ unrealized_pnl من اللقطة للحصول على الربح/الخسارة العائم الإجمالي.
closed_tradesقائمة الصفقات المغلقة: position_id وsymbol وside وqty وأسعار الدخول/الخروج والربح/الخسارة المحقّق و**commission** (تتضمن حصة هذا الإغلاق من عمولة الفتح) و**slippage_cost** (انزلاق الدخول + الخروج) و**raw_exit_price** و**raw_entry_price** الاختياري وentry_time / exit_time؛ وتضيف الإغلاقات الجزئية partial وremaining_qty. وهي متاحة أيضًا باسم account.closed_trades على الكائن.
tick_pricesنسخة من خريطة الأسعار المستخدمة لتلك اللقطة. يرحّل close_trade العلامات السابقة ويضع علامة الرمز المغلق عند تنفيذ خروجه، فتبقى الأرجل المفتوحة مقيَّمة عبر الإغلاق.

لا توجد واجهة account.get_open_positions(symbol). صفِّ snap["open_positions"] (أو account.open_positions_payload()) بحسب symbol بأحرف كبيرة، أو اقرأ account.closed_trades للسجل المغلق.

يسهّل هذا فحص الحساب داخل استراتيجيتك:

snap = account.open_trade("AAPL", "long", 10, px) print("equity now:", snap["equity"]) print("open positions:", len(snap["open_positions"]))

مقاييس الأداء

عندما ينتهي التشغيل، يمكنك أن تطلب من الحساب حساب مجموعة كاملة من المقاييس:

def on_finish(bars, symbol=None, bars_by_symbol=None): last_px = float(bars[-1]["close"]) metrics = account.refresh_metrics({"AAPL": last_px})

الوسيط خريطة {symbol: last_price} تُستخدم علامةً نهائية لتقييم المراكز المفتوحة بشكل صحيح. استخدم قائمة bars الممرَّرة إلى on_finish (الأقدم أولًا) للحصول على آخر إغلاق — فهي السلسلة المرجعية لكامل السجل.

ويمكنك أيضًا تمرير وسيطين اختياريين:

  • risk_free_rate=0 — تستخدمه نسب شبيهة بشارب/سورتينو.
  • calmar_annualized_return=None — يتجاوز العائد السنوي الذي يستخدمه كالمار.

المقاييس المتاحة

يُرجع refresh_metrics(...) قاموسًا يتضمن:

المجموعةالحقول
العوائدtotal_pnl وtotal_return وcagr
المخاطرmax_drawdown وvolatility وrisk_adjusted_return
النسبsharpe_ratio وsortino_ratio وcalmar_ratio وprofit_factor
إحصاءات الصفقاتaverage_trade_expectancy وaverage_win وaverage_loss وtotal_commission وtotal_slippage_cost
التعرّضcurrent_exposure وaverage_exposure وmax_exposure
الإجراءات المؤسسيةtotal_dividend_income وtotal_splits_applied

ملاحظات:

  • تستخدم النسب القائمة على الصفقات العائد لكل صفقة المعرَّف بأنه realized_pnl / starting_balance.
  • يستخدم total_return وcagr حقوق الملكية الحالية مقابل الرصيد الابتدائي.
  • للحصول على CAGR / Calmar ذي معنى يحتاج المحرك إلى معلومات تقويمية — وهو يمرّر الطوابع الزمنية بالفعل عند تحريك الحساب، لذا لا تحتاج عمومًا إلى فعل شيء خاص.
  • قد تُرجع بعض النسب inf عندما يكون المقام صفرًا والأداء مواتيًا (مثل غياب الصفقات الخاسرة).

وتظهر المقاييس أيضًا في عرض Test Results في المحرر وفي الحمولة المنظّمة لنتيجة التشغيل.


جمع كل شيء معًا

الاستخدام المعتاد داخل استراتيجية:

  1. اضبط الرصيد الابتدائي والعمولة / الانزلاق في علامة تبويب Simulation (SL/TP / المخاطر الاختيارية عبر .qcs / الشيفرة عند الدعم).
  2. افتح الصفقات من on_bar (أو من on_tick لعمليات التنفيذ داخل العمود) بواسطة account.open_trade(...) — واحتفظ بـ id المُرجَع إن كنت ستحتاج إليه.
  3. أغلق الصفقات بـ account.close_trade(position_id, exit_price) — أو بـ qty= للتخفيف وترك باقي الدفعة مفتوحًا.
  4. اقرأ snap["equity"] وsnap["cash"] وsnap["unrealized_pnl"] وغيرها كلما أردت فحص الحساب.
  5. في on_finish استدعِ account.refresh_metrics({symbol: last_price}) لملء مقاييس الأداء في النتيجة.

هذا هو نموذج الحساب كاملًا — واجهة صغيرة، لكنها كافية لمحاكاة المراكز الشرائية والبيعية والتدفقات النقدية والربح/الخسارة ومجموعة مقاييس كاملة لأي استراتيجية يمكنك التعبير عنها بدوال دورة الحياة.

12 دقائق للقراءةاقترح تعديلًا ↗
نموذج الحساب — الوثائق (1.0 (latest)) | QuantCraft