تاريخ أسعار يكذب
عندما تقسّم شركة أسهمها 4 مقابل 1 يحصل كل مالك على أربعة أسهم لكل سهم ويهبط السعر إلى الربع. لم يخسر أحد شيئًا، لكن التاريخ الخام يُظهر يومًا بنسبة -75%. والتوزيع النقدي يفعل الشيء نفسه بحجم أصغر: في تاريخ الاستحقاق ينخفض السعر بمقدار التوزيع تقريبًا. واستراتيجية تقرأ الأسعار الخام سترى انهيارات لم تؤذِ أحدًا. والحل تعديل التاريخ: إعادة قياس كل الأيام السابقة للحدث لتصبح السلسلة متصلة وقابلة للمقارنة بأسعار اليوم.
import pandas as pd
index = pd.bdate_range("2026-03-02", periods=6)
raw = pd.Series([480.0, 484.0, 488.0, 123.0, 124.5, 126.0], index=index, name="close") # 4-for-1 split on 5 March
print(raw.pct_change().round(4).tolist())
# [nan, 0.0083, 0.0083, -0.748, 0.0122, 0.012] the -74.8% day never happened to anyoneتعديل التقسيم
تتغير فقط الأيام السابقة للتقسيم: اقسم أسعارها على النسبة (4 لتقسيم 4 مقابل 1). تبقى أسعار اليوم كما هي مسعّرة فتنتهي السلسلة المعدلة عند السعر الحالي الحقيقي. ابنِ قناع صح/خطأ بـ index >= split_date (نص التاريخ يصلح مع DatetimeIndex) واستخدم where الذي يُبقي القيمة حيث القناع صحيح ويأخذ البديل في غيره. ويسير الحجم في الاتجاه المعاكس: يُتداول بعد التقسيم أربعة أضعاف الأسهم، فتُضرب الأحجام السابقة في النسبة. ويستخدم التقسيم العكسي نسبة أقل من 1 والصيغ نفسها.
split_date = "2026-03-05"
adjusted = raw.where(raw.index >= split_date, raw / 4) # the days before the split, divided by 4
print(adjusted.tolist()) # [120.0, 121.0, 122.0, 123.0, 124.5, 126.0]
print(adjusted.pct_change().round(4).tolist()) # [nan, 0.0083, 0.0083, 0.0082, 0.0122, 0.012] no more crash# Volume goes the other way: older days are multiplied
volume = pd.Series([1000, 1100, 900, 4200, 4000, 4300], index=index)
print(volume.where(volume.index >= split_date, volume * 4).tolist()) # [4000, 4400, 3600, 4200, 4000, 4300]تعديل التوزيعات
التوزيع أصغر من التقسيم، لذا يكون التعديل معاملًا قريبًا من 1: 1 - التوزيع / الإغلاق السابق، والإغلاق السابق هو آخر إغلاق قبل تاريخ الاستحقاق. اضرب كل سعر سابق للتاريخ في هذا المعامل. فيهبط الإغلاق الذي يسبق التاريخ مباشرة بمقدار التوزيع بالضبط، ولا تُظهر حركة يوم الاستحقاق إلا ما فعله السوق حقًّا، وتشمل العوائد المحسوبة من السلسلة المعدلة التوزيع. وهذا الإغلاق المعدّل هو ما ينبغي أن تستخدمه حسابات العوائد. ضع المعامل في دالة صغيرة مستقلة لتختبره وتعيد استخدامه.
def dividend_factor(prev_close, dividend):
return 1 - dividend / prev_close
prices = pd.Series([100.0, 101.0, 102.0, 100.8, 101.5, 102.5], index=index) # a 1.50 dividend, ex-date 5 March
ex_date, dividend = "2026-03-05", 1.5
prev_close = prices[prices.index < ex_date].iloc[-1] # 102.0, the last close before the ex-date
factor = dividend_factor(prev_close, dividend)
print(round(factor, 6)) # 0.985294
adjusted = prices.where(prices.index >= ex_date, prices * factor)
print(adjusted.round(4).tolist()) # [98.5294, 99.5147, 100.5, 100.8, 101.5, 102.5]
print(prices.pct_change().round(4).tolist()[3], adjusted.pct_change().round(4).tolist()[3]) # -0.0118 0.003close وadj_close) وحدّد أيهما يستخدمه كل حساب.- يقسم التقسيم الأسعار القديمة على النسبة ويضرب الأحجام القديمة بها؛ وتتغير الأيام السابقة للتقسيم فقط.
- يضرب التوزيع الأسعار السابقة لتاريخ الاستحقاق في
1 - التوزيع / الإغلاق السابق. - استخدم
where(index >= date, المعدّل)مع قناع لا حلقة؛ احسب العوائد من الأسعار المعدلة وتداول بالخام.
التالي: الرسوم الخطية مع matplotlib، لرسم سلسلة الأسعار النظيفة التي أصبحت بين يديك.
المختبر: ما الذي يُقيَّم
- اجعل الأسعار قابلة للمقارنة عبر التقسيم باستخدام
adjust_for_split25 نقطة - قيّس الأحجام القديمة باستخدام
adjust_volume_for_split25 نقطة - احسب معامل تعديل التوزيعات باستخدام
dividend_factor25 نقطة - أزل هبوط التوزيعات من التاريخ باستخدام
adjust_for_dividend25 نقطة