پیتھون کے ساتھ ایونٹ سے چلنے والی بیک ٹیسٹنگ - حصہ V

مصنف:نیکی, تخلیق: 2019-03-25 15:54:16, تازہ کاری:

ایونٹ سے چلنے والی بیک ٹسٹنگ کے بارے میں پچھلے مضمون میں ہم نے اس بات پر غور کیا کہ حکمت عملی کلاس درجہ بندی کی تعمیر کیسے کی جائے۔ یہاں بیان کردہ حکمت عملیوں کا استعمال سگنل پیدا کرنے کے لئے کیا جاتا ہے ، جو کسی پورٹ فولیو آبجیکٹ کے ذریعہ آرڈر بھیجنے کے بارے میں فیصلے کرنے کے لئے استعمال ہوتے ہیں۔ جیسا کہ پہلے تھا ، یہ قدرتی طور پر ایک پورٹ فولیو تجریدی بیس کلاس (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()")

اس مضمون کا بنیادی موضوع نایف پورٹ فولیو کلاس ہے۔ یہ پوزیشن سائزنگ اور موجودہ ہولڈنگز کو سنبھالنے کے لئے ڈیزائن کیا گیا ہے ، لیکن صرف ان کو براہ راست بروکرج کو پہلے سے طے شدہ فکسڈ مقدار کے سائز کے ساتھ بھیج کر ایک dumb انداز میں ٹریڈنگ آرڈرز انجام دے گا۔ یہ سب غیر حقیقی مفروضے ہیں ، لیکن وہ اس بات کا خاکہ پیش کرنے میں مدد کرتے ہیں کہ پورٹ فولیو آرڈر مینجمنٹ سسٹم (او ایم ایس) ایونٹ سے چلنے والے انداز میں کیسے کام کرتا ہے۔

NaivePortfolio کو ابتدائی سرمایہ کی قیمت کی ضرورت ہوتی ہے، جسے میں نے ڈیفالٹ کے طور پر 100،000 امریکی ڈالر مقرر کیا ہے۔ اس کے ساتھ ساتھ شروع ہونے کی تاریخ کی ضرورت ہوتی ہے۔

پورٹ فولیو میں all_positions اور current_positions ممبران شامل ہیں۔ سابقہ مارکیٹ ڈیٹا ایونٹ کے ٹائم اسٹیمپ پر ریکارڈ کی گئی تمام پچھلی پوزیشنوں کی ایک فہرست اسٹور کرتا ہے۔ پوزیشن صرف اثاثہ کی مقدار ہے۔ منفی پوزیشنوں کا مطلب یہ ہے کہ اثاثہ مختصر ہو گیا ہے۔ آخری ممبر ایک لغت اسٹور کرتا ہے جس میں مارکیٹ بار کی آخری تازہ کاری کے لئے موجودہ پوزیشنیں شامل ہیں۔

پوزیشن ممبروں کے علاوہ پورٹ فولیو ہولڈنگز کو اسٹور کرتا ہے ، جو ان پوزیشنوں کی موجودہ مارکیٹ ویلیو کی وضاحت کرتے ہیں۔ موجودہ مارکیٹ ویلیو اس معاملے میں موجودہ مارکیٹ بار سے حاصل ہونے والی اختتامی قیمت کا مطلب ہے ، جو واضح طور پر ایک تخمینہ ہے ، لیکن فی الحال کافی معقول ہے۔ all_holdings تمام علامت ہولڈنگز کی تاریخی فہرست کو اسٹور کرتا ہے ، جبکہ current_holdings تمام علامت ہولڈنگز کی قیمتوں کا تازہ ترین لغت اسٹور کرتا ہے۔

# 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

ہر دل کی دھڑکن پر ، یعنی جب بھی ڈیٹا ہینڈلر آبجیکٹ سے مارکیٹ کے نئے اعداد و شمار کی درخواست کی جاتی ہے ، تو پورٹ فولیو کو تمام پوزیشنوں کی موجودہ مارکیٹ ویلیو کو اپ ڈیٹ کرنا ہوگا۔ براہ راست تجارتی منظرنامے میں یہ معلومات براہ راست بروکرج سے ڈاؤن لوڈ اور تجزیہ کی جاسکتی ہیں ، لیکن بیک ٹیسٹنگ کے نفاذ کے لئے ان اقدار کا دستی طور پر حساب لگانا ضروری ہے۔

بدقسمتی سے ، بولی / مانگ کے پھیلاؤ اور لیکویڈیٹی کے مسائل کی وجہ سے موجودہ مارکیٹ ویلیو جیسی کوئی چیز نہیں ہے۔ لہذا ، قیمت سے منعقدہ اثاثہ کی مقدار کو ضرب دے کر اس کا تخمینہ لگانا ضروری ہے۔ یہاں میں نے جو نقطہ نظر اپنایا ہے وہ آخری بار موصول ہونے والی اختتامی قیمت کا استعمال کرنا ہے۔ ایک انٹرا ڈے حکمت عملی کے ل this یہ نسبتا حقیقت پسندانہ ہے۔ روزانہ کی حکمت عملی کے ل this یہ کم حقیقت پسندانہ ہے کیونکہ افتتاحی قیمت اختتامی قیمت سے کافی حد تک مختلف ہوسکتی ہے۔

طریقہ update_timeindex نئے ہولڈنگز کی ٹریکنگ کو سنبھالتا ہے۔ یہ سب سے پہلے مارکیٹ ڈیٹا ہینڈلر سے تازہ ترین قیمتیں حاصل کرتا ہے اور موجودہ پوزیشنوں کی نمائندگی کرنے کے لئے علامتوں کا ایک نیا لغت بناتا ہے ، new پوزیشنوں کو current پوزیشنوں کے برابر ترتیب دے کر۔ یہ صرف اس وقت تبدیل ہوجاتے ہیں جب فل ایونٹ حاصل کیا جاتا ہے ، جسے بعد میں پورٹ فولیو میں سنبھالا جاتا ہے۔ اس کے بعد یہ طریقہ موجودہ پوزیشنوں کے اس سیٹ کو all_positions فہرست میں شامل کرتا ہے۔ اس کے بعد ہولڈنگز کو اسی طرح سے اپ ڈیٹ کیا جاتا ہے ، اس استثناء کے ساتھ کہ مارکیٹ ویلیو کو موجودہ پوزیشنوں کو تازہ ترین بار (self.current_positions[s] * bars[s][0][5]) کی بندش کی قیمت سے ضرب کرکے دوبارہ شمار کیا جاتا ہے۔ آخر میں نئے ہولڈنگز کو all_holdings میں شامل کیا جاتا ہے:

# 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 ہے اور اس طرح یہ naively تمام آرڈرز کو براہ راست سگنلز سے بھیجتا ہے ، بغیر کسی رسک سسٹم کے۔

یہ طریقہ موجودہ مقدار اور مخصوص علامت کی بنیاد پر پوزیشن کی خواہش ، مختصر اور باہر نکلنے کو سنبھالتا ہے۔ اس کے بعد متعلقہ آرڈر ایونٹ آبجیکٹ تیار کیے جاتے ہیں۔

# 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 بنیادی ہے۔ بعد کے ورژن میں رسک مینجمنٹ اور پوزیشن سائزنگ پر غور کیا جائے گا ، جس سے حکمت عملی کی کارکردگی کا بہت زیادہ حقیقت پسندانہ خیال پیدا ہوگا۔

اگلے مضمون میں ہم ایونٹ سے چلنے والے بیک ٹیسٹر کے آخری ٹکڑے پر غور کریں گے ، یعنی ایک ایگزیکشن ہینڈلر آبجیکٹ ، جو آرڈر ایونٹ آبجیکٹ لینے اور ان سے فل ایونٹ آبجیکٹ بنانے کے لئے استعمال ہوتا ہے۔


مزید