ایونٹ سے چلنے والی بیک ٹسٹنگ کے بارے میں پچھلے مضمون میں ہم نے اس بات پر غور کیا کہ حکمت عملی کلاس درجہ بندی کی تعمیر کیسے کی جائے۔ یہاں بیان کردہ حکمت عملیوں کا استعمال سگنل پیدا کرنے کے لئے کیا جاتا ہے ، جو کسی پورٹ فولیو آبجیکٹ کے ذریعہ آرڈر بھیجنے کے بارے میں فیصلے کرنے کے لئے استعمال ہوتے ہیں۔ جیسا کہ پہلے تھا ، یہ قدرتی طور پر ایک پورٹ فولیو تجریدی بیس کلاس (ABC) بنانا ہے جس سے بعد کے تمام ذیلی طبقے وراثت کرتے ہیں۔
یہ مضمون ایک نائن پورٹ فولیو آبجیکٹ کی وضاحت کرتا ہے جو پورٹ فولیو کے اندر پوزیشنوں کا سراغ لگاتا ہے اور سگنلز کی بنیاد پر اسٹاک کی ایک مقررہ مقدار کے آرڈر تیار کرتا ہے۔ بعد میں پورٹ فولیو آبجیکٹ میں زیادہ نفیس رسک مینجمنٹ ٹولز شامل ہوں گے اور یہ بعد کے مضامین کا موضوع ہوں گے۔
پورٹ فولیو آرڈر مینجمنٹ سسٹم ممکنہ طور پر ایونٹ سے چلنے والے بیک ٹیسٹر کا سب سے پیچیدہ جزو ہے۔ اس کا کردار تمام موجودہ مارکیٹ پوزیشنوں کے ساتھ ساتھ پوزیشنوں کی مارکیٹ ویلیو (جسے
پوزیشنوں اور ہولڈنگز مینجمنٹ کے علاوہ پورٹ فولیو کو خطرے کے عوامل اور پوزیشن سائزنگ کی تکنیکوں سے بھی آگاہ ہونا چاہئے تاکہ بروکرج یا مارکیٹ تک رسائی کی کسی اور شکل کو بھیجے جانے والے احکامات کو بہتر بنایا جاسکے۔
ایونٹ کلاس درجہ بندی کے سلسلے میں جاری رکھتے ہوئے ، ایک پورٹ فولیو آبجیکٹ کو سگنل ایونٹ آبجیکٹ کو سنبھالنے ، آرڈر ایونٹ آبجیکٹ تیار کرنے اور پوزیشنوں کو اپ ڈیٹ کرنے کے لئے فل ایونٹ آبجیکٹ کی ترجمانی کرنے کے قابل ہونا چاہئے۔ لہذا یہ کوئی تعجب کی بات نہیں ہے کہ پورٹ فولیو آبجیکٹ اکثر کوڈ کی لائنوں (LOC) کے لحاظ سے ، ایونٹ سے چلنے والے سسٹم کا سب سے بڑا جزو ہوتے ہیں۔
ہم ایک نئی فائل بناتے ہیںportfolio.pyاور ضروری لائبریریاں درآمد کریں۔ یہ دوسرے تجریدی بیس کلاس کے بیشتر نفاذوں کی طرح ہیں۔ ہمیں عددی قدر والے آرڈر سائز پیدا کرنے کے لئے ریاضی لائبریری سے فلور فنکشن درآمد کرنے کی ضرورت ہے۔ ہمیں فل ایونٹ اور آرڈر ایونٹ آبجیکٹ کی بھی ضرورت ہے کیونکہ پورٹ فولیو دونوں کو سنبھالتا ہے۔
# portfolio.py
import datetime
import numpy as np
import pandas as pd
import Queue
سے abc درآمد ABCMeta، خلاصہ طریقہ ریاضی کی درآمد کی منزل سے
ایونٹ درآمد سے FillEvent، OrderEvent جیسا کہ پہلے ہم پورٹ فولیو کے لئے ایک اے بی سی تخلیق کرتے ہیں اور دو خالص مجازی طریقوں update_signal اور update_fill رکھتے ہیں۔ سابقہ واقعات کی قطار سے پکڑے جانے والے نئے تجارتی سگنل کو سنبھالتا ہے اور دوسرا ایک عملدرآمد ہینڈلر آبجیکٹ سے موصول ہونے والی بھرتیوں کو سنبھالتا ہے۔
# portfolio.py
class Portfolio(object):
"""
The Portfolio class handles the positions and market
value of all instruments at a resolution of a "bar",
i.e. secondly, minutely, 5-min, 30-min, 60 min or EOD.
"""
__metaclass__ = ABCMeta
@abstractmethod
def update_signal(self, event):
"""
Acts on a SignalEvent to generate new orders
based on the portfolio logic.
"""
raise NotImplementedError("Should implement update_signal()")
@abstractmethod
def update_fill(self, event):
"""
Updates the portfolio current positions and holdings
from a FillEvent.
"""
raise NotImplementedError("Should implement update_fill()")
اس مضمون کا بنیادی موضوع نایف پورٹ فولیو کلاس ہے۔ یہ پوزیشن سائزنگ اور موجودہ ہولڈنگز کو سنبھالنے کے لئے ڈیزائن کیا گیا ہے ، لیکن صرف ان کو براہ راست بروکرج کو پہلے سے طے شدہ فکسڈ مقدار کے سائز کے ساتھ بھیج کر ایک
NaivePortfolio کو ابتدائی سرمایہ کی قیمت کی ضرورت ہوتی ہے، جسے میں نے ڈیفالٹ کے طور پر 100،000 امریکی ڈالر مقرر کیا ہے۔ اس کے ساتھ ساتھ شروع ہونے کی تاریخ کی ضرورت ہوتی ہے۔
پورٹ فولیو میں all_positions اور current_positions ممبران شامل ہیں۔ سابقہ مارکیٹ ڈیٹا ایونٹ کے ٹائم اسٹیمپ پر ریکارڈ کی گئی تمام پچھلی پوزیشنوں کی ایک فہرست اسٹور کرتا ہے۔ پوزیشن صرف اثاثہ کی مقدار ہے۔ منفی پوزیشنوں کا مطلب یہ ہے کہ اثاثہ مختصر ہو گیا ہے۔ آخری ممبر ایک لغت اسٹور کرتا ہے جس میں مارکیٹ بار کی آخری تازہ کاری کے لئے موجودہ پوزیشنیں شامل ہیں۔
پوزیشن ممبروں کے علاوہ پورٹ فولیو ہولڈنگز کو اسٹور کرتا ہے ، جو ان پوزیشنوں کی موجودہ مارکیٹ ویلیو کی وضاحت کرتے ہیں۔
# portfolio.py
class NaivePortfolio(Portfolio):
"""
The NaivePortfolio object is designed to send orders to
a brokerage object with a constant quantity size blindly,
i.e. without any risk management or position sizing. It is
used to test simpler strategies such as BuyAndHoldStrategy.
"""
def __init__(self, bars, events, start_date, initial_capital=100000.0):
"""
Initialises the portfolio with bars and an event queue.
Also includes a starting datetime index and initial capital
(USD unless otherwise stated).
Parameters:
bars - The DataHandler object with current market data.
events - The Event Queue object.
start_date - The start date (bar) of the portfolio.
initial_capital - The starting capital in USD.
"""
self.bars = bars
self.events = events
self.symbol_list = self.bars.symbol_list
self.start_date = start_date
self.initial_capital = initial_capital
self.all_positions = self.construct_all_positions()
self.current_positions = dict( (k,v) for k, v in [(s, 0) for s in self.symbol_list] )
self.all_holdings = self.construct_all_holdings()
self.current_holdings = self.construct_current_holdings()
مندرجہ ذیل طریقہ ، construct_all_positions ، صرف ہر علامت کے لئے ایک لغت بناتا ہے ، ہر ایک کے لئے قدر کو صفر پر مقرر کرتا ہے اور پھر ایک تاریخ وقت کی کلید شامل کرتا ہے ، آخر کار اسے فہرست میں شامل کرتا ہے۔ یہ لغت کی تفہیم کا استعمال کرتا ہے ، جو روح میں فہرست کی تفہیم کی طرح ہے:
# portfolio.py
def construct_all_positions(self):
"""
Constructs the positions list using the start_date
to determine when the time index will begin.
"""
d = dict( (k,v) for k, v in [(s, 0) for s in self.symbol_list] )
d['datetime'] = self.start_date
return [d]
construct_all_holdings طریقہ کار مذکورہ بالا سے ملتا جلتا ہے لیکن نقد ، کمیشن اور کل کے لئے اضافی چابیاں شامل کرتا ہے ، جو بالترتیب کسی بھی خریداری کے بعد اکاؤنٹ میں اضافی نقد رقم کی نمائندگی کرتے ہیں ، جمع شدہ کمیشن اور کل اکاؤنٹ کیپٹل بشمول نقد رقم اور کسی بھی کھلی پوزیشنز۔ مختصر پوزیشنوں کو منفی سمجھا جاتا ہے۔ ابتدائی نقد رقم اور کل اکاؤنٹ کیپٹل دونوں کو ابتدائی سرمایہ پر مقرر کیا جاتا ہے۔
# portfolio.py
def construct_all_holdings(self):
"""
Constructs the holdings list using the start_date
to determine when the time index will begin.
"""
d = dict( (k,v) for k, v in [(s, 0.0) for s in self.symbol_list] )
d['datetime'] = self.start_date
d['cash'] = self.initial_capital
d['commission'] = 0.0
d['total'] = self.initial_capital
return [d]
مندرجہ ذیل طریقہ، construct_current_holdings مندرجہ بالا طریقہ کے ساتھ تقریبا ایک جیسی ہے سوائے اس کے کہ یہ ایک فہرست میں لغت لپیٹ نہیں کرتا:
# portfolio.py
def construct_current_holdings(self):
"""
This constructs the dictionary which will hold the instantaneous
value of the portfolio across all symbols.
"""
d = dict( (k,v) for k, v in [(s, 0.0) for s in self.symbol_list] )
d['cash'] = self.initial_capital
d['commission'] = 0.0
d['total'] = self.initial_capital
return d
ہر
بدقسمتی سے ، بولی / مانگ کے پھیلاؤ اور لیکویڈیٹی کے مسائل کی وجہ سے
طریقہ update_timeindex نئے ہولڈنگز کی ٹریکنگ کو سنبھالتا ہے۔ یہ سب سے پہلے مارکیٹ ڈیٹا ہینڈلر سے تازہ ترین قیمتیں حاصل کرتا ہے اور موجودہ پوزیشنوں کی نمائندگی کرنے کے لئے علامتوں کا ایک نیا لغت بناتا ہے ،
# portfolio.py
def update_timeindex(self, event):
"""
Adds a new record to the positions matrix for the current
market data bar. This reflects the PREVIOUS bar, i.e. all
current market data at this stage is known (OLHCVI).
Makes use of a MarketEvent from the events queue.
"""
bars = {}
for sym in self.symbol_list:
bars[sym] = self.bars.get_latest_bars(sym, N=1)
# Update positions
dp = dict( (k,v) for k, v in [(s, 0) for s in self.symbol_list] )
dp['datetime'] = bars[self.symbol_list[0]][0][1]
for s in self.symbol_list:
dp[s] = self.current_positions[s]
# Append the current positions
self.all_positions.append(dp)
# Update holdings
dh = dict( (k,v) for k, v in [(s, 0) for s in self.symbol_list] )
dh['datetime'] = bars[self.symbol_list[0]][0][1]
dh['cash'] = self.current_holdings['cash']
dh['commission'] = self.current_holdings['commission']
dh['total'] = self.current_holdings['cash']
for s in self.symbol_list:
# Approximation to the real value
market_value = self.current_positions[s] * bars[s][0][5]
dh[s] = market_value
dh['total'] += market_value
# Append the current holdings
self.all_holdings.append(dh)
طریقہ update_positions_from_fill اس بات کا تعین کرتا ہے کہ آیا FillEvent ایک خرید یا فروخت ہے اور پھر حصص کی صحیح مقدار کو شامل / گھٹاتے ہوئے اس کے مطابق current_positions لغت کو اپ ڈیٹ کرتا ہے:
# portfolio.py
def update_positions_from_fill(self, fill):
"""
Takes a FilltEvent object and updates the position matrix
to reflect the new position.
Parameters:
fill - The FillEvent object to update the positions with.
"""
# Check whether the fill is a buy or sell
fill_dir = 0
if fill.direction == 'BUY':
fill_dir = 1
if fill.direction == 'SELL':
fill_dir = -1
# Update positions list with new quantities
self.current_positions[fill.symbol] += fill_dir*fill.quantity
متعلقہ update_holdings_from_fill مندرجہ بالا طریقہ کار کی طرح ہے لیکن اس کی بجائے ہولڈنگ ویلیوز کو اپ ڈیٹ کرتا ہے۔ بھرنے کی لاگت کی نقالی کرنے کے ل the ، مندرجہ ذیل طریقہ کار FillEvent سے وابستہ لاگت کا استعمال نہیں کرتا ہے۔ اس کا کیا سبب ہے؟ آسان الفاظ میں ، بیک ٹیسٹنگ ماحول میں بھرنے کی لاگت دراصل نامعلوم ہے اور اس طرح اس کا اندازہ لگایا جانا چاہئے۔ اس طرح بھرنے کی لاگت
ایک بار جب بھرنے کی لاگت معلوم ہوجائے تو موجودہ ہولڈنگز ، نقدی اور کل اقدار کو اپ ڈیٹ کیا جاسکتا ہے۔ مجموعی کمیشن بھی اپ ڈیٹ کیا جاتا ہے۔
# portfolio.py
def update_holdings_from_fill(self, fill):
"""
Takes a FillEvent object and updates the holdings matrix
to reflect the holdings value.
Parameters:
fill - The FillEvent object to update the holdings with.
"""
# Check whether the fill is a buy or sell
fill_dir = 0
if fill.direction == 'BUY':
fill_dir = 1
if fill.direction == 'SELL':
fill_dir = -1
# Update holdings list with new quantities
fill_cost = self.bars.get_latest_bars(fill.symbol)[0][5] # Close price
cost = fill_dir * fill_cost * fill.quantity
self.current_holdings[fill.symbol] += cost
self.current_holdings['commission'] += fill.commission
self.current_holdings['cash'] -= (cost + fill.commission)
self.current_holdings['total'] -= (cost + fill.commission)
پورٹ فولیو اے بی سی سے خالص ورچوئل اپ ڈیٹ_فِل طریقہ یہاں نافذ کیا گیا ہے۔ یہ صرف پچھلے دو طریقوں ، اپ ڈیٹ_پوزشن_فرام_فِل اور اپ ڈیٹ_ ہولڈنگز_فرام_فِل کو انجام دیتا ہے ، جن پر پہلے ہی تبادلہ خیال کیا گیا ہے:
# portfolio.py
def update_fill(self, event):
"""
Updates the portfolio current positions and holdings
from a FillEvent.
"""
if event.type == 'FILL':
self.update_positions_from_fill(event)
self.update_holdings_from_fill(event)
اگرچہ پورٹ فولیو آبجیکٹ کو فل ایونٹس کو سنبھالنا چاہئے ، لیکن اسے ایک یا زیادہ سگنل ایونٹس کی وصولی پر آرڈر ایونٹس پیدا کرنے کا بھی خیال رکھنا چاہئے۔ generate_naive_order طریقہ کسی اثاثے کو لمبا یا مختصر کرنے کے لئے صرف ایک سگنل لیتا ہے اور پھر اس طرح کے اثاثے کے 100 حصص کے لئے ایسا کرنے کا آرڈر بھیجتا ہے۔ واضح طور پر 100 ایک مشتبہ قدر ہے۔ حقیقت پسندانہ نفاذ میں یہ قدر رسک مینجمنٹ یا پوزیشن سائزنگ اوورلے کے ذریعہ طے کی جائے گی۔ تاہم ، یہ ایک NaivePortfolio ہے اور اس طرح یہ
یہ طریقہ موجودہ مقدار اور مخصوص علامت کی بنیاد پر پوزیشن کی خواہش ، مختصر اور باہر نکلنے کو سنبھالتا ہے۔ اس کے بعد متعلقہ آرڈر ایونٹ آبجیکٹ تیار کیے جاتے ہیں۔
# portfolio.py
def generate_naive_order(self, signal):
"""
Simply transacts an OrderEvent object as a constant quantity
sizing of the signal object, without risk management or
position sizing considerations.
Parameters:
signal - The SignalEvent signal information.
"""
order = None
symbol = signal.symbol
direction = signal.signal_type
strength = signal.strength
mkt_quantity = floor(100 * strength)
cur_quantity = self.current_positions[symbol]
order_type = 'MKT'
if direction == 'LONG' and cur_quantity == 0:
order = OrderEvent(symbol, order_type, mkt_quantity, 'BUY')
if direction == 'SHORT' and cur_quantity == 0:
order = OrderEvent(symbol, order_type, mkt_quantity, 'SELL')
if direction == 'EXIT' and cur_quantity > 0:
order = OrderEvent(symbol, order_type, abs(cur_quantity), 'SELL')
if direction == 'EXIT' and cur_quantity < 0:
order = OrderEvent(symbol, order_type, abs(cur_quantity), 'BUY')
return order
update_signal طریقہ کار صرف مندرجہ بالا طریقہ کار کو بلاتا ہے اور واقعات کی قطار میں پیدا کردہ آرڈر شامل کرتا ہے:
# portfolio.py
def update_signal(self, event):
"""
Acts on a SignalEvent to generate new orders
based on the portfolio logic.
"""
if event.type == 'SIGNAL':
order_event = self.generate_naive_order(event)
self.events.put(order_event)
نائن پورٹ فولیو میں حتمی طریقہ ایکویٹی وکر کی تخلیق ہے۔ اس سے آسانی سے منافع کا سلسلہ پیدا ہوتا ہے ، جو کارکردگی کے حساب کتاب کے لئے مفید ہے اور پھر ایکویٹی وکر کو فیصد پر مبنی ہونے کے لئے معمول پر لاتا ہے۔ اس طرح اکاؤنٹ کا ابتدائی سائز 1.0 کے برابر ہے:
# portfolio.py
def create_equity_curve_dataframe(self):
"""
Creates a pandas DataFrame from the all_holdings
list of dictionaries.
"""
curve = pd.DataFrame(self.all_holdings)
curve.set_index('datetime', inplace=True)
curve['returns'] = curve['total'].pct_change()
curve['equity_curve'] = (1.0+curve['returns']).cumprod()
self.equity_curve = curve
پورٹ فولیو آبجیکٹ پورے ایونٹ سے چلنے والے بیک ٹیسٹ سسٹم کا سب سے پیچیدہ پہلو ہے۔ یہاں عمل درآمد ، اگرچہ پیچیدہ ہے ، لیکن پوزیشنوں کے انتظام میں نسبتا element بنیادی ہے۔ بعد کے ورژن میں رسک مینجمنٹ اور پوزیشن سائزنگ پر غور کیا جائے گا ، جس سے حکمت عملی کی کارکردگی کا بہت زیادہ حقیقت پسندانہ خیال پیدا ہوگا۔
اگلے مضمون میں ہم ایونٹ سے چلنے والے بیک ٹیسٹر کے آخری ٹکڑے پر غور کریں گے ، یعنی ایک ایگزیکشن ہینڈلر آبجیکٹ ، جو آرڈر ایونٹ آبجیکٹ لینے اور ان سے فل ایونٹ آبجیکٹ بنانے کے لئے استعمال ہوتا ہے۔