الشيفرة تُقرأ أكثر بكثير مما تُكتب
تعيش قاعدة شيفرة التداول سنوات ويعدّلها أناس لم يكتبوها، غالبًا تحت الضغط وغالبًا أنت بعد ستة أشهر. والشيفرة النظيفة هي التي تُفهم بسرعة وتُعدَّل بأمان. وليست قواعد أسلوب، بل جعل التعديل الخاطئ مرئيًا. فالدالة السيئة التسمية التي تفعل ثلاثة أشياء وتخفي حدًّا داخل معادلة هي مكمن الأخطاء المكلفة، كحدّ مخاطرة لا يتذكر أحد أنه مكتوب صلبًا في الشيفرة.
# The same calculation, written two ways
def f(d, e, m, w):
r = []
if sum([abs(x) for x in d.values()]) / e > m:
r.append("LEVERAGE")
for k in sorted(d):
if abs(d[k]) / sum([abs(x) for x in d.values()]) > w:
r.append("CONCENTRATION:" + k)
return r
book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(f(book, 50_000.0, 1.5, 0.5)) # works, but what is d? e? m? w?أسماء ودوال صغيرة وبلا أرقام مخفية
ثلاث عادات تؤدي معظم العمل. سمِّ الأشياء بما هي: gross_exposure بدل f وmax_leverage بدل m. اجعل الدوال صغيرة وذات غرض واحد: تؤدي كل منها عملًا يُسمّى بكلمات قليلة فتُختبر وحدها وتُعاد استعمالها، وتُبنى الدالة الأكبر باستدعائها. لا تدفن الأرقام: حدّ مثل 1.5 مكتوب داخل معادلة رقم سحري لا يستطيع أحد إيجاده أو تغييره بأمان؛ مرّره وسيطًا (أو سمِّه ثابتًا). وحساب التعرض الإجمالي مرة واحدة في دالته يزيل التعبير المكرر.
# Small functions with names that say what they return; the limits come in as arguments
def gross_exposure(positions):
return sum(abs(value) for value in positions.values())
def leverage(positions, equity):
return gross_exposure(positions) / equity
def risk_flags(positions, equity, max_leverage, max_weight):
flags = []
if leverage(positions, equity) > max_leverage:
flags.append("LEVERAGE")
gross = gross_exposure(positions)
for symbol in sorted(positions):
if abs(positions[symbol]) / gross > max_weight:
flags.append(f"CONCENTRATION:{symbol}")
return flags
book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(risk_flags(book, 50_000.0, 1.5, 0.5)) # ['LEVERAGE', 'CONCENTRATION:AAPL']
print(risk_flags(book, 50_000.0, 3.0, 0.9)) # [] the same code, looser limitsاحمِ الحدود وقل السبب
عالج المدخل السيئ أولًا وأرجع أو ارفع فورًا (جملة حراسة) ليُقرأ المسار الرئيس مستقيمًا بلا تداخل. ورافعة gross / equity مع رأس مال صفر يجب أن تكون ValueError واضحة لا ZeroDivisionError من الأعماق، والمحفظة الفارغة تعطي 0.0 لا انهيارًا. ويجب أن تشرح التعليقات السبب الذي لا تقوله الشيفرة (# البورصة تقرّب إلى السنت) لا أن تكرر ما تُظهره الشيفرة أصلًا. وتبيّن docstring ما تأخذه الدالة وما ترجعه. وأخيرًا فضّل الدوال النقية التي تعتمد على وسائطها فقط بلا آثار جانبية: فهي التي تُختبر بقليل من المدخلات.
# A guard clause stops bad input at the door and says exactly what is wrong
def leverage(positions, equity):
if equity <= 0:
raise ValueError("equity must be positive")
return sum(abs(value) for value in positions.values()) / equity
print(leverage({"AAPL": 60_000.0, "MSFT": -30_000.0}, 45_000.0)) # 2.0
print(leverage({}, 45_000.0)) # 0.0 an empty book is fine
try:
leverage({"AAPL": 60_000.0}, 0.0)
except ValueError as error:
print("rejected:", error) # rejected: equity must be positive# A pure function answers the same way every time; hidden state does not
history = []
def running_notional(price, quantity): # remembers earlier calls: the answer depends on the past
history.append(price * quantity)
return sum(history)
def notional(price, quantity): # depends only on its arguments
return price * quantity
print(running_notional(10.0, 5), running_notional(10.0, 5)) # 50.0 100.0 same call, different answers
print(notional(10.0, 5), notional(10.0, 5)) # 50.0 50.0 always the same- سمِّ الأشياء بما هي، واجعل الدوال صغيرة وذات غرض واحد، وابنِ الأكبر منها باستدعائها.
- لا أرقام سحرية: الحدود تأتي وسائط أو ثوابت مسمّاة؛ واحسب القيمة المكررة مرة واحدة.
- احمِ من المدخل السيئ أولًا بخطأ واضح، وعلّق على السبب لا على الماهية، وفضّل الدوال النقية القابلة للاختبار.
التالي: تلميحات الأنواع والفحص الساكن، لندع الأدوات تلتقط الأخطاء قبل تشغيل الشيفرة.
المختبر: ما الذي يُقيَّم
- احسب صافي المحفظة باستخدام
net_exposure25 نقطة - اجمع كل المراكز باستخدام
gross_exposure25 نقطة - احمِ المقام في
leverage25 نقطة - ركّب الفحوص في
risk_flags25 نقطة