فرمول نویسی چندپایه با optionSE | شورت کال باترفلای
آموزش فرمولنویسی استراتژی چندپایه در دیدهبان: حلقهٔ تودرتوی optionSE برای K2 و K3، شرط تقارن، سرخط درست هر پایه، ثابتهای SETTING_ و FILTER_ و pos.add.
نویسنده: آپشن باز، تاریخ انتشار: ، آخرین بهروزرسانی: ، 24 دقیقه مطالعه
اشتراکگذاری:
این مقاله برای کسانی است که میخواهند خودشان در دیدهبان با فرمولنویسی ترکیبهای شورت کال باترفلای (Short Call Butterfly) را پیدا کنند: فروش 1 کال با اعمال پایین (K1)، خرید 2 کال با اعمال میانی (K2) و فروش 1 کال با اعمال بالا (K3) روی یک نماد و یک سررسید، با شرط تقارن K2 − K1 = K3 − K2.
منطق کد: از ردیف جاری بهعنوان K1 شروع کنید، با تابع optionSE اعمالهای بالاتر را برای K2 و K3 پیدا کنید، اعتبار خالص دریافتی را با سمت درست سرخطها حساب کنید و سپس سود دو دنباله، حداکثر زیان، دو سربهسر و سرمایهٔ درگیر را بسازید. مبانی مفهومی در شورت کال باترفلای و راهنمای اسکنر آماده در پیادهسازی در دیدهبان آمده است.
برای ساخت فرمول، میزکار را باز کنید و یک باکس دیدهبان اضافه کنید (راهنمای میزکار)؛ سپس فرمول نویسی را بزنید. جزئیات فیلدها و توابع در راهنمای فرمولنویسی آمده است.
چهار قرارداد، سه پایه در کد
نسبت پایهها همیشه 1 : 2 : 1 است و هر سه قرارداد باید اختیار خرید (Call)، همسررسید و روی یک نماد پایه باشند:
نسبت پایهها همیشه 1 : 2 : 1 است — هر سه کال، همسررسید و روی یک نماد پایه. بالها فروخته میشوند و بدنه خریداری.
highOption = optionSE(j) با j > i = کال فروختهشده @ K3 (بال بالا)
چون optionSE بهطور پیشفرض فقط قراردادهای همنوعِ ردیف جاری را برمیگرداند، کافی است در ابتدای فرمول یکبار option.type !== 'call' را رد کنید:
if (option.type !== 'call') {
if (FILTER_RESULT === true) return false;
return '';
}
// پایهٔ K1 فروخته میشود؛ بدون سرخط تقاضا هیچ ترکیبی معتبر نیست
if (option.ti.buy1Volume === 0 || option.ti.buy1Price === 0) {
if (FILTER_RESULT === true) return false;
return '';
}
شرطهایی که به متغیر حلقه وابسته نیستند (مثل سرخط خودِ ردیف) را بیرون حلقه بگذارید تا در هر تکرار دوباره ارزیابی نشوند.
اعتبار خالص با سمت درست سرخط
هر پایه باید با قیمتی محاسبه شود که واقعاً با آن معامله میشود: پایههای فروش با سرخط تقاضا (buy1Price) و پایهٔ خرید با سرخط عرضه (sell1Price). خطای رایج، استفاده از lastPrice برای هر سه پایه است که اعتبار را خوشبینانه نشان میدهد.
فروش با buy1Price و خرید با sell1Price — با قیمت پایانی، اعتبار خالص بزرگتر درمیآید و ردیفهای غیرقابلاجرا بالای فهرست مینشینند.
چون بدنه دو قرارداد است، حجم سرخط عرضهٔ K2 باید حداقل 2 باشد (midOption.ti.sell1Volume < 2 را رد کنید). دربارهٔ نرخها: کارمزد معاملات اختیار معامله.
سه ناحیهٔ کارمزد اعمال — مهمترین نکتهٔ این استراتژی
در منابع خارجی حداکثر سود شورت باترفلای «همان اعتبار خالص دریافتی» معرفی میشود. این جمله در بازار ایران فقط برای دنبالهٔ پایین (زیر K1) درست است؛ بالای K3 هر سه پایه ITM میشوند و اعمال هر چهار قرارداد کارمزد دارد — و کارمزد اعمال فروشنده روی سهام 0.55٪ ارزش اعمال است (روی صندوقها/ETF حدود 0.05٪). یعنی دو پایهٔ فروش K1 و K3 با هم میتوانند بیش از 1٪ ارزش قرارداد کارمزد بسازند.
کارمزد اعمال در چهار ناحیهٔ قیمت، چهار مقدار متفاوت دارد — پس در فرمول باید تابعی از قیمت فرضی باشد، نه یک عدد ثابت.«حداکثر سود = اعتبار خالص» فقط زیر K1 درست است؛ بالای K3 چهار کارمزد اعمال از آن کم میشود. سودِ قابل اتکا کمترینِ دو دنباله است.
ناحیهٔ قیمت پایه در سررسید
کدام پایهها اعمال میشوند؟
کارمزد اعمال
سود/زیان
S < K1
هیچکدام
صفر
سود کامل = اعتبار خالص دریافتی
K1 < S < K2
فروش K1
کارمزد اعمال فروشنده روی K1
کاهنده تا حداکثر زیان
K2 < S < K3
فروش K1 + خرید 2×K2
کارمزد فروشندهٔ K1 + دو کارمزد خریدار K2
افزاینده از حداکثر زیان به بالا
S > K3
هر سه پایه
کارمزد فروشندهٔ K1 و K3 + دو کارمزد خریدار K2
سود ثابت = اعتبار − کارمزد اعمال
پس در فرمول باید کارمزد اعمال را تابعی از قیمت فرضی سررسید نوشت، نه یک عدد ثابت:
const exerciseFeeAt = (price, midOpt, highOpt) => {
if (!SETTING_FEE_EXERCISE) return 0;
let ex = 0;
if (price > option.strike) ex += option.strike * option.size * option.writeExerciseFee;
if (price > midOpt.strike) ex += 2 * midOpt.strike * option.size * midOpt.buyExerciseFee;
if (price > highOpt.strike) ex += highOpt.strike * option.size * highOpt.writeExerciseFee;
return ex;
};
const exZoneLow = SETTING_FEE_EXERCISE
? option.strike * option.size * option.writeExerciseFee : 0; // K1..K2
const exZoneMid = exerciseFeeAt(midOption.strike + 1, midOption, highOption); // K2..K3
const exZoneTop = exerciseFeeAt(highOption.strike + 1, midOption, highOption); // بالای K3
اشتباهی که ستون را غلط میکند
اگر برای سربهسر بالا از کارمزد ناحیهٔ «بالای K3» استفاده کنید، پایهٔ K3 را هم اعمالشده حساب کردهاید در حالی که سربهسر بالا زیر K3 است. همین جابهجایی ناحیه در سهام تا حدود نیم درصد قیمت پایه خطا میدهد — و همین اندازه فاصله معمولاً تمام سود این استراتژی است.
سود دو دنباله، حداکثر زیان و دو سربهسر
با تقارن اعمالها (K1 − 2K2 + K3 = 0) شکل بازده در سررسید ساده است: دو فلات سود در دو طرف و یک قلهٔ زیان روی K2.
دو فلات سود در دو طرف و یک قلهٔ زیان روی K2 — ولی بدترین نقطه ذرهای بالاتر از K2 است، جایی که دو پایهٔ خریداریشده با ارزش ذاتی صفر اعمال میشوند.کارمزد اعمال فروشندهٔ K3 یکجا اضافه میشود، پس ممکن است کمی زیر K3 در سود باشید و بالای آن در زیان — منحنی همانجا یک پله میپرد.
// سود دنبالهٔ پایین و بالا (جدا از هم)
const profitDown = netCreditCash; // زیر K1 هیچ اعمالی نیست
const profitUp = netCreditCash - exZoneTop; // بالای K3 هر سه پایه اعمال میشوند
const plBelowK3 = netCreditCash - exZoneMid; // درست زیر K3
// سود قابل اتکا در هر دو جهت خروج از محدوده
const maxProfit = Math.min(profitDown, profitUp);
// حداکثر زیان: درست بالای K2 (بدترین نقطه)
const gap1 = midOption.strike - option.strike;
const maxLoss = (gap1 * option.size) - netCreditCash + exZoneMid;
// دو سربهسر — هر ناحیه با کارمزد خودش
const lowerBE = option.strike + ((netCreditCash - exZoneLow) / option.size);
const upperBE = highOption.strike - (plBelowK3 / option.size);
حداکثر زیان دقیقاً روی K2 نیست، بلکه ذرهای بالاتر است: در همان نقطه دو پایهٔ خرید K2 هم اعمال میشوند در حالی که ارزش ذاتیشان صفر است، پس کارمزد خریدار هم اضافه میشود.
اگر plBelowK3 <= 0 باشد سربهسر بالا وجود ندارد: کارمزد اعمال K1 و دو پایهٔ K2 از اعتبار بیشتر شده و کل ناحیهٔ K2 تا K3 زیان است.
پرش نزولی روی K3: چون کارمزد اعمال فروشندهٔ K3 یکجا اضافه میشود، ممکن است کمی زیر K3 در سود باشید و بالای K3 در زیان. این تنها استراتژی باترفلای است که در بازار ایران این پرش را جدی نشان میدهد.
یک تابع P&L بنویسید و همهٔ اعداد نمایشی را با آن اعتبارسنجی کنید؛ اگر currentPnL(lowerBE) و currentPnL(upperBE) صفر نشدند، فرمول سربهسرها غلط است:
const currentPnL = (price) => {
let intrinsic = 0;
if (price > option.strike) intrinsic -= (price - option.strike); // Short K1
if (price > midOption.strike) intrinsic += 2 * (price - midOption.strike); // Long 2x K2
if (price > highOption.strike) intrinsic -= (price - highOption.strike); // Short K3
return (intrinsic * option.size) + netCreditCash - exerciseFeeAt(price, midOption, highOption);
};
حلقهٔ تو در تو و شرط تقارن
optionSE(n) برای n ≥ 0 قرارداد nام از اعمال ردیف جاری به بالا را میدهد (0 = خود ردیف)، مرتبشده بر اساس قیمت اعمال. پس دو حلقهٔ تو در تو خودبهخود K1 < K2 < K3 را تضمین میکنند:
حلقه با شرطِ بزرگتر بودنِ اندیس دوم، جفتها را میسازد و شرط تقارن غربالشان میکند: فاصلهٔ K1 تا K2 باید با K2 تا K3 برابر باشد.حلقه با شرطِ بزرگتر بودنِ اندیس دوم، جفتها را میسازد و شرط تقارن غربالشان میکند: فاصلهٔ K1 تا K2 باید با K2 تا K3 برابر باشد.
let indexMid = 0;
while (optionSE(++indexMid) !== undefined) {
const midOption = optionSE(indexMid); // K2
if (midOption.ti.sell1Volume < 2 || midOption.ti.sell1Price === 0) continue;
let indexHigh = indexMid;
while (optionSE(++indexHigh) !== undefined) {
const highOption = optionSE(indexHigh); // K3
if (highOption.ti.buy1Volume === 0 || highOption.ti.buy1Price === 0) continue;
// شرط تقارن باترفلای — ریاضی سربهسر بالا به آن تکیه دارد
const gap1 = midOption.strike - option.strike;
const gap2 = highOption.strike - midOption.strike;
if (gap1 !== gap2 || gap1 <= 0) continue;
// محاسبات…
}
}
شرط gap1 <= 0 همزمان اعمالهای تکراری را حذف میکند. اگر تقارن را بردارید دیگر باترفلای ندارید و فرمول سربهسر بالا معتبر نیست — آن حالت باترفلای بالشکسته است و ریاضی جداگانه دارد.
سرمایهٔ درگیر، وجه تضمین و pos.add
شورت کال باترفلای را میتوان «کال اسپرد نزولی (فروش K1 + خرید K2) + کال اسپرد صعودی (خرید K2 + فروش K3)» دید. این تفکیک، پاسخ وجه تضمین را میدهد:
پایهٔ فروش K3 با پایهٔ خرید K2 که اعمال پایینتری دارد کاملاً پوشش داده میشود ⇒ وجه تضمین ندارد.
پایهٔ فروش K1 اعمال پایینتر از پایهٔ خرید دارد ⇒ پوشش کامل نیست و «اختلاف دو اعمال منهای پرمیوم دریافتی» قفل میشود؛ اگر این عدد منفی شد یا از وجه تضمین استاندارد فروش بیشتر بود، وجه تضمین استاندارد ملاک است.
const k1DefaultMargin = option.requiredMargin || 0;
let k1Margin = (gap1 - bid1) * option.size;
let useDefaultMargin = false;
if (k1Margin <= 0) { k1Margin = k1DefaultMargin; useDefaultMargin = true; }
else if (k1DefaultMargin > 0 && k1Margin > k1DefaultMargin) { k1Margin = k1DefaultMargin; useDefaultMargin = true; }
const netCashOut = ((2 * ask2 - bid3) * option.size) + tradeFee;
const requiredCapital = Math.max(k1Margin + netCashOut, 1);
// همان عدد وجه تضمین را به نمودار هم بدهید تا سرمایهٔ ستون و نمودار یکی شود
pos.add('sell', option.namad, 1, 'buy', k1Margin > 1 ? k1Margin : true);
pos.add('buy', midOption.namad, 2, 'sell');
pos.add('sell', highOption.namad, 1, 'buy', false); // پوششداده با K2
return pos.build('شورت کال باترفلای ' + option.namad, SETTING_FEE, 'itm-only');
پارامتر پنجم pos.add عدد هم میپذیرد
در pos.add(side, namad, count, price, cashBlock) مقدار true یعنی «وجه تضمین استاندارد را خودت حساب کن»، false یعنی «پوششداده است و وجه تضمین ندارد»، و یک عدد یعنی «همین مقدار قفل میشود». اگر در ستون وجه تضمین اسپرد را لحاظ کردهاید ولی به نمودار false بدهید، سرمایهٔ ستون و نمودار با هم نمیخواند.
مشاهده وگا و سایر یونانیها در نمودار استراتژی
علاوه بر ستون و فیلتر دیدهبان، در نمودار استراتژی میتوانید یونانیها را روی منحنی سود/زیان ببینید: موقعیتهای call/put را اضافه کنید، از بخش «یونانیها» روی نمودار استفاده کنید و حساسیت کل استراتژی به قیمت پایه و زمان را لحظهای بررسی کنید. جمع دلتای موقعیتها در پایین جدول نیز نمایش داده میشود.
شورت باترفلای وگای مثبت دارد: با رشد نوسان ضمنی سود میکند و اگر قیمت روی بدنه بماند تتای منفی به آن ضرر میزند. برای مرور این دو ضریب: وگا و تتا.
سود تضمینی: وقتی اعتبار از فاصلهٔ اعمال بیشتر است
اگر اعتبار خالص دریافتی از (K2 − K1) × اندازه قرارداد بیشتر شود، maxLoss منفی میشود؛ یعنی موقعیت در هیچ قیمتی زیان نمیدهد. این ترکیبها را نباید با continue حذف کرد — همانها ارزشمندترین یافتهٔ اسکنر هستند:
وقتی اعتبار خالص از فاصلهٔ اعمال بیشتر شود، حتی قلهٔ زیان هم بالای صفر میماند — ولی چنین ردیفی معمولاً نقدشونده نیست.
const hasGuaranteedProfit = maxLoss <= 0;
const guaranteedProfit = hasGuaranteedProfit ? -maxLoss : 0;
// در این حالت سربهسر وجود ندارد، احتمال موفقیت 100٪ و نسبت سود به زیان بینهایت است
در بازار کمعمق ایران بخشی از این موارد محصول سرخطهای قدیمی یا کمحجم است، نه قیمتگذاری اشتباه واقعی؛ بنابراین همیشه کنار آن فیلتر حداقل ارزش سفارش هر پایه را روشن نگه دارید (سنجش نقدشوندگی).
ثابتهای SETTING_ و FILTER_
برای اتصال خودکار به پنجرهٔ تنظیمات ویژوال (راهنمای تنظیمات ویژوال) از پیشوندهای استاندارد استفاده کنید:
const SETTING_COLLATERAL = true; // وثیقه/سرمایه درگیر در برابر وجه تضمین کامل دو پایهٔ فروش
const SETTING_FEE = true; // کارمزد معامله
const SETTING_FEE_EXERCISE = true; // کارمزد اعمال
const FILTER_MIN_RETURN = 1; // حداقل سود ماهانه (کمترین سود دو دنباله)
const FILTER_MIN_ORDER_VALUE = 10; // حداقل ارزش سفارش هر پایه (میلیون ریال)
const FILTER_MIN_K2_VOLUME = 1000; // حداقل حجم معاملات بدنه
const FILTER_PRICE_ZONES = '1,2,3,4,5';
const FILTER_REQUIRE_UPSIDE_PROFIT = false; // فقط سود در هر دو دنباله
const FILTER_GUARANTEED_ONLY = false; // فقط سود تضمینی
const FILTER_SHOW = false;
const FILTER_RESULT = false;
FILTER_REQUIRE_UPSIDE_PROFIT — فقط ترکیبهایی که بالای K3 هم پس از کارمزد اعمال سه پایه سود میدهند
FILTER_GUARANTEED_ONLY — فقط ترکیبهای بدون ناحیهٔ زیان
FILTER_PRICE_ZONES — پنج زون: زیر K1، K1 تا سربهسر پایین، بین دو سربهسر (زیان)، سربهسر بالا تا K3، بالای K3
FILTER_MIN_K2_VOLUME — بدنه دو قرارداد است و کمعمقترین پایه محسوب میشود
استراتژی آماده
اگر نمیخواهید از صفر بنویسید، استراتژی آمادهٔ شورت کال باترفلای در دیدهبان همین منطق را اجرا میکند: اسکن همهٔ ترکیبهای متقارن، سود دو دنباله، حداکثر زیان، دو سربهسر، احتمال موفقیت و نمودار. راهنمای گامبهگام: شورت کال باترفلای در دیدهبان · فهرست همهٔ اسکنرها: استراتژیهای آماده.
جمعبندی: در فرمولنویسی این استراتژی سه چیز تعیینکننده است: سمت درست سرخط برای هر پایه، تفکیک کارمزد اعمال به سه ناحیهٔ سررسید (وگرنه سود دنبالهٔ بالا و هر دو سربهسر غلط میشوند)، و وجه تضمین پایهٔ فروش K1 که با پوشش K2 به «اختلاف دو اعمال» کاهش مییابد. اعداد نمایشی را همیشه با تابع currentPnL خودتان راستیآزمایی کنید.
اگر سوال یا ابهامی در رابطه با این مطلب دارید، همینجا بپرسید؛ کارشناسان ما پاسخ میدهند.
با ایمیل یا شماره موبایلی که ثبت میکنید، وقتی به کامنتتان پاسخ داده شود خبرتان میکنیم.
پرسشها و نظرات کاربران
4 پیام
الهام
02 اردیبهشت 1405 - 17:32
سه ناحیهٔ کارمزد اعمال را مهمترین نکته نوشتید. چرا اینقدر اهمیت دارد؟
تیم آپشن بازپاسخ
05 اردیبهشت 1405 - 09:07
چون در ساختارهای چندپایه، کارمزد اعمال بسته به قیمت پایه در سررسید عوض میشود — و اگر آن را ثابت فرض کنید، عددی که میسازید در بخشی از نمودار غلط است.
منشأ ماجرا یک قاعدهٔ ساده است: فقط قراردادهای در سود اعمال میشوند. پس در باترفلای که سه سطح اعمال دارید، بسته به اینکه قیمت پایه کجا بنشیند، تعداد پاهای اعمالشونده فرق میکند — و همین سه ناحیه را میسازد.
دو چیزی که این را از یک ریزهکاری به یک مسئلهٔ جدی تبدیل میکنند:
کارمزد اعمال درصدی از قیمت اعمال است، نه از سود شما. روی ساختار باریک، این قلم میتواند از خود سود بزرگتر شود.
سه پا یعنی تا سه بار. خطای کوچک در یک پا، در نتیجهٔ نهایی سه برابر میشود.
نتیجهٔ عملی برای فرمولنویسی: هرگز یک عدد کارمزد ثابت را از کل نمودار کم نکنید. ناحیه را از روی قیمت پایه تشخیص دهید و کارمزد همان ناحیه را اعمال کنید — دقیقاً کاری که مقاله در کد نشان میدهد.
و برای بررسی نتیجه، عدد را در هر سه ناحیه جداگانه امتحان کنید، نه فقط در نقطهٔ سود بیشینه.
مجید
21 آذر 1404 - 10:56
فرمول را نوشتم ولی ستون خالی است. از کجا شروع کنم به عیبیابی؟
تیم آپشن بازپاسخ
22 آذر 1404 - 08:17
خالی بودن ستون تقریباً همیشه یکی از این چهار دلیل را دارد، و به همین ترتیب بررسی کنید:
۱. فیلتری همهچیز را حذف کرده. رایجترین علت. ثابتهای `FILTER_` را موقتاً روی مقداری بگذارید که چیزی حذف نکند و دوباره اجرا کنید. اگر خروجی آمد، مقصر پیدا شده.
۲. شرط تقارن هیچ ترکیبی را قبول نمیکند. در ساختارهای سهسطحی، فاصلهٔ مساوی اجباری است و در نردبانهای نامنظم ممکن است هیچ ترکیبی نداشته باشید. روی نمادی با نردبان منظم آزمایش کنید.
۳. فرمول `return` ندارد یا در همهٔ مسیرها مقدار برنمیگرداند.
۴. حلقه اصلاً وارد نشده چون سررسید انتخابشده قرارداد کافی ندارد.
روش سریع برای تشخیص بین این چهار: همهٔ فیلترها را خاموش کنید و فقط یک عدد ثابت برگردانید. اگر آن هم چیزی نشان نداد، مشکل در حلقه یا داده است نه در محاسبه. اگر نشان داد، بهترتیب محاسبات را برگردانید تا ببینید کدام قدم خروجی را میکشد.
و آزمایش را روی نمادی انجام دهید که زنجیرهٔ پرمعامله دارد؛ نماد کمعمق، عیبیابی را از مسئلهٔ کد به مسئلهٔ داده تبدیل میکند.