घटना-संचालित बैकटेस्टर श्रृंखला पर पिछले लेख में हमने एक बुनियादी निष्पादन हैंडलर पदानुक्रम पर विचार किया। इस लेख में हम पोर्टफोलियो ऑब्जेक्ट में पहले से निर्मित इक्विटी वक्र डेटाफ्रेम का उपयोग करके बैकटेस्ट के बाद रणनीति के प्रदर्शन का आकलन करने के तरीके पर चर्चा करने जा रहे हैं।
हमने पहले ही एक लेख में शार्प अनुपात पर विचार किया है। उस लेख में मैंने रेखांकित किया है कि (वार्षिक) शार्प अनुपात की गणना निम्न के माध्यम से की जाती हैः
जहां Ra इक्विटी वक्र की रिटर्न स्ट्रीम है और Rb एक बेंचमार्क है, जैसे कि उपयुक्त ब्याज दर या इक्विटी सूचकांक।
अधिकतम ड्रॉडाउन और ड्रॉडाउन अवधि दो अतिरिक्त माप हैं जिनका उपयोग निवेशक अक्सर पोर्टफोलियो में जोखिम का आकलन करने के लिए करते हैं। पूर्ववर्ती मात्रा एक इक्विटी वक्र प्रदर्शन में उच्चतम शिखर से नीचे की ओर गिरावट है, जबकि उत्तरार्द्ध को व्यापारिक अवधि की संख्या के रूप में परिभाषित किया गया है जिसमें यह होता है।
इस लेख में हम पायथन आधारित इवेंट-ड्राइव बैकटेस्टिंग सूट में उपयोग के लिए पोर्टफोलियो प्रदर्शन के उपायों के रूप में शार्प अनुपात, अधिकतम ड्रॉडाउन और ड्रॉडाउन अवधि को लागू करेंगे।
पहला कार्य एक नई फ़ाइल बनाना हैperformance.py, जो शार्प अनुपात और ड्रॉडाउन जानकारी की गणना करने के लिए कार्यों को संग्रहीत करता है। हमारे अधिकांश गणना-भारी वर्गों के साथ हमें NumPy और पांडा आयात करने की आवश्यकता हैः
# performance.py
import numpy as np
import pandas as pd
ध्यान दें कि शार्प अनुपात जोखिम-लाभ का एक माप है (वास्तव में यह कई में से एक है) । इसमें एक ही पैरामीटर है, जो वार्षिक मूल्य तक स्केल करने पर समायोजित करने वाली अवधि की संख्या है।
आमतौर पर यह मान 252 पर सेट होता है, जो प्रति वर्ष अमेरिका में ट्रेडिंग दिनों की संख्या है। हालांकि, यदि आपकी रणनीति एक घंटे के भीतर ट्रेड करती है, तो आपको इसे सही तरीके से वार्षिक करने के लिए शार्प को समायोजित करने की आवश्यकता होती है। इस प्रकार आपको अवधि को 252
create_sharpe_ratio फ़ंक्शन रिटर्न नामक पांडा सीरीज़ ऑब्जेक्ट पर काम करता है और केवल अवधि प्रतिशत रिटर्न और अवधि प्रतिशत रिटर्न मानक विचलन के औसत के अनुपात की गणना करता है जो अवधि कारक द्वारा स्केल किया जाता हैः
# performance.py
def create_sharpe_ratio(returns, periods=252):
"""
Create the Sharpe ratio for the strategy, based on a
benchmark of zero (i.e. no risk-free rate information).
Parameters:
returns - A pandas Series representing period percentage returns.
periods - Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc.
"""
return np.sqrt(periods) * (np.mean(returns)) / np.std(returns)
जबकि शार्प अनुपात यह दर्शाता है कि प्रति रिटर्न इकाई कितना जोखिम (संपत्ति पथ मानक विचलन द्वारा परिभाषित) लिया जा रहा है,
नीचे दिए गए create_drawdowns फ़ंक्शन वास्तव में अधिकतम ड्रॉडाउन और अधिकतम ड्रॉडाउन अवधि दोनों प्रदान करता है। पूर्ववर्ती उपर्युक्त सबसे बड़ी शिखर-से-कठोर गिरावट है, जबकि उत्तरार्द्ध को उन अवधियों की संख्या के रूप में परिभाषित किया गया है जिन पर यह गिरावट होती है।
ड्रॉडाउन अवधि की व्याख्या में कुछ सूक्ष्मता की आवश्यकता होती है क्योंकि यह व्यापारिक अवधियों को गिनती करती है और इसलिए इसे सीधे समय की इकाई जैसे
यह फ़ंक्शन दो पांडा सीरीज़ ऑब्जेक्ट बनाने से शुरू होता है जो प्रत्येक ट्रेडिंग
ड्रॉडाउन तब केवल वर्तमान एचडब्ल्यूएम और इक्विटी वक्र के बीच का अंतर है। यदि यह मान नकारात्मक है तो प्रत्येक बार के लिए अवधि बढ़ जाती है जब तक कि यह अगले एचडब्ल्यूएम तक नहीं पहुंच जाता है। फ़ंक्शन तब बस दो श्रृंखलाओं में से प्रत्येक के अधिकतम को लौटाता हैः
# performance.py
def create_drawdowns(equity_curve):
"""
Calculate the largest peak-to-trough drawdown of the PnL curve
as well as the duration of the drawdown. Requires that the
pnl_returns is a pandas Series.
Parameters:
pnl - A pandas Series representing period percentage returns.
Returns:
drawdown, duration - Highest peak-to-trough drawdown and duration.
"""
# Calculate the cumulative returns curve
# and set up the High Water Mark
# Then create the drawdown and duration series
hwm = [0]
eq_idx = equity_curve.index
drawdown = pd.Series(index = eq_idx)
duration = pd.Series(index = eq_idx)
# Loop over the index range
for t in range(1, len(eq_idx)):
cur_hwm = max(hwm[t-1], equity_curve[t])
hwm.append(cur_hwm)
drawdown[t]= hwm[t] - equity_curve[t]
duration[t]= 0 if drawdown[t] == 0 else duration[t-1] + 1
return drawdown.max(), duration.max()
इन प्रदर्शन मापों का उपयोग करने के लिए हमें एक बैकटेस्ट के बाद उनकी गणना करने के साधन की आवश्यकता होती है, अर्थात जब एक उपयुक्त इक्विटी वक्र उपलब्ध होता है!
हमें गणना को एक विशेष ऑब्जेक्ट पदानुक्रम के साथ भी जोड़ने की आवश्यकता है। यह देखते हुए कि प्रदर्शन उपायों की गणना एक पोर्टफोलियो के आधार पर की जाती है, यह पोर्टफोलियो वर्ग पदानुक्रम पर एक विधि के लिए प्रदर्शन गणना संलग्न करने के लिए समझ में आता है जो हमने इस लेख में चर्चा की है।
पहला कार्य खुला होना हैportfolio.pyजैसा कि पिछले लेख में चर्चा की गई है और प्रदर्शन कार्यों को आयात करेंः
# portfolio.py
.. # Other imports
from performance import create_sharpe_ratio, create_drawdowns
चूंकि पोर्टफोलियो एक अमूर्त आधार वर्ग है, इसलिए हम इसके व्युत्पन्न वर्गों में से एक में एक विधि संलग्न करना चाहते हैं, जो इस मामले में NaivePortfolio होगा। इसलिए हम एक विधि बनाएंगे जिसे output_summary_stats कहा जाता है जो शार्प और ड्रॉडाउन जानकारी उत्पन्न करने के लिए पोर्टफोलियो इक्विटी वक्र पर कार्य करेगा।
विधि सरल है. यह केवल दो प्रदर्शन उपायों का उपयोग करता है और उन्हें सीधे इक्विटी वक्र डेटाफ्रेम पर लागू करता है, एक प्रारूप-अनुकूल तरीके से ट्यूपल की सूची के रूप में आँकड़े आउटपुट करता हैः
# portfolio.py
..
..
class NaivePortfolio(object):
..
..
def output_summary_stats(self):
"""
Creates a list of summary statistics for the portfolio such
as Sharpe Ratio and drawdown information.
"""
total_return = self.equity_curve['equity_curve'][-1]
returns = self.equity_curve['returns']
pnl = self.equity_curve['equity_curve']
sharpe_ratio = create_sharpe_ratio(returns)
max_dd, dd_duration = create_drawdowns(pnl)
stats = [("Total Return", "%0.2f%%" % ((total_return - 1.0) * 100.0)),
("Sharpe Ratio", "%0.2f" % sharpe_ratio),
("Max Drawdown", "%0.2f%%" % (max_dd * 100.0)),
("Drawdown Duration", "%d" % dd_duration)]
return stats
स्पष्ट रूप से यह एक पोर्टफोलियो के लिए एक बहुत ही सरल प्रदर्शन विश्लेषण है। यह व्यापार स्तर के विश्लेषण या जोखिम / इनाम के अन्य उपायों पर विचार नहीं करता है।performance.pyऔर फिर उन्हें आवश्यकतानुसार output_summary_stat में शामिल करें।