def __init__(self, initial_cash, events_queue, price_handler,
                 position_sizer, risk_manager):
        """
        The PortfolioHandler is designed to interact with the 
        backtesting or live trading overall event-driven
        architecture. It exposes two methods, on_signal and
        on_fill, which handle how SignalEvent and FillEvent
        objects are dealt with.

        Each PortfolioHandler contains a Portfolio object,
        which stores the actual Position objects. 

        The PortfolioHandler takes a handle to a PositionSizer
        object which determines a mechanism, based on the current
        Portfolio, as to how to size a new Order.

        The PortfolioHandler also takes a handle to the 
        RiskManager, which is used to modify any generated 
        Orders to remain in line with risk parameters.
        """
        self.initial_cash = initial_cash
        self.events_queue = events_queue
        self.price_handler = price_handler
        self.position_sizer = position_sizer
        self.risk_manager = risk_manager
        self.portfolio = Portfolio(price_handler, initial_cash)
Example #2
0
 def setUp(self):
     """
     Set up the Portfolio object that will store the 
     collection of Position objects, supplying it with
     $500,000.00 USD in initial cash.
     """
     ph = PriceHandlerMock()
     cash = Decimal("500000.00")
     self.portfolio = Portfolio(ph, cash)
    def __init__(
        self, initial_cash, events_queue, 
        price_handler, position_sizer, risk_manager
    ):
        """
        The PortfolioHandler is designed to interact with the 
        backtesting or live trading overall event-driven
        architecture. It exposes two methods, on_signal and
        on_fill, which handle how SignalEvent and FillEvent
        objects are dealt with.

        Each PortfolioHandler contains a Portfolio object,
        which stores the actual Position objects. 

        The PortfolioHandler takes a handle to a PositionSizer
        object which determines a mechanism, based on the current
        Portfolio, as to how to size a new Order.

        The PortfolioHandler also takes a handle to the 
        RiskManager, which is used to modify any generated 
        Orders to remain in line with risk parameters.
        """
        self.initial_cash = initial_cash
        self.events_queue = events_queue
        self.price_handler = price_handler
        self.position_sizer = position_sizer
        self.risk_manager = risk_manager
        self.portfolio = Portfolio(price_handler, initial_cash)
Example #4
0
	def test_calculating_statistics(self):
		"""
		Purchase/sell multiple lots of AMZN, GOOG
		at various prices/commissions to ensure
		the arithmetic in calculating equity, drawdowns
		and sharpe ratio is correct.
		"""
		# Create Statistics object
		price_handler = PriceHandlerMock()
		self.portfolio = Portfolio(price_handler, Decimal("500000.00"))
		
		portfolio_handler = PortfolioHandlerMock(self.portfolio)
		statistics=SimpleStatistics(portfolio_handler)


		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "BOT", "AMZN", 100, 
		    Decimal("566.56"), Decimal("1.00")
		)
		t="2000-01-01 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499807.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("193.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("-0.0386"))

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "BOT", "AMZN", 200, 
		    Decimal
		    ("566.395"), Decimal("1.00")
		)
		t="2000-01-02 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499455.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("545.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("-0.0705"))

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "BOT", "GOOG", 200, 
		    Decimal("707.50"), Decimal("1.00")
		)
		t="2000-01-03 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499046.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("954.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("-0.0820"))

		
		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "AMZN", 100, 
		    Decimal("565.83"), Decimal("1.00")
		)
		t="2000-01-04 00:00:00"
		statistics.update(t);
		self.assertEqual(statistics.equity[t], Decimal("499164.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("836.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0236"))

		
		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "BOT", "GOOG", 200, 
		    Decimal("705.545"), Decimal("1.00")
		)
		t="2000-01-05 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499146.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("854.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("-0.0036"))


		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "AMZN", 200, 
		    Decimal("565.59"), Decimal("1.00")
		)
		t="2000-01-06 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499335.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("665.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0379"))
		

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "GOOG", 100, 
		    Decimal("707.92"), Decimal("1.00")
		)
		t="2000-01-07 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499580.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("420.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0490"))

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "GOOG", 100, 
		    Decimal("707.90"), Decimal("0.00")
		)
		t="2000-01-08 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("499824.00"))
		self.assertEqual(statistics.drawdowns[t], Decimal("176.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0488"))
		

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "GOOG", 100, 
		    Decimal("707.92"), Decimal("0.50")
		)
		t="2000-01-09 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("500069.50"))
		self.assertEqual(statistics.drawdowns[t], Decimal("00.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0491"))
		

		# Perform transaction and test statistics at this tick
		self.portfolio.transact_position(
		    "SLD", "GOOG", 100, 
		    Decimal("707.78"), Decimal("1.00")
		)
		t="2000-01-10 00:00:00"
		statistics.update(t)
		self.assertEqual(statistics.equity[t], Decimal("500300.50"))
		self.assertEqual(statistics.drawdowns[t], Decimal("00.00"))
		self.assertEqual(statistics.equity_returns[t], Decimal("0.0462"))

		# Test that results are calculated correctly.
		results=statistics.get_results()
		self.assertEqual(results["max_drawdown"], Decimal("954.00"))
		self.assertEqual(results["max_drawdown_pct"], Decimal("0.1908"))
		self.assertEqual(results["sharpe"], Decimal("1.8353"))
class PortfolioHandler(object):
    def __init__(self, initial_cash, events_queue, price_handler,
                 position_sizer, risk_manager):
        """
        The PortfolioHandler is designed to interact with the 
        backtesting or live trading overall event-driven
        architecture. It exposes two methods, on_signal and
        on_fill, which handle how SignalEvent and FillEvent
        objects are dealt with.

        Each PortfolioHandler contains a Portfolio object,
        which stores the actual Position objects. 

        The PortfolioHandler takes a handle to a PositionSizer
        object which determines a mechanism, based on the current
        Portfolio, as to how to size a new Order.

        The PortfolioHandler also takes a handle to the 
        RiskManager, which is used to modify any generated 
        Orders to remain in line with risk parameters.
        """
        self.initial_cash = initial_cash
        self.events_queue = events_queue
        self.price_handler = price_handler
        self.position_sizer = position_sizer
        self.risk_manager = risk_manager
        self.portfolio = Portfolio(price_handler, initial_cash)

    def _create_order_from_signal(self, signal_event):
        """
        Take a SignalEvent object and use it to form a
        SuggestedOrder object. These are not OrderEvent objects,
        as they have yet to be sent to the RiskManager object.
        At this stage they are simply "suggestions" that the
        RiskManager will either verify, modify or eliminate.
        """
        order = SuggestedOrder(signal_event.ticker, signal_event.action)
        return order

    def _place_orders_onto_queue(self, order_list):
        """
        Once the RiskManager has verified, modified or eliminated
        any order objects, they are placed onto the events queue,
        to ultimately be executed by the ExecutionHandler.
        """
        for order_event in order_list:
            self.events_queue.put(order_event)

    def _convert_fill_to_portfolio_update(self, fill_event):
        """
        Upon receipt of a FillEvent, the PortfolioHandler converts
        the event into a transaction that gets stored in the Portfolio
        object. This ensures that the broker and the local portfolio
        are "in sync".

        In addition, for backtesting purposes, the portfolio value can
        be reasonably estimated in a realistic manner, simply by 
        modifying how the ExecutionHandler object handles slippage,
        transaction costs, liquidity and market impact.
        """
        action = fill_event.action
        ticker = fill_event.ticker
        quantity = fill_event.quantity
        price = fill_event.price
        commission = fill_event.commission
        # Create or modify the position from the fill info
        self.portfolio.transact_position(action, ticker, quantity, price,
                                         commission)

    def on_signal(self, signal_event):
        """
        This is called by the backtester or live trading architecture
        to form the initial orders from the SignalEvent. 

        These orders are sized by the PositionSizer object and then
        sent to the RiskManager to verify, modify or eliminate.

        Once received from the RiskManager they are converted into
        full OrderEvent objects and sent back to the events queue.
        """
        # Create the initial order list from a signal event
        initial_order = self._create_order_from_signal(signal_event)
        # Size the quantity of the initial order
        sized_order = self.position_sizer.size_order(self.portfolio,
                                                     initial_order)
        # Refine or eliminate the order via the risk manager overlay
        order_events = self.risk_manager.refine_orders(self.portfolio,
                                                       sized_order)
        # Place orders onto events queue
        self._place_orders_onto_queue(order_events)

    def on_fill(self, fill_event):
        """
        This is called by the backtester or live trading architecture
        to take a FillEvent and update the Portfolio object with new
        or modified Positions.

        In a backtesting environment these FillEvents will be simulated
        by a model representing the execution, whereas in live trading
        they will come directly from a brokerage (such as Interactive
        Brokers).
        """
        self._convert_fill_to_portfolio_update(fill_event)

    def update_portfolio_value(self):
        """
        Update the portfolio to reflect current market value as
        based on last bid/ask of each ticker.
        """
        self.portfolio._update_portfolio()
Example #6
0
class PriceHandlerMock(object):
    def __init__(self):
        pass

    def get_best_bid_ask(self, ticker):
        prices = {
            "GOOG": (Decimal("762.15"), Decimal("762.15")),
            "AMZN": (Decimal("660.51"), Decimal("660.51")),
        }
        return prices[ticker]


ph = PriceHandlerMock()
cash = Decimal("500000.00")
port = Portfolio(ph, cash)

port.add_position("BOT", "GOOG", 100, Decimal("761.75"), Decimal("1.00"))
port.add_position("BOT", "AMZN", 100, Decimal("660.86"), Decimal("1.00"))
port.modify_position("BOT", "AMZN", 100, Decimal("660.14"), Decimal("1.00"))
port.modify_position("BOT", "AMZN", 150, Decimal("660.20"), Decimal("1.00"))
port.modify_position("SLD", "AMZN", 300, Decimal("659.713"), Decimal("1.50"))

print("CASH:", port.cur_cash)
print("EQUITY:", port.equity)
print("UPnL:", port.unrealised_pnl)
print("PnL:", port.realised_pnl)
pprint.pprint(port.positions)
print("")

for p in port.positions:
Example #7
0
class TestAmazonGooglePortfolio(unittest.TestCase):
    """
    Test a portfolio consisting of Amazon and 
    Google/Alphabet with various orders to create 
    round-trips for both.

    These orders were carried out in the Interactive Brokers
    demo account and checked for cash, equity and PnL
    equality.
    """
    def setUp(self):
        """
        Set up the Portfolio object that will store the 
        collection of Position objects, supplying it with
        $500,000.00 USD in initial cash.
        """
        ph = PriceHandlerMock()
        cash = Decimal("500000.00")
        self.portfolio = Portfolio(ph, cash)

    def test_calculate_round_trip(self):
        """
        Purchase/sell multiple lots of AMZN and GOOG
        at various prices/commissions to check the 
        arithmetic and cost handling.
        """
        # Buy 300 of AMZN over two transactions
        self.portfolio.transact_position(
            "BOT", "AMZN", 100, 
            Decimal("566.56"), Decimal("1.00")
        )
        self.portfolio.transact_position(
            "BOT", "AMZN", 200, 
            Decimal("566.395"), Decimal("1.00")
        )
        # Buy 200 GOOG over one transaction
        self.portfolio.transact_position(
            "BOT", "GOOG", 200, 
            Decimal("707.50"), Decimal("1.00")
        )
        # Add to the AMZN position by 100 shares
        self.portfolio.transact_position(
            "SLD", "AMZN", 100, 
            Decimal("565.83"), Decimal("1.00")
        )
        # Add to the GOOG position by 200 shares
        self.portfolio.transact_position(
            "BOT", "GOOG", 200, 
            Decimal("705.545"), Decimal("1.00")
        )
        # Sell 200 of the AMZN shares
        self.portfolio.transact_position(
            "SLD", "AMZN", 200, 
            Decimal("565.59"), Decimal("1.00")
        )
        # Multiple transactions bundled into one (in IB)
        # Sell 300 GOOG from the portfolio
        self.portfolio.transact_position(
            "SLD", "GOOG", 100, 
            Decimal("704.92"), Decimal("1.00")
        )
        self.portfolio.transact_position(
            "SLD", "GOOG", 100, 
            Decimal("704.90"), Decimal("0.00")
        )
        self.portfolio.transact_position(
            "SLD", "GOOG", 100, 
            Decimal("704.92"), Decimal("0.50")
        )
        # Finally, sell the remaining GOOG 100 shares
        self.portfolio.transact_position(
            "SLD", "GOOG", 100, 
            Decimal("704.78"), Decimal("1.00")
        )

        # The figures below are derived from Interactive Brokers
        # demo account using the above trades with prices provided
        # by their demo feed. 
        self.assertEqual(self.portfolio.cur_cash, Decimal("499100.50"))
        self.assertEqual(self.portfolio.equity, Decimal("499100.50"))
        self.assertEqual(self.portfolio.unrealised_pnl, Decimal("0.00"))
        self.assertEqual(self.portfolio.realised_pnl, Decimal("-899.50"))
class PortfolioHandler(object):
    def __init__(
        self, initial_cash, events_queue, 
        price_handler, position_sizer, risk_manager
    ):
        """
        The PortfolioHandler is designed to interact with the 
        backtesting or live trading overall event-driven
        architecture. It exposes two methods, on_signal and
        on_fill, which handle how SignalEvent and FillEvent
        objects are dealt with.

        Each PortfolioHandler contains a Portfolio object,
        which stores the actual Position objects. 

        The PortfolioHandler takes a handle to a PositionSizer
        object which determines a mechanism, based on the current
        Portfolio, as to how to size a new Order.

        The PortfolioHandler also takes a handle to the 
        RiskManager, which is used to modify any generated 
        Orders to remain in line with risk parameters.
        """
        self.initial_cash = initial_cash
        self.events_queue = events_queue
        self.price_handler = price_handler
        self.position_sizer = position_sizer
        self.risk_manager = risk_manager
        self.portfolio = Portfolio(price_handler, initial_cash)

    def _create_order_from_signal(self, signal_event):
        """
        Take a SignalEvent object and use it to form a
        SuggestedOrder object. These are not OrderEvent objects,
        as they have yet to be sent to the RiskManager object.
        At this stage they are simply "suggestions" that the
        RiskManager will either verify, modify or eliminate.
        """
        order = SuggestedOrder(
            signal_event.ticker, signal_event.action
        )
        return order

    def _place_orders_onto_queue(self, order_list):
        """
        Once the RiskManager has verified, modified or eliminated
        any order objects, they are placed onto the events queue,
        to ultimately be executed by the ExecutionHandler.
        """
        for order_event in order_list:
            self.events_queue.put(order_event)

    def _convert_fill_to_portfolio_update(self, fill_event):
        """
        Upon receipt of a FillEvent, the PortfolioHandler converts
        the event into a transaction that gets stored in the Portfolio
        object. This ensures that the broker and the local portfolio
        are "in sync".

        In addition, for backtesting purposes, the portfolio value can
        be reasonably estimated in a realistic manner, simply by 
        modifying how the ExecutionHandler object handles slippage,
        transaction costs, liquidity and market impact.
        """
        action = fill_event.action       
        ticker = fill_event.ticker
        quantity = fill_event.quantity
        price = fill_event.price
        commission = fill_event.commission
        # Create or modify the position from the fill info
        self.portfolio.transact_position(
            action, ticker, quantity, 
            price, commission
        )

    def on_signal(self, signal_event):
        """
        This is called by the backtester or live trading architecture
        to form the initial orders from the SignalEvent. 

        These orders are sized by the PositionSizer object and then
        sent to the RiskManager to verify, modify or eliminate.

        Once received from the RiskManager they are converted into
        full OrderEvent objects and sent back to the events queue.
        """
        # Create the initial order list from a signal event
        initial_order = self._create_order_from_signal(signal_event)
        # Size the quantity of the initial order
        sized_order = self.position_sizer.size_order(
            self.portfolio, initial_order
        )
        # Refine or eliminate the order via the risk manager overlay
        order_events = self.risk_manager.refine_orders(
            self.portfolio, sized_order
        )
        # Place orders onto events queue
        self._place_orders_onto_queue(order_events)

    def on_fill(self, fill_event):
        """
        This is called by the backtester or live trading architecture
        to take a FillEvent and update the Portfolio object with new
        or modified Positions.

        In a backtesting environment these FillEvents will be simulated
        by a model representing the execution, whereas in live trading
        they will come directly from a brokerage (such as Interactive
        Brokers).
        """
        self._convert_fill_to_portfolio_update(fill_event)