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

