def test_position_short_twice():
    """
    Tests that the properties on the Position
    are calculated for two consective short trades
    with differing quantities and market prices.
    """
    # Initial short details
    asset = 'EQ:MSFT'
    quantity = -100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 194.55
    order_id = 123
    commission = 1.44

    # Create the initial transaction and position
    first_transaction = Transaction(asset,
                                    quantity=quantity,
                                    dt=dt,
                                    price=price,
                                    order_id=order_id,
                                    commission=commission)
    position = Position.open_from_transaction(first_transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Second short
    second_quantity = -60
    second_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    second_price = 194.76
    second_order_id = 234
    second_commission = 1.27
    second_transaction = Transaction(asset,
                                     quantity=second_quantity,
                                     dt=second_dt,
                                     price=second_price,
                                     order_id=second_order_id,
                                     commission=second_commission)
    position.transact(second_transaction)

    assert position.current_price == second_price
    assert position.current_dt == second_dt

    assert position.buy_quantity == 0
    assert position.sell_quantity == 160
    assert position.avg_bought == 0.0
    assert position.avg_sold == 194.62875
    assert position.commission == 2.71

    assert position.direction == -1
    assert np.isclose(position.market_value, -31161.6)
    assert np.isclose(position.avg_price, 194.6118125)
    assert position.net_quantity == -160
    assert position.total_bought == 0.0
    assert position.total_sold == 31140.60
    assert position.net_total == 31140.6
    assert position.net_incl_commission == 31137.89
    assert np.isclose(position.unrealised_pnl, -23.71)
    assert np.isclose(position.realised_pnl, 0.0)
Exemple #2
0
def test_transact_position_current_position():
    """
    Tests the 'transact_position' method for a transaction
    with a current asset and checks that all objects are
    set correctly.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = Equity('Amazon, Inc.', 'AMZN')
    dt = pd.Timestamp('2015-05-06')
    transaction_long = Transaction(asset,
                                   quantity=100,
                                   dt=dt,
                                   price=960.0,
                                   order_id=123,
                                   commission=26.83)
    transaction_long_again = Transaction(asset,
                                         quantity=200,
                                         dt=dt,
                                         price=990.0,
                                         order_id=234,
                                         commission=18.53)
    ph.transact_position(transaction_long)
    ph.transact_position(transaction_long_again)

    # Check that the position object is set correctly
    pos = ph.positions[asset]
    assert pos.quantity == 300
    assert pos.direction == 1.0
    assert pos.book_cost_pu == 980.1512000000001
    assert pos.book_cost == 294045.36000000004
Exemple #3
0
def test_transact_position_quantity_zero():
    """
    Tests the 'transact_position' method for a transaction
    with net zero quantity after the transaction to ensure
    deletion of the position.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = Equity('Amazon, Inc.', 'AMZN')
    dt = pd.Timestamp('2015-05-06')
    transaction_long = Transaction(asset,
                                   quantity=100,
                                   dt=dt,
                                   price=960.0,
                                   order_id=123,
                                   commission=26.83)
    transaction_close = Transaction(asset,
                                    quantity=-100,
                                    dt=dt,
                                    price=980.0,
                                    order_id=234,
                                    commission=18.53)

    # Go long and then close, then check that the
    # positions OrderedDict is empty
    ph.transact_position(transaction_long)
    ph.transact_position(transaction_close)
    od = OrderedDict()
    assert ph.positions == od
Exemple #4
0
def test_total_values_for_two_separate_transactions():
    """
    Tests 'total_book_cost', 'total_market_value',
    'total_gain' and 'total_perc_gain' for single
    transactions in two separate assets.
    """
    ph = PositionHandler()

    # Asset 1
    asset1 = Equity('Amazon, Inc.', 'AMZN')
    dt1 = pd.Timestamp('2015-05-06')
    trans_pos_1 = Transaction(asset1.symbol,
                              quantity=75,
                              dt=dt1,
                              price=483.45,
                              order_id=1,
                              commission=15.97)
    ph.transact_position(trans_pos_1)

    # Asset 2
    asset2 = Equity('Microsoft, Inc.', 'MSFT')
    dt2 = pd.Timestamp('2015-05-07')
    trans_pos_2 = Transaction(asset2.symbol,
                              quantity=250,
                              dt=dt2,
                              price=142.58,
                              order_id=2,
                              commission=8.35)
    ph.transact_position(trans_pos_2)

    # Check all total values
    assert ph.total_book_cost() == 71928.07
    assert ph.total_market_value() == 71903.75
    assert ph.total_unrealised_gain() == -24.31999999999971
    assert ph.total_unrealised_percentage_gain() == -0.03381155646190282
def test_position_long_twice():
    """
    Tests that the properties on the Position
    are calculated for two consective long trades
    with differing quantities and market prices.
    """
    # Initial long details
    asset = 'EQ:MSFT'
    quantity = 100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 193.74
    order_id = 123
    commission = 1.0

    # Create the initial transaction and position
    first_transaction = Transaction(asset,
                                    quantity=quantity,
                                    dt=dt,
                                    price=price,
                                    order_id=order_id,
                                    commission=commission)
    position = Position.open_from_transaction(first_transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Second long
    second_quantity = 60
    second_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    second_price = 193.79
    second_order_id = 234
    second_commission = 1.0
    second_transaction = Transaction(asset,
                                     quantity=second_quantity,
                                     dt=second_dt,
                                     price=second_price,
                                     order_id=second_order_id,
                                     commission=second_commission)
    position.transact(second_transaction)

    assert position.current_price == second_price
    assert position.current_dt == second_dt

    assert position.buy_quantity == 160
    assert position.sell_quantity == 0
    assert np.isclose(position.avg_bought, 193.75875)
    assert position.avg_sold == 0.0
    assert position.commission == 2.0

    assert position.direction == 1
    assert np.isclose(position.market_value, 31006.40)
    assert position.avg_price == 193.77125
    assert position.net_quantity == 160
    assert position.total_bought == 31001.40
    assert position.total_sold == 0.0
    assert position.net_total == -31001.40
    assert position.net_incl_commission == -31003.40
    assert np.isclose(position.unrealised_pnl, 3.0)
    assert np.isclose(position.realised_pnl, 0.0)
def test_transact_position_quantity_zero():
    """
    Tests the 'transact_position' method for a transaction
    with net zero quantity after the transaction to ensure
    deletion of the position.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = 'EQ:AMZN'
    dt = pd.Timestamp('2015-05-06 15:00:00', tz=pytz.UTC)
    new_dt = pd.Timestamp('2015-05-06 16:00:00', tz=pytz.UTC)

    transaction_long = Transaction(
        asset,
        quantity=100,
        dt=dt,
        price=960.0,
        order_id=123, commission=26.83
    )
    ph.transact_position(transaction_long)

    transaction_close = Transaction(
        asset,
        quantity=-100,
        dt=new_dt,
        price=980.0,
        order_id=234,
        commission=18.53
    )
    ph.transact_position(transaction_close)

    # Go long and then close, then check that the
    # positions OrderedDict is empty
    assert ph.positions == OrderedDict()
def test_position_long_close():
    """
    Tests that the properties on the Position
    are calculated for a long opening trade and
    subsequent closing trade.
    """
    # Initial long details
    asset = 'EQ:AMZN'
    quantity = 100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 2615.27
    order_id = 123
    commission = 1.0

    # Create the initial transaction and position
    first_transaction = Transaction(asset,
                                    quantity=quantity,
                                    dt=dt,
                                    price=price,
                                    order_id=order_id,
                                    commission=commission)
    position = Position.open_from_transaction(first_transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Closing trade
    second_quantity = -100
    second_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    second_price = 2622.0
    second_order_id = 234
    second_commission = 6.81
    second_transaction = Transaction(asset,
                                     quantity=second_quantity,
                                     dt=second_dt,
                                     price=second_price,
                                     order_id=second_order_id,
                                     commission=second_commission)
    position.transact(second_transaction)

    assert position.current_price == second_price
    assert position.current_dt == second_dt

    assert position.buy_quantity == 100
    assert position.sell_quantity == 100
    assert position.avg_bought == 2615.27
    assert position.avg_sold == 2622.0
    assert position.commission == 7.81

    assert position.direction == 0
    assert position.market_value == 0.0
    assert position.avg_price == 0.0
    assert position.net_quantity == 0
    assert position.total_bought == 261527.0
    assert position.total_sold == 262200.0
    assert position.net_total == 673.0
    assert position.net_incl_commission == 665.19
    assert position.unrealised_pnl == 0.0
    assert position.realised_pnl == 665.19
def test_position_short_close():
    """
    Tests that the properties on the Position
    are calculated for a short opening trade and
    subsequent closing trade.
    """
    # Initial short details
    asset = 'EQ:TSLA'
    quantity = -100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 982.13
    order_id = 123
    commission = 3.18

    # Create the initial transaction and position
    first_transaction = Transaction(asset,
                                    quantity=quantity,
                                    dt=dt,
                                    price=price,
                                    order_id=order_id,
                                    commission=commission)
    position = Position.open_from_transaction(first_transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Closing trade
    second_quantity = 100
    second_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    second_price = 982.13
    second_order_id = 234
    second_commission = 1.0
    second_transaction = Transaction(asset,
                                     quantity=second_quantity,
                                     dt=second_dt,
                                     price=second_price,
                                     order_id=second_order_id,
                                     commission=second_commission)
    position.transact(second_transaction)

    assert position.current_price == second_price
    assert position.current_dt == second_dt

    assert position.buy_quantity == 100
    assert position.sell_quantity == 100
    assert position.avg_bought == 982.13
    assert position.avg_sold == 982.13
    assert position.commission == 4.18

    assert position.direction == 0
    assert position.market_value == 0.0
    assert position.avg_price == 0.0
    assert position.net_quantity == 0
    assert position.total_bought == 98213.0
    assert position.total_sold == 98213.0
    assert position.net_total == 0.0
    assert position.net_incl_commission == -4.18
    assert position.unrealised_pnl == 0.0
    assert position.realised_pnl == -4.18
Exemple #9
0
def test_portfolio_to_dict_for_two_holdings():
    """
    Test portfolio_to_dict for two holdings.
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    asset1_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    asset2_dt = pd.Timestamp('2017-10-07 08:00:00', tz=pytz.UTC)
    update_dt = pd.Timestamp('2017-10-08 08:00:00', tz=pytz.UTC)
    asset1 = Equity("AAA Inc.", "EQ:AAA", tax_exempt=False)
    asset2 = Equity("BBB Inc.", "EQ:BBB", tax_exempt=False)

    port = Portfolio(start_dt, portfolio_id='1234')
    port.subscribe_funds(start_dt, 100000.0)
    tn_asset1 = Transaction(asset=asset1.symbol,
                            quantity=100,
                            dt=asset1_dt,
                            price=567.0,
                            order_id=1,
                            commission=15.78)
    port.transact_asset(tn_asset1)

    tn_asset2 = Transaction(asset=asset2.symbol,
                            quantity=100,
                            dt=asset2_dt,
                            price=123.0,
                            order_id=2,
                            commission=7.64)
    port.transact_asset(tn_asset2)

    port.update_market_value_of_asset(asset2.symbol, 134.0, update_dt)
    test_holdings = {
        asset1.symbol: {
            "quantity": 100,
            "book_cost": 56715.78,
            "market_value": 56700.0,
            "gain": -15.78,
            "perc_gain": -0.027822944513854874
        },
        asset2.symbol: {
            "quantity": 100,
            "book_cost": 12307.64,
            "market_value": 13400.0,
            "gain": 1092.3600000000006,
            "perc_gain": 8.8754627207165679
        }
    }
    port_holdings = port.portfolio_to_dict()

    # This is needed because we're not using Decimal
    # datatypes and have to compare slightly differing
    # floating point representations
    for asset in (asset1.symbol, asset2.symbol):
        for key, val in test_holdings[asset].items():
            assert port_holdings[asset][key] == pytest.approx(
                test_holdings[asset][key])
def test_portfolio_to_dict_for_two_holdings():
    """
    Test portfolio_to_dict for two holdings.
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    asset1_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    asset2_dt = pd.Timestamp('2017-10-07 08:00:00', tz=pytz.UTC)
    update_dt = pd.Timestamp('2017-10-08 08:00:00', tz=pytz.UTC)
    asset1 = 'EQ:AAA'
    asset2 = 'EQ:BBB'

    port = Portfolio(start_dt, portfolio_id='1234')
    port.subscribe_funds(start_dt, 100000.0)
    tn_asset1 = Transaction(asset=asset1,
                            quantity=100,
                            dt=asset1_dt,
                            price=567.0,
                            order_id=1,
                            commission=15.78)
    port.transact_asset(tn_asset1)

    tn_asset2 = Transaction(asset=asset2,
                            quantity=100,
                            dt=asset2_dt,
                            price=123.0,
                            order_id=2,
                            commission=7.64)
    port.transact_asset(tn_asset2)
    port.update_market_value_of_asset(asset2, 134.0, update_dt)
    test_holdings = {
        asset1: {
            "quantity": 100,
            "market_value": 56700.0,
            "unrealised_pnl": -15.78,
            "realised_pnl": 0.0,
            "total_pnl": -15.78
        },
        asset2: {
            "quantity": 100,
            "market_value": 13400.0,
            "unrealised_pnl": 1092.3600000000006,
            "realised_pnl": 0.0,
            "total_pnl": 1092.3600000000006
        }
    }
    port_holdings = port.portfolio_to_dict()

    # This is needed because we're not using Decimal
    # datatypes and have to compare slightly differing
    # floating point representations
    for asset in (asset1, asset2):
        for key, val in test_holdings[asset].items():
            assert port_holdings[asset][key] == pytest.approx(
                test_holdings[asset][key])
Exemple #11
0
def test_transact_position_new_position():
    """
    Tests the 'transact_position' method for a transaction
    with a brand new asset and checks that all objects are
    set correctly.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = Equity('Amazon, Inc.', 'AMZN')
    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset,
                              quantity=100,
                              dt=dt,
                              price=960.0,
                              order_id=123,
                              commission=26.83)
    ph.transact_position(transaction)

    # Check that the position object is set correctly
    pos = ph.positions[asset]
    assert pos.quantity == 100
    assert pos.direction == 1.0
    assert pos.book_cost_pu == 960.2683000000001
    assert pos.book_cost == 96026.83
Exemple #12
0
def test_update_position_for_non_none_values():
    """
    Tests the 'update_position' method for non-None
    values when updating a Position entity.
    """
    ph = PositionHandler()

    # Asset 1
    asset1 = Equity('Amazon, Inc.', 'AMZN')
    dt1 = pd.Timestamp('2015-05-06')
    trans_pos_1 = Transaction(asset1,
                              quantity=75,
                              dt=dt1,
                              price=483.45,
                              order_id=1,
                              commission=13.76)
    ph.transact_position(trans_pos_1)

    # Update values manually
    quantity = 100
    current_price = 504.32
    current_dt = pd.Timestamp('2015-05-07')
    book_cost_pu = 23.65
    ph.update_position(asset1,
                       quantity=quantity,
                       current_price=current_price,
                       current_dt=current_dt,
                       book_cost_pu=book_cost_pu)

    assert ph.positions[asset1].quantity == quantity
    assert ph.positions[asset1].current_price == current_price
    assert ph.positions[asset1].current_dt == current_dt
    assert ph.positions[asset1].book_cost_pu == book_cost_pu
Exemple #13
0
def test_position_long_short_positive_gain():
    """
    Tests that the quantity and book cost are
    correctly calculated for an initial long
    position with an additional short transaction
    in the same asset, where the short does not
    completely eliminate the position and the
    result is a gain.
    """
    asset = Equity('Apple, Inc.', 'AAPL')
    position = Position(asset,
                        quantity=100,
                        book_cost_pu=950.0,
                        current_price=950.0)

    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset,
                              quantity=-50,
                              dt=dt,
                              price=960.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)

    assert position.quantity == 50
    assert position.book_cost_pu == 950.0
    assert position.direction == 1.0
    assert position.current_price == 960.0
    assert position.market_value == 48000.0
    assert position.unrealised_gain == 500.0
    assert position.unrealised_percentage_gain == 1.0526315789473684
Exemple #14
0
def test_position_long_twice():
    """
    Tests that the quantity and book cost are
    correctly calculated for an initial long
    position with an additional long transaction
    in the same asset.
    """
    asset = Equity('Apple, Inc.', 'AAPL')
    position = Position(asset,
                        quantity=100,
                        book_cost_pu=950.0,
                        current_price=950.0)

    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset,
                              quantity=100,
                              dt=dt,
                              price=960.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)

    assert position.quantity == 200
    assert position.book_cost_pu == 955.0
    assert position.direction == 1.0
    assert position.current_price == 960.0
    assert position.market_value == 192000.0
    assert position.unrealised_gain == 1000.0
    assert position.unrealised_percentage_gain == 0.5235602094240838
def test_transact_for_incorrect_asset():
    """
    Tests that the 'transact' method, when provided
    with a Transaction with an Asset that does not
    match the position's asset, raises an Exception.
    """
    asset1 = 'EQ:AAPL'
    asset2 = 'EQ:AMZN'

    position = Position(asset1,
                        current_price=950.0,
                        current_dt=pd.Timestamp('2020-06-16 15:00:00',
                                                tz=pytz.UTC),
                        buy_quantity=100,
                        sell_quantity=0,
                        avg_bought=950.0,
                        avg_sold=0.0,
                        buy_commission=1.0,
                        sell_commission=0.0)

    new_dt = pd.Timestamp('2020-06-16 16:00:00')
    transaction = Transaction(asset2,
                              quantity=50,
                              dt=new_dt,
                              price=960.0,
                              order_id=123,
                              commission=1.0)

    with pytest.raises(Exception):
        position.update(transaction)
Exemple #16
0
def test_position_short_long_excess_cover():
    """
    Tests that the quantity and book cost are
    correctly calculated for an initial short
    position with an additional long transaction
    in the same asset, where the long position
    is in excess of the short position.
    """
    asset = Equity('Apple, Inc.', 'AAPL')
    position = Position(asset,
                        quantity=-100,
                        book_cost_pu=700.0,
                        current_price=700.0)
    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset,
                              quantity=175,
                              dt=dt,
                              price=873.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)

    assert position.quantity == 75
    assert position.book_cost_pu == 873.0
    assert position.direction == 1.0
    assert position.current_price == 873.0
    assert position.market_value == 65475.0
    assert position.unrealised_gain == 0.0
    assert position.unrealised_percentage_gain == 0.0
def test_transact_position_new_position():
    """
    Tests the 'transact_position' method for a transaction
    with a brand new asset and checks that all objects are
    set correctly.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = 'EQ:AMZN'

    transaction = Transaction(
        asset,
        quantity=100,
        dt=pd.Timestamp('2015-05-06 15:00:00', tz=pytz.UTC),
        price=960.0,
        order_id=123,
        commission=26.83
    )
    ph.transact_position(transaction)

    # Check that the position object is set correctly
    pos = ph.positions[asset]

    assert pos.buy_quantity == 100
    assert pos.sell_quantity == 0
    assert pos.net_quantity == 100
    assert pos.direction == 1
    assert pos.avg_price == 960.2683000000001
def test_transact_position_current_position():
    """
    Tests the 'transact_position' method for a transaction
    with a current asset and checks that all objects are
    set correctly.
    """
    # Create the PositionHandler, Transaction and
    # carry out a transaction
    ph = PositionHandler()
    asset = 'EQ:AMZN'
    dt = pd.Timestamp('2015-05-06 15:00:00', tz=pytz.UTC)
    new_dt = pd.Timestamp('2015-05-06 16:00:00', tz=pytz.UTC)

    transaction_long = Transaction(
        asset,
        quantity=100,
        dt=dt,
        price=960.0,
        order_id=123,
        commission=26.83
    )
    ph.transact_position(transaction_long)

    transaction_long_again = Transaction(
        asset,
        quantity=200,
        dt=new_dt,
        price=990.0,
        order_id=234,
        commission=18.53
    )
    ph.transact_position(transaction_long_again)

    # Check that the position object is set correctly
    pos = ph.positions[asset]

    assert pos.buy_quantity == 300
    assert pos.sell_quantity == 0
    assert pos.net_quantity == 300
    assert pos.direction == 1
    assert np.isclose(pos.avg_price, 980.1512)
def test_basic_short_equities_position():
    """
    Tests that the properties on the Position
    are calculated for a simple short equities position.
    """
    # Initial short details
    asset = 'EQ:TLT'
    quantity = -100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 162.39
    order_id = 123
    commission = 1.37

    # Create the initial transaction and position
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position = Position.open_from_transaction(transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Update the market price
    new_market_price = 159.43
    new_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    position.update_current_price(new_market_price, new_dt)

    assert position.current_price == new_market_price
    assert position.current_dt == new_dt

    assert position.buy_quantity == 0
    assert position.sell_quantity == 100
    assert position.avg_bought == 0.0
    assert position.avg_sold == 162.39
    assert position.commission == 1.37

    assert position.direction == -1
    assert position.market_value == -15943.0
    assert position.avg_price == 162.3763
    assert position.net_quantity == -100
    assert position.total_bought == 0.0

    # np.isclose used for floating point precision
    assert np.isclose(position.total_sold, 16239.0)
    assert np.isclose(position.net_total, 16239.0)
    assert np.isclose(position.net_incl_commission, 16237.63)
    assert np.isclose(position.unrealised_pnl, 294.63)
    assert np.isclose(position.realised_pnl, 0.0)
def test_total_values_for_two_separate_transactions():
    """
    Tests 'total_market_value', 'total_unrealised_pnl',
    'total_realised_pnl' and 'total_pnl' for single
    transactions in two separate assets.
    """
    ph = PositionHandler()

    # Asset 1
    asset1 = 'EQ:AMZN'
    dt1 = pd.Timestamp('2015-05-06 15:00:00', tz=pytz.UTC)
    trans_pos_1 = Transaction(
        asset1,
        quantity=75,
        dt=dt1,
        price=483.45,
        order_id=1,
        commission=15.97
    )
    ph.transact_position(trans_pos_1)

    # Asset 2
    asset2 = 'EQ:MSFT'
    dt2 = pd.Timestamp('2015-05-07 15:00:00', tz=pytz.UTC)
    trans_pos_2 = Transaction(
        asset2,
        quantity=250,
        dt=dt2,
        price=142.58,
        order_id=2,
        commission=8.35
    )
    ph.transact_position(trans_pos_2)

    # Check all total values
    assert ph.total_market_value() == 71903.75
    assert np.isclose(ph.total_unrealised_pnl(), -24.31999999999971)
    assert ph.total_realised_pnl() == 0.0
    assert np.isclose(ph.total_pnl(), -24.31999999999971)
def test_basic_long_equities_position():
    """
    Tests that the properties on the Position
    are calculated for a simple long equities position.
    """
    # Initial long details
    asset = 'EQ:MSFT'
    quantity = 100
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 193.74
    order_id = 123
    commission = 1.0

    # Create the initial transaction and position
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position = Position.open_from_transaction(transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    # Update the market price
    new_market_price = 192.80
    new_dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    position.update_current_price(new_market_price, new_dt)

    assert position.current_price == new_market_price
    assert position.current_dt == new_dt

    assert position.buy_quantity == 100
    assert position.sell_quantity == 0
    assert position.avg_bought == 193.74
    assert position.avg_sold == 0.0
    assert position.commission == 1.0

    assert position.direction == 1
    assert position.market_value == 19280.0
    assert position.avg_price == 193.75
    assert position.net_quantity == 100
    assert position.total_bought == 19374.0
    assert position.total_sold == 0.0
    assert position.net_total == -19374.0
    assert position.net_incl_commission == -19375.0
    assert np.isclose(position.unrealised_pnl, -95.0)
    assert np.isclose(position.realised_pnl, 0.0)
Exemple #22
0
def test_update_commission():
    """
    Tests the 'update_commission' method to ensure
    commission is correctly set on the Position entities.
    """
    ph = PositionHandler()

    # Asset 1
    asset1 = Equity('Amazon, Inc.', 'AMZN')
    dt1 = pd.Timestamp('2015-05-06')
    trans_pos_1 = Transaction(asset1.symbol,
                              quantity=75,
                              dt=dt1,
                              price=483.45,
                              order_id=1,
                              commission=0.0)
    ph.transact_position(trans_pos_1)
    ph.update_commission(asset1.symbol, 15.97)

    # Asset 2
    asset2 = Equity('Microsoft, Inc.', 'MSFT')
    dt2 = pd.Timestamp('2015-05-07')
    trans_pos_2 = Transaction(asset2.symbol,
                              quantity=250,
                              dt=dt2,
                              price=142.58,
                              order_id=2,
                              commission=0.0)
    ph.transact_position(trans_pos_2)
    ph.update_commission(asset2.symbol, 8.35)

    # Check all total values
    assert ph.total_book_cost() == 71928.07
    assert ph.total_market_value() == 71903.75
    assert ph.total_unrealised_gain() == -24.31999999999971
    assert ph.total_unrealised_percentage_gain() == -0.03381155646190282
def test_transaction_representation():
    """
    Tests that the Transaction representation
    correctly recreates the object.
    """
    dt = pd.Timestamp('2015-05-06')
    asset = Equity('Apple, Inc.', 'AAPL')
    transaction = Transaction(asset,
                              quantity=168,
                              dt=dt,
                              price=56.18,
                              order_id=153)
    exp_repr = (
        "Transaction(asset=Equity(name='Apple, Inc.', symbol='AAPL', tax_exempt=True), "
        "quantity=168, dt=2015-05-06 00:00:00, price=56.18, order_id=153)")
    assert repr(transaction) == exp_repr
def test_update_market_value_of_asset_negative_price():
    """
    Test update_market_value_of_asset for
    asset with negative price.
    """
    start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC)
    later_dt = pd.Timestamp('2017-10-06 08:00:00', tz=pytz.UTC)
    port = Portfolio(start_dt)

    asset = 'EQ:AAA'
    port.subscribe_funds(later_dt, 100000.0)
    tn_asset = Transaction(asset=asset,
                           quantity=100,
                           dt=later_dt,
                           price=567.0,
                           order_id=1,
                           commission=15.78)
    port.transact_asset(tn_asset)
    with pytest.raises(ValueError):
        port.update_market_value_of_asset(asset, -54.34, later_dt)
def test_update_market_value_of_asset_earlier_date():
    """
    Test update_market_value_of_asset for asset
    with current_trade_date in past
    """
    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)
    port = Portfolio(start_dt, portfolio_id='1234')

    asset = 'EQ:AAA'
    port.subscribe_funds(later_dt, 100000.0)
    tn_asset = Transaction(asset=asset,
                           quantity=100,
                           dt=later_dt,
                           price=567.0,
                           order_id=1,
                           commission=15.78)
    port.transact_asset(tn_asset)
    with pytest.raises(ValueError):
        port.update_market_value_of_asset(asset, 50.23, earlier_dt)
Exemple #26
0
def test_update_for_incorrect_asset():
    """
    Tests that the 'update' method, when provided
    with a transaction with an asset that does not
    match the position's asset, raises an Exception.
    """
    asset1 = Equity('Apple, Inc.', 'AAPL')
    asset2 = Equity('Amazon, Inc.', 'AMZN')

    position = Position(asset1,
                        quantity=100,
                        book_cost_pu=950.0,
                        current_price=950.0)
    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset2,
                              quantity=50,
                              dt=dt,
                              price=960.0,
                              order_id=123,
                              commission=None)

    with pytest.raises(Exception):
        position.update(transaction)
def test_position_short_long_short_long_ending_short():
    """
    Tests that the properties on the Position
    are calculated for four trades consisting
    of a short, long, short and long ending net
    short after all trades with varying quantities
    and market prices.
    """
    # First trade (first short)
    asset = 'EQ:AGG'
    quantity = -762
    dt = pd.Timestamp('2020-06-16 15:00:00', tz=pytz.UTC)
    price = 117.74
    order_id = 100
    commission = 5.35
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position = Position.open_from_transaction(transaction)

    # Second trade (first long)
    quantity = 477
    dt = pd.Timestamp('2020-06-16 16:00:00', tz=pytz.UTC)
    price = 117.875597
    order_id = 101
    commission = 2.31
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position.transact(transaction)

    # Third trade (second short)
    quantity = -595
    dt = pd.Timestamp('2020-06-16 17:00:00', tz=pytz.UTC)
    price = 117.74
    order_id = 102
    commission = 4.18
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position.transact(transaction)

    # Fourth trade (second long), now net short
    quantity = 427
    dt = pd.Timestamp('2020-06-16 18:00:00', tz=pytz.UTC)
    price = 117.793115
    order_id = 103
    commission = 2.06
    transaction = Transaction(asset,
                              quantity=quantity,
                              dt=dt,
                              price=price,
                              order_id=order_id,
                              commission=commission)
    position.transact(transaction)

    assert position.asset == asset
    assert position.current_price == price
    assert position.current_dt == dt

    assert position.buy_quantity == 904
    assert position.sell_quantity == 1357
    assert position.avg_bought == 117.83663702876107
    assert position.avg_sold == 117.74
    assert np.isclose(position.commission, 13.90)

    assert position.direction == -1
    assert np.isclose(position.market_value, -53360.281095)
    assert position.avg_price == 117.73297715549005
    assert position.net_quantity == -453
    assert position.total_bought == 106524.31987400001
    assert np.isclose(position.total_sold, 159773.18)
    assert np.isclose(position.net_total, 53248.86)
    assert np.isclose(position.net_incl_commission, 53234.95)
    assert np.isclose(position.unrealised_pnl, -27.242443563)
    assert np.isclose(position.realised_pnl, -98.0785254)
Exemple #28
0
def test_position_three_longs_one_short_one_long():
    """
    Tests that the quantity and book cost are
    correctly calculated for three long transactions,
    followed by a partial closing position, followed
    by a new long position, all in the same asset.

    Buy 100 qty at £1.00 -> £100
    Buy 100 qty at £2.00 -> £200
    Buy 200 qty at £3.00 -> £600
    Total qty after 3 longs is 400, with book cost £900 (£2.25 p/u)

    Sell 100 qty -> Book cost now £675 (25% holdings reduced),
    still at £2.25 p/u
    Buy 100 at £4.00 -> 400
    Final qty is 400, but book cost is now £1,075 (£2.6875 p/u).
    """
    asset = Equity('Apple, Inc.', 'AAPL')

    # Initial long
    position = Position(asset,
                        quantity=100,
                        book_cost_pu=1.0,
                        current_price=1.0)

    # Second long
    dt = pd.Timestamp('2015-05-06')
    transaction = Transaction(asset,
                              quantity=100,
                              dt=dt,
                              price=2.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)
    assert position.quantity == 200
    assert position.book_cost_pu == 1.5

    # Third long
    dt = pd.Timestamp('2015-05-07')
    transaction = Transaction(asset,
                              quantity=200,
                              dt=dt,
                              price=3.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)
    assert position.quantity == 400
    assert position.book_cost_pu == 2.25

    # First short
    dt = pd.Timestamp('2015-05-08')
    transaction = Transaction(asset,
                              quantity=-100,
                              dt=dt,
                              price=3.5,
                              order_id=123,
                              commission=None)
    position.update(transaction)
    assert position.quantity == 300
    assert position.book_cost_pu == 2.25

    # Final long
    dt = pd.Timestamp('2015-05-09')
    transaction = Transaction(asset,
                              quantity=100,
                              dt=dt,
                              price=4.0,
                              order_id=123,
                              commission=None)
    position.update(transaction)

    assert position.quantity == 400
    assert position.book_cost_pu == 2.6875
    assert position.direction == 1.0
    assert position.current_price == 4.0
    assert position.market_value == 1600.0
    assert position.unrealised_gain == 525.0
    assert position.unrealised_percentage_gain == 48.837209302325576
Exemple #29
0
    def _execute_order(self, dt, portfolio_id, order):
        """
        For a given portfolio ID string, create a Transaction instance from
        the provided Order and ensure the Portfolio is appropriately updated
        with the new information.

        Parameters
        ----------
        dt : `pd.Timestamp`
            The current timestamp.
        portfolio_id : `str`
            The portfolio ID string.
        order : `Order`
            The Order instance to create the Transaction for.
        """
        # Obtain a price for the asset, if no price then
        # raise a ValueError
        price_err_msg = (
            "Could not obtain a latest market price for "
            "Asset with ticker symbol '%s'. Order with ID '%s' was "
            "not executed." % (order.asset, order.order_id))
        bid_ask = self.data_handler.get_asset_latest_bid_ask_price(
            dt, order.asset)
        if bid_ask == (np.NaN, np.NaN):
            raise ValueError(price_err_msg)

        # Calculate the consideration and total commission
        # based on the commission model
        if order.direction > 0:
            price = bid_ask[1]
        else:
            price = bid_ask[0]
        consideration = round(price * order.quantity)
        total_commission = self.fee_model.calc_total_cost(
            order.asset, order.quantity, consideration, self)

        # Check that sufficient cash exists to carry out the
        # order, else scale it down
        est_total_cost = consideration + total_commission
        total_cash = self.portfolios[portfolio_id].total_cash

        scaled_quantity = order.quantity
        if est_total_cost > total_cash:
            print("WARNING: Estimated transaction size of %0.2f exceeds "
                  "available cash of %0.2f. Reducing quantity to allow "
                  "transaction to succeed." % (est_total_cost, total_cash))
            scaled_quantity = int(floor(total_cash / price))

        # Create a transaction entity and update the portfolio
        txn = Transaction(order.asset,
                          scaled_quantity,
                          self.current_dt,
                          price,
                          order.order_id,
                          commission=total_commission)
        self.portfolios[portfolio_id].transact_asset(txn)
        print("(%s) - executed order: %s, qty: %s, price: %0.2f, "
              "consideration: %0.2f, commission: %0.2f, total: %0.2f" %
              (self.current_dt, order.asset, scaled_quantity, price,
               consideration, total_commission,
               consideration + total_commission))
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