コード例 #1
0
class Portfolio(object):

    def __init__(self, dataHandler,
                 events,
                 startDate,
                 assets,
                 initialCapital=100000.0,
                 benchmark=None,
                 portfolioType=PortfolioType.CashManageable):
        self.dataHandler = dataHandler
        self.events = events
        self.allTradableAssets = self.dataHandler.allTradableAssets
        self.startDate = startDate
        self.initialCapital = initialCapital
        self.benchmark = benchmark
        self.assets = assets
        self.positionsBook = StocksPositionsBook(assets)
        self.portfolioType = portfolioType

        self.allPositions = self.constructAllPositions()
        self.currentPosition = defaultdict(int, [(s, 0) for s in self.allTradableAssets])

        self.allHoldings = [] # self.constructAllHoldings()
        self.currentHoldings = self.constructCurrentHoldings()

        self.orderBook = None

        vp_settings.set_source(Settings.data_source)

    def constructAllPositions(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.allTradableAssets])
        d['datetime'] = self.startDate
        return [d]

    def constructAllHoldings(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.allTradableAssets])
        d['datetime'] = self.startDate
        d['cash'] = self.initialCapital
        d['margin'] = 0.0
        d['commission'] = 0.0
        d['pnl'] = 0.
        d['total'] = self.initialCapital
        return [d]

    def constructCurrentHoldings(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.allTradableAssets])
        d['datetime'] = self.startDate
        d['cash'] = self.initialCapital
        d['margin'] = 0.0
        d['commission'] = 0.0
        d['pnl'] = 0.
        d['total'] = self.initialCapital
        return d

    def updateTimeindex(self):
        latestDatetime = self.dataHandler.currentTimeIndex

        dh = dict((s, 0) for s in self.allTradableAssets)
        dh['datetime'] = latestDatetime
        dh['cash'] = self.currentHoldings['cash']
        dh['commission'] = self.currentHoldings['commission']
        dh['margin'] = 0.
        dh['total'] = self.currentHoldings['total']
        dh['pnl'] = self.currentHoldings['pnl']

        for s in self.allTradableAssets:
            bookValue = 0.
            bookPnL = 0.
            margin = 0.
            if self.currentPosition[s]:
                currentCost = self.dataHandler.getLatestBarValue(s, 'close') * self.assets[s].multiplier
                bookValue, bookPnL, margin = self.positionsBook.getBookValueAndBookPnL(s, currentCost)
            dh[s] = bookValue
            dh['margin'] += margin
            dh['pnl'] += bookPnL
            dh['total'] += bookPnL

        self.allHoldings.append(dh)

    def updatePositionFromFill(self, fill):
        fillDir = fill.direction
        if fillDir == OrderDirection.BUY or fillDir == OrderDirection.BUY_BACK:
            self.currentPosition[fill.symbol] += fill.quantity
        else:
            self.currentPosition[fill.symbol] -= fill.quantity

    def updateHoldingsFromFill(self, fill, pnl):
        self.currentHoldings[fill.symbol] += fill.fillCost
        self.currentHoldings['commission'] += fill.commission
        if not isClose(fill.fillCost, 0.):
            self.currentHoldings['cash'] -= (fill.fillCost + fill.commission)
        else:
            self.currentHoldings['cash'] += (pnl - fill.commission)
        self.currentHoldings['pnl'] += pnl - fill.commission
        self.currentHoldings['total'] += pnl - fill.commission

        fillDir = fill.direction
        if fillDir == OrderDirection.BUY_BACK or fillDir == OrderDirection.SELL_SHORT:
            self.currentHoldings['margin'] -= fill.fillCost

    def updateFill(self, event):
        posClosed, posOpen, pnl = self.positionsBook.updatePositionsByFill(event)
        self.updatePositionFromFill(event)
        self.updateHoldingsFromFill(event, pnl)

        self.filledBook.updateFromFillEvent(event)

    def cancelOrders(self, timeIndex, posBook):
        self.orderBook.cancelOrders(timeIndex, posBook)

    def generateNaiveOrder(self, signal):
        order = None

        symbol = signal.symbol
        direction = signal.signalType

        mktQuantity = signal.quantity
        curQuantity = self.currentPosition[symbol]
        orderType = 'MKT'

        if direction == 'LONG' and curQuantity == 0:
            order = OrderEvent(symbol, orderType, mktQuantity, 1)
        if direction == 'SHORT' and curQuantity == 0:
            order = OrderEvent(symbol, orderType, mktQuantity, -1)

        if direction == 'EXIT' and curQuantity > 0:
            order = OrderEvent(symbol, orderType, abs(curQuantity), -1)
        if direction == 'EXIT' and curQuantity < 0:
            order = OrderEvent(symbol, orderType, abs(curQuantity), 1)

        return order

    def updateSignal(self, event):
        if event.type == 'SIGNAL':
            orderEvent = self.generateNaiveOrder(event)
            self.events.put(orderEvent)

    def _createFullNotionalEquityCurve(self, curve):
        rawpos = curve.drop(['cash', 'commission', 'total', 'margin', 'pnl'], axis=1)
        notionals = rawpos.abs().sum(axis=1).fillna(0)
        notional_directions = rawpos.sum(axis=1).fillna(0).apply(sign)
        pnldiffs = curve['pnl'].diff()
        # to handl the case when pnl is shown in null notional day
        cumPnL = 0.
        returns = []
        for notional, pnldiff, direction in zip(notionals, pnldiffs, notional_directions):
            if not isClose(notional):
                r = direction * np.log(direction * notional / (direction * notional - pnldiff - cumPnL))
                returns.append(r)
                cumPnL = 0.
            elif not np.isnan(pnldiff):
                returns.append(0.)
                cumPnL += pnldiff
            else:
                returns.append(0.)
        curve['return'] = returns
        curve.fillna(0., inplace=True)
        curve['equity_curve'] = np.exp(curve['return'].cumsum())

    def createEquityCurveDataframe(self):
        curve = pd.DataFrame(self.allHoldings)
        curve.set_index('datetime', inplace=True)
        if self.portfolioType == PortfolioType.FullNotional:
            self._createFullNotionalEquityCurve(curve)
        else:
            curve['return'] = np.log(curve['total'] / curve['total'].shift(1))
            curve.dropna(inplace=True)
            curve['equity_curve'] = np.exp(curve['return'].cumsum())
        self.equityCurve = curve.dropna()

    def outputSummaryStats(self, curve, other_curves, plot):
        returns = curve['return']
        if hasattr(self.dataHandler, "benchmarkData"):
            benchmarkReturns = self.dataHandler.benchmarkData['return']
            benchmarkReturns.name = self.benchmark
        else:
            benchmarkReturns = None

        perf_metric, perf_df, rollingRisk = createPerformanceTearSheet(returns=returns,
                                                                       benchmarkReturns=benchmarkReturns,
                                                                       other_curves=other_curves,
                                                                       plot=plot)

        if self.portfolioType == PortfolioType.FullNotional:
            positons = curve.drop(['cash', 'commission', 'total', 'return', 'margin', 'equity_curve', 'pnl'], axis=1)
        else:
            positons = curve.drop(['commission', 'total', 'return', 'margin', 'equity_curve', 'pnl'], axis=1)
        aggregated_positons = createPostionTearSheet(positons, plot=plot)

        transactions = extractTransactionFromFilledBook(self.filledBook.view())

        if hasattr(self.dataHandler, "_freq"):

            if self.dataHandler._freq == 0:
                freq = 'M'
            else:
                freq = 'D'

            turnover_rate = createTranscationTearSheet(transactions, positons, freq=freq, plot=plot)
        else:
            turnover_rate = createTranscationTearSheet(transactions, positons, plot=plot)

        if plot:
            plt.show()

        return perf_metric, perf_df, rollingRisk, aggregated_positons, transactions, turnover_rate
コード例 #2
0
ファイル: Portfolio.py プロジェクト: rlcjj/AlgoTrading
class Portfolio(object):

    def __init__(self, dataHandler,
                 events,
                 startDate,
                 assets,
                 initialCapital=100000.0,
                 benchmark=None,
                 portfolioType=PortfolioType.CashManageable):
        self.dataHandler = dataHandler
        self.events = events
        self.tradableAssets = self.dataHandler.tradableAssets
        self.startDate = startDate
        self.initialCapital = initialCapital
        self.benchmark = benchmark
        self.assets = assets
        self.positionsBook = StocksPositionsBook(assets)
        self.portfolioType = portfolioType

        self.allPositions = self.constructAllPositions()
        self.currentPosition = defaultdict(int, [(s, 0) for s in self.tradableAssets])

        self.allHoldings = self.constructAllHoldings()
        self.currentHoldings = self.constructCurrentHoldings()

        vp_settings.set_source(Settings.data_source)

    def constructAllPositions(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.tradableAssets])
        d['datetime'] = self.startDate
        return [d]

    def constructAllHoldings(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.tradableAssets])
        d['datetime'] = self.startDate
        d['cash'] = self.initialCapital
        d['margin'] = 0.0
        d['commission'] = 0.0
        d['pnl'] = 0.
        d['total'] = self.initialCapital
        return [d]

    def constructCurrentHoldings(self):
        d = dict((k, v) for k, v in [(s, 0) for s in self.tradableAssets])
        d['datetime'] = self.startDate
        d['cash'] = self.initialCapital
        d['margin'] = 0.0
        d['commission'] = 0.0
        d['pnl'] = 0.
        d['total'] = self.initialCapital
        return d

    def updateTimeindex(self):
        latestDatetime = self.dataHandler.currentTimeIndex

        dh = dict((s, 0) for s in self.tradableAssets)
        dh['datetime'] = latestDatetime
        dh['cash'] = self.currentHoldings['cash']
        dh['commission'] = self.currentHoldings['commission']
        dh['margin'] = self.currentHoldings['margin']
        dh['total'] = self.currentHoldings['total']
        dh['pnl'] = self.currentHoldings['pnl']

        for s in self.tradableAssets:
            bookValue = 0.
            bookPnL = 0.
            if self.currentPosition[s]:
                currentCost = self.dataHandler.getLatestBarValue(s, 'close') * self.assets[s].multiplier
                bookValue, bookPnL = self.positionsBook.getBookValueAndBookPnL(s, currentCost)
            dh[s] = bookValue
            dh['pnl'] += bookPnL
            dh['total'] += bookPnL

        self.allHoldings.append(dh)

    def updatePositionFromFill(self, fill):
        fillDir = fill.direction
        self.currentPosition[fill.symbol] += fillDir * fill.quantity

    def updateHoldingsFromFill(self, fill, pnl):
        self.currentHoldings[fill.symbol] += fill.fillCost
        self.currentHoldings['commission'] += fill.commission
        if not isClose(fill.fillCost, 0.):
            self.currentHoldings['cash'] -= (fill.fillCost + fill.commission)
        else:
            self.currentHoldings['cash'] += (pnl - fill.commission)
        self.currentHoldings['pnl'] += pnl - fill.commission
        self.currentHoldings['total'] += pnl - fill.commission

    def updateFill(self, event):
        posClosed, posOpen, pnl = self.positionsBook.updatePositionsByFill(event)
        self.updatePositionFromFill(event)
        self.updateHoldingsFromFill(event, pnl)

        self.filledBook.updateFromFillEvent(event)

    def generateNaiveOrder(self, signal):
        order = None

        symbol = signal.symbol
        direction = signal.signalType

        mktQuantity = signal.quantity
        curQuantity = self.currentPosition[symbol]
        orderType = 'MKT'

        if direction == 'LONG' and curQuantity == 0:
            order = OrderEvent(symbol, orderType, mktQuantity, 1)
        if direction == 'SHORT' and curQuantity == 0:
            order = OrderEvent(symbol, orderType, mktQuantity, -1)

        if direction == 'EXIT' and curQuantity > 0:
            order = OrderEvent(symbol, orderType, abs(curQuantity), -1)
        if direction == 'EXIT' and curQuantity < 0:
            order = OrderEvent(symbol, orderType, abs(curQuantity), 1)

        return order

    def updateSignal(self, event):
        if event.type == 'SIGNAL':
            orderEvent = self.generateNaiveOrder(event)
            self.events.put(orderEvent)

    def _createFullNotionalEquityCurve(self, curve):
        rawpos = curve.drop(['cash', 'commission', 'total', 'margin', 'pnl'], axis=1)
        notionals = rawpos.abs().sum(axis=1).fillna(0)
        notional_directions = rawpos.sum(axis=1).fillna(0).apply(sign)
        pnldiffs = curve['pnl'].diff()
        # to handl the case when pnl is shown in null notional day
        cumPnL = 0.
        returns = []
        for notional, pnldiff, direction in zip(notionals, pnldiffs, notional_directions):
            if not isClose(notional):
                r = direction * np.log(direction * notional / (direction * notional - pnldiff - cumPnL))
                returns.append(r)
                cumPnL = 0.
            elif not np.isnan(pnldiff):
                returns.append(0.)
                cumPnL += pnldiff
            else:
                returns.append(0.)
        curve['return'] = returns
        curve.fillna(0., inplace=True)
        curve['equity_curve'] = np.exp(curve['return'].cumsum())

    def createEquityCurveDataframe(self):
        curve = pd.DataFrame(self.allHoldings)
        curve.set_index('datetime', inplace=True)
        if self.portfolioType == PortfolioType.FullNotional:
            self._createFullNotionalEquityCurve(curve)
        else:
            curve['return'] = np.log(curve['total'] / curve['total'].shift(1))
            curve.dropna(inplace=True)
            curve['equity_curve'] = np.exp(curve['return'].cumsum())
        self.equityCurve = curve.dropna()

    def outputSummaryStats(self, curve, plot):
        returns = curve['return']
        if hasattr(self.dataHandler, "benchmarkData"):
            benchmarkReturns = self.dataHandler.benchmarkData['return']
            benchmarkReturns.name = self.benchmark
        else:
            benchmarkReturns = None
        perf_metric, perf_df, rollingRisk = createPerformanceTearSheet(returns=returns, benchmarkReturns=benchmarkReturns, plot=plot)

        if self.portfolioType == PortfolioType.FullNotional:
            positons = curve.drop(['cash', 'commission', 'total', 'return', 'margin', 'equity_curve', 'pnl'], axis=1)
        else:
            positons = curve.drop(['commission', 'total', 'return', 'margin', 'equity_curve', 'pnl'], axis=1)
        aggregated_positons = createPostionTearSheet(positons, plot=plot)

        transactions = extractTransactionFromFilledBook(self.filledBook.view())
        turnover_rate = createTranscationTearSheet(transactions, positons, plot=plot)

        if plot:
            plt.show()

        return perf_metric, perf_df, rollingRisk, aggregated_positons, transactions, turnover_rate