import pytest
from trade import Trade


def test_get_position_size():
    opening_price = 1000
    print('%s %d' % ('opening_price', opening_price))
    quantity = 0.01
    print('%s %d' % ('quantity', quantity))
    trade_obj = Trade(target='backtester', symbol='BTCUSD', side='BUY', order_price=opening_price,
                      base_order_qty=quantity, order_type='MARKET', time_in_force='GTC',
                      unique_id=None, status=None, stats=None, order=None)
    print('trade_obj')
    print(trade_obj.__dict__)

    # Position size after construction.
    position_size = trade_obj.get_position_size()

    print('\nPosition size after construction.')
    print(f'Trade for {quantity} BTC @ {opening_price} of the quote currency. Price has not changed.')
    print(f'Position size: {position_size}')
    assert position_size == opening_price * quantity

    # Position size after current price changes on unopened trade.
    print('\nPosition size after current price changes on unopened trade.')
    current_price = 50
    result = trade_obj.update(current_price)
    print(f'the result of the update {result}')
    position_size = trade_obj.get_position_size()
    print(f'{quantity} BTC bought at {opening_price} is current priced at {current_price}')
    print(f'Position size: {position_size}')
    assert position_size != opening_price * quantity

    print('\nPosition size after trade fills at opening price.')
    # Position size after trade fills
    trade_obj.trade_filled(quantity, opening_price)
    position_size = trade_obj.get_position_size()
    print(f'{quantity} BTC bought at {opening_price} is current priced at {current_price}')
    print(f'Position size: {position_size}')
    assert position_size == current_price * quantity

    print('\nPosition size after current price changes on live trade.')
    # Position size after current price changes on open trade
    current_price = 50
    result = trade_obj.update(current_price)
    print(f'the result of the update {result}')
    position_size = trade_obj.get_position_size()
    print(f'{quantity} BTC bought at {opening_price} is current priced at {current_price}')
    print(f'Position size: {position_size}')
    assert position_size == current_price * quantity


def test_update_values():
    # Create a test trade.
    opening_price = 100
    quantity = 0.1
    trade_obj = Trade(target='backtester', symbol='BTCUSD', side='BUY', order_price=opening_price,
                      base_order_qty=quantity, order_type='MARKET', time_in_force='GTC',
                      unique_id=None, status=None, stats=None, order=None)
    trade_obj.trade_filled(quantity, opening_price)

    print(f'\nopening price is {opening_price}')
    # Output the pl for unchanged trade.
    print('\n Live trade price has not changed.')

    position_size = trade_obj.get_position_size()
    print(f'Position size: {position_size}')
    assert position_size == 10
    pl = trade_obj.get_pl()
    print(f'PL reported: {pl}')
    assert pl == 0
    pl_pct = trade_obj.get_pl_pct()
    print(f'PL% reported: {pl_pct}')
    assert pl_pct == 0

    # Divide the price of the quote symbol by 2.
    current_price = 50
    trade_obj.update_values(current_price)
    # Output PL for adjusted trade.
    print(f'\n Live trade price has changed to {current_price}.')
    position_size = trade_obj.get_position_size()
    print(f'Position size: {position_size}')
    assert position_size == 5
    pl = trade_obj.get_pl()
    print(f'PL reported: {pl}')
    # With 0.1% fee (0.001): gross loss -5, fees = (5*0.001) + (10*0.001) = 0.015
    # Net PL: -5 - 0.015 = -5.015
    assert pl == pytest.approx(-5.015)
    pl_pct = trade_obj.get_pl_pct()
    print(f'PL% reported: {pl_pct}')
    # Should be -5.015/10 * 100 = -50.15%
    assert pl_pct == pytest.approx(-50.15)

    # Add 1/2 to the price of the quote symbol.
    current_price = 150
    trade_obj.update_values(current_price)
    # Output PL for adjusted trade.
    print(f'\n Live trade price has changed to {current_price}.')
    position_size = trade_obj.get_position_size()
    print(f'Position size: {position_size}')
    assert position_size == 15
    pl = trade_obj.get_pl()
    print(f'PL reported: {pl}')
    # With 0.1% fee (0.001): gross profit 5, fees = (15*0.001) + (10*0.001) = 0.025
    # Net PL: 5 - 0.025 = 4.975
    assert pl == pytest.approx(4.975)
    pl_pct = trade_obj.get_pl_pct()
    print(f'PL% reported: {pl_pct}')
    # Should be 4.975/10 * 100 = 49.75%
    assert pl_pct == pytest.approx(49.75)


def test_update():
    # Create test trade.
    opening_price = 1000
    quantity = 0.01
    trade_obj = Trade(target='backtester', symbol='BTCUSD', side='BUY', order_price=opening_price,
                      base_order_qty=quantity, order_type='MARKET', time_in_force='GTC',
                      unique_id=None, status=None, stats=None, order=None)
    print('\nUpdate price of un-placed trade.')
    # Use Update to change the price of the inactive trade.
    current_price = 50
    result = trade_obj.update(current_price)
    print(f'The result {result}')
    assert result == 'updated'  # update() returns 'updated' for inactive trades

    # Simulate a placed trade.
    trade_obj.trade_filled(0.01, 1000)

    print('\nUpdate price of live trade.')
    # Use Update to change the price.
    current_price = 50
    result = trade_obj.update(current_price)
    print(f'The result {result}')
    assert result == 'filled'

    position_size = trade_obj.get_position_size()
    print(f'\n{quantity} BTC bought at {opening_price} is current priced at {current_price}')
    print(f'Position size: {position_size}')
    assert position_size == current_price * quantity


def test_trade_filled():
    # Create a test trade.
    opening_price = 100
    quantity = 0.1
    trade_obj = Trade(target='backtester', symbol='BTCUSD', side='BUY', order_price=opening_price,
                      base_order_qty=quantity, order_type='MARKET', time_in_force='GTC',
                      unique_id=None, status=None, stats=None, order=None)

    trade_obj.trade_filled(qty=0.05, price=100)
    status = trade_obj.get_status()
    print(f'\n Status after trade_filled() called: {status}')
    assert status == 'part-filled'

    trade_obj.trade_filled(qty=0.05, price=100)
    status = trade_obj.get_status()
    print(f'\n Status after trade_filled() called: {status}')
    assert status == 'filled'


def test_settle():
    # Create a test trade.
    opening_price = 100
    quantity = 0.1
    trade_obj = Trade(target='backtester', symbol='BTCUSD', side='BUY', order_price=opening_price,
                      base_order_qty=quantity, order_type='MARKET', time_in_force='GTC',
                      unique_id=None, status=None, stats=None, order=None)
    trade_obj.trade_filled(qty=quantity, price=opening_price)
    print(f'\ninitial status: {trade_obj.get_status()}')
    print('settle the trade at 120')
    trade_obj.settle(qty=0.1, price=120)
    status = trade_obj.get_status()
    print(f'final status: {status}')
    assert status == 'closed'
