Пример #1
0
    def transact_asset(self, txn):
        """
        Adjusts positions to account for a transaction.
        """
        if txn.dt < self.current_dt:
            raise ValueError('Transaction datetime (%s) is earlier than '
                             'current portfolio datetime (%s). Cannot '
                             'transact assets.' % (txn.dt, self.current_dt))
        self.current_dt = txn.dt

        txn_share_cost = txn.price * txn.quantity
        txn_total_cost = txn_share_cost + txn.commission

        if txn_total_cost > self.total_cash:
            raise ValueError('Not enough cash in the portfolio to '
                             'carry out transaction. Transaction cost of %s '
                             'exceeds remaining cash of %s.' %
                             (txn_total_cost, self.total_cash))

        self.pos_handler.transact_position(txn)
        cash_position = self.pos_handler.positions[self.cash_position_key]

        new_cash_quantity = cash_position.quantity - txn_total_cost
        self.pos_handler.update_position(self.cash_position_key,
                                         quantity=new_cash_quantity,
                                         current_dt=self.current_dt)

        # Form Portfolio history details
        direction = "LONG" if txn.direction > 0 else "SHORT"
        description = "%s %s %s %0.2f %s" % (
            direction, txn.quantity, txn.asset.upper(), txn.price,
            datetime.datetime.strftime(txn.dt, "%d/%m/%Y"))
        if direction == "LONG":
            pe = PortfolioEvent(dt=txn.dt,
                                type='asset_transaction',
                                description=description,
                                debit=round(txn_total_cost, 2),
                                credit=0.0,
                                balance=round(self.total_cash, 2))
            self.logger.info(
                '(%s) Asset "%s" transacted LONG in portfolio "%s" '
                '- Debit: %0.2f, Balance: %0.2f' %
                (txn.dt.strftime(settings.LOGGING["DATE_FORMAT"]), txn.asset,
                 self.portfolio_id, round(txn_total_cost,
                                          2), round(self.total_cash, 2)))
        else:
            pe = PortfolioEvent(dt=txn.dt,
                                type='asset_transaction',
                                description=description,
                                debit=0.0,
                                credit=-1.0 * round(txn_total_cost, 2),
                                balance=round(self.total_cash, 2))
            self.logger.info(
                '(%s) Asset "%s" transacted SHORT in portfolio "%s" '
                '- Credit: %0.2f, Balance: %0.2f' %
                (txn.dt.strftime(settings.LOGGING["DATE_FORMAT"]), txn.asset,
                 self.portfolio_id, -1.0 * round(txn_total_cost, 2),
                 round(self.total_cash, 2)))
        self.history.append(pe)
Пример #2
0
def test_subscribe_funds_behaviour():
    """
    Test subscribe_funds raises for incorrect datetime
    Test subscribe_funds raises for negative amount
    Test subscribe_funds correctly adds positive
    amount, generates correct event and modifies time
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    earlier_dt = pd.Timestamp('2017-10-04 08:00:00', tz=pytz.UTC)
    later_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    pos_cash = 1000.0
    neg_cash = -1000.0
    port = Portfolio(start_dt, starting_cash=2000.0)

    # Test subscribe_funds raises for incorrect datetime
    with pytest.raises(ValueError):
        port.subscribe_funds(earlier_dt, pos_cash)

    # Test subscribe_funds raises for negative amount
    with pytest.raises(ValueError):
        port.subscribe_funds(start_dt, neg_cash)

    # Test subscribe_funds correctly adds positive
    # amount, generates correct event and modifies time
    port.subscribe_funds(later_dt, pos_cash)

    assert port.cash == 3000.0
    assert port.total_market_value == 0.0
    assert port.total_equity == 3000.0

    pe1 = PortfolioEvent(dt=start_dt,
                         type='subscription',
                         description="SUBSCRIPTION",
                         debit=0.0,
                         credit=2000.0,
                         balance=2000.0)
    pe2 = PortfolioEvent(dt=later_dt,
                         type='subscription',
                         description="SUBSCRIPTION",
                         debit=0.0,
                         credit=1000.0,
                         balance=3000.0)

    assert port.history == [pe1, pe2]
    assert port.current_dt == later_dt
Пример #3
0
def test_withdraw_funds_behaviour():
    """
    Test withdraw_funds raises for incorrect datetime
    Test withdraw_funds raises for negative amount
    Test withdraw_funds raises for lack of cash
    Test withdraw_funds correctly subtracts positive
    amount, generates correct event and modifies time
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    earlier_dt = pd.Timestamp('2017-10-04 08:00:00', tz=pytz.UTC)
    later_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    even_later_dt = pd.Timestamp('2017-10-07 08:00:00', tz=pytz.UTC)
    pos_cash = 1000.0
    neg_cash = -1000.0
    port_raise = Portfolio(start_dt)

    # Test withdraw_funds raises for incorrect datetime
    with pytest.raises(ValueError):
        port_raise.withdraw_funds(earlier_dt, pos_cash)

    # Test withdraw_funds raises for negative amount
    with pytest.raises(ValueError):
        port_raise.withdraw_funds(start_dt, neg_cash)

    # Test withdraw_funds raises for not enough cash
    port_broke = Portfolio(start_dt)
    port_broke.subscribe_funds(later_dt, 1000.0)

    with pytest.raises(ValueError):
        port_broke.withdraw_funds(later_dt, 2000.0)

    # Test withdraw_funds correctly subtracts positive
    # amount, generates correct event and modifies time
    # Initial subscribe
    port_cor = Portfolio(start_dt)
    port_cor.subscribe_funds(later_dt, pos_cash)
    pe_sub = PortfolioEvent(dt=later_dt,
                            type='subscription',
                            description="SUBSCRIPTION",
                            debit=0.0,
                            credit=1000.0,
                            balance=1000.0)
    assert port_cor.cash == 1000.0
    assert port_cor.total_market_value == 0.0
    assert port_cor.total_equity == 1000.0
    assert port_cor.history == [pe_sub]
    assert port_cor.current_dt == later_dt

    # Now withdraw
    port_cor.withdraw_funds(even_later_dt, 468.0)
    pe_wdr = PortfolioEvent(dt=even_later_dt,
                            type='withdrawal',
                            description="WITHDRAWAL",
                            debit=468.0,
                            credit=0.0,
                            balance=532.0)
    assert port_cor.cash == 532.0
    assert port_cor.total_market_value == 0.0
    assert port_cor.total_equity == 532.0
    assert port_cor.history == [pe_sub, pe_wdr]
    assert port_cor.current_dt == even_later_dt
Пример #4
0
def test_transact_asset_behaviour():
    """
    Test transact_asset raises for incorrect time
    Test transact_asset raises for transaction total
    cost exceeding total cash
    Test correct total_cash and total_securities_value
    for correct transaction (commission etc), correct
    portfolio event and correct time update
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    earlier_dt = pd.Timestamp('2017-10-04 08:00:00', tz=pytz.UTC)
    later_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    even_later_dt = pd.Timestamp('2017-10-07 08:00:00', tz=pytz.UTC)
    port = Portfolio(start_dt)
    asset = 'EQ:AAA'

    # Test transact_asset raises for incorrect time
    tn_early = Transaction(asset=asset,
                           quantity=100,
                           dt=earlier_dt,
                           price=567.0,
                           order_id=1,
                           commission=0.0)
    with pytest.raises(ValueError):
        port.transact_asset(tn_early)

    # Test transact_asset raises for transaction total
    # cost exceeding total cash
    port.subscribe_funds(later_dt, 1000.0)

    assert port.cash == 1000.0
    assert port.total_market_value == 0.0
    assert port.total_equity == 1000.0

    pe_sub1 = PortfolioEvent(dt=later_dt,
                             type='subscription',
                             description="SUBSCRIPTION",
                             debit=0.0,
                             credit=1000.0,
                             balance=1000.0)
    tn_large = Transaction(asset=asset,
                           quantity=100,
                           dt=later_dt,
                           price=567.0,
                           order_id=1,
                           commission=15.78)
    with pytest.raises(ValueError):
        port.transact_asset(tn_large)

    # Test correct total_cash and total_securities_value
    # for correct transaction (commission etc), correct
    # portfolio event and correct time update
    port.subscribe_funds(even_later_dt, 99000.0)

    assert port.cash == 100000.0
    assert port.total_market_value == 0.0
    assert port.total_equity == 100000.0

    pe_sub2 = PortfolioEvent(dt=even_later_dt,
                             type='subscription',
                             description="SUBSCRIPTION",
                             debit=0.0,
                             credit=99000.0,
                             balance=100000.0)
    tn_even_later = Transaction(asset=asset,
                                quantity=100,
                                dt=even_later_dt,
                                price=567.0,
                                order_id=1,
                                commission=15.78)
    port.transact_asset(tn_even_later)

    assert port.cash == 43284.22
    assert port.total_market_value == 56700.00
    assert port.total_equity == 99984.22

    description = "LONG 100 EQ:AAA 567.00 07/10/2017"
    pe_tn = PortfolioEvent(dt=even_later_dt,
                           type="asset_transaction",
                           description=description,
                           debit=56715.78,
                           credit=0.0,
                           balance=43284.22)

    assert port.history == [pe_sub1, pe_sub2, pe_tn]
    assert port.current_dt == even_later_dt
Пример #5
0
    def transact_stock(self, txn):
  
        if txn.dt < self.current_k:
            raise ValueError(
                'Transaction datetime (%s) is earlier than '
                'current portfolio datetime (%s). Cannot '
                'transact assets.' % (txn.dt, self.current_dt)
            )
        self.current_dt = txn.dt

        txn_share_cost = txn.price * txn.quantity
        txn_total_cost = txn_share_cost + txn.commission

        if txn_total_cost > self.cash:
            if settings.PRINT_EVENTS:
                print(
                    'WARNING: Not enough cash in the portfolio to '
                    'carry out transaction. Transaction cost of %s '
                    'exceeds remaining cash of %s. Transaction '
                    'will proceed with a negative cash balance.' % (
                        txn_total_cost, self.cash
                    )
                )

        self.pos_handler.transact_position(txn)

        self.cash -= txn_total_cost

        # Form Portfolio history details
        direction = "LONG" if txn.direction > 0 else "SHORT"
        description = "%s %s %s %0.2f %s" % (
            direction, txn.quantity, txn.asset.upper(),
            txn.price, datetime.datetime.strftime(txn.dt, "%d/%m/%Y")
        )
        if direction == "LONG":
            pe = PortfolioEvent(
                dt=txn.dt, type='asset_transaction',
                description=description,
                debit=round(txn_total_cost, 2), credit=0.0,
                balance=round(self.cash, 2)
            )
            self.logger.info(
                '(%s) Asset "%s" transacted LONG in portfolio "%s" '
                '- Debit: %0.2f, Balance: %0.2f' % (
                    txn.dt.strftime(settings.LOGGING["DATE_FORMAT"]),
                    txn.asset, self.portfolio_id,
                    round(txn_total_cost, 2), round(self.cash, 2)
                )
            )
        else:
            pe = PortfolioEvent(
                dt=txn.dt, type='asset_transaction',
                description=description,
                debit=0.0, credit=-1.0 * round(txn_total_cost, 2),
                balance=round(self.cash, 2)
            )
            self.logger.info(
                '(%s) Asset "%s" transacted SHORT in portfolio "%s" '
                '- Credit: %0.2f, Balance: %0.2f' % (
                    txn.dt.strftime(settings.LOGGING["DATE_FORMAT"]),
                    txn.asset, self.portfolio_id,
                    -1.0 * round(txn_total_cost, 2), round(self.cash, 2)
                )
            )
        self.history.append(pe)