Module xchg.tests.test_xchg

Unit tests for xchg.py.

Expand source code
'''Unit tests for xchg.py.'''

from pytest import raises
from pytest import approx
from ..xchg import Xchg


def test_init(files: dict, tmp_path: str, candles: list, balance: dict,
              xchg_repr: str, default_balance: dict, incomplete_balance: dict,
              permuted_balance: dict):
    '''Test a construction of a Xchg class.

    Args:
        files: A dictionary with csv files content.
        tmp_path: A path which authomatically created by pytest for testing.
        candles: An expected result.
        balance: An initial balance.
        xchg_repr: A representation __repr__ of Xchg class.
        default_balance: A default balance when it is not set.
        incomplete_balance: An incomplete balance which should be filled with
    zeros.
        permuted_balance: A permuted balance which should be sorted.
    '''
    # Prepare files.
    for filename, content in files.items():
        filepath = tmp_path / filename
        with open(filepath, 'w') as f:
            f.write(content)

    fee = 0.1
    min_order_size = 0.01
    x = Xchg(fee, min_order_size, balance=balance, data_path=tmp_path)
    assert x.current_candle == candles[0]
    assert x.balance == balance
    assert x.fee == fee
    assert x.min_order_size == min_order_size
    assert x.currencies == [f.split('.')[0] for f in files.keys()]
    assert repr(x) == xchg_repr
    x = Xchg(fee, min_order_size, tmp_path)
    assert x.balance == default_balance
    x = Xchg(fee, min_order_size, tmp_path, 1.0)
    assert x.balance == default_balance

    # Check that incompleted balance filled to the full.
    x = Xchg(fee, min_order_size, tmp_path, balance=incomplete_balance)
    assert x.balance == default_balance

    # Check that currencies are sorted in the balance.
    x = Xchg(fee, min_order_size, tmp_path, balance=permuted_balance)
    assert str(x.balance) == str(balance)


def test_next_step(x: Xchg, candles: list):
    '''Test a next_step method.

    Args:
        x: A Xchg instance.
        candles: A candles list.
    '''
    x = x.next_step()
    assert x.current_candle == candles[1]

    # Test for an exception at the end of a file.
    with raises(StopIteration):
        x.next_step()


def test_capital(x: Xchg):
    '''Test a capital property.

    Args:
        x: A Xchg instance.
    '''
    assert x.capital == 1.062048


def test_portfolio(x: Xchg, portfolio: dict):
    '''Test a portfolio property.

    Args:
        x: A Xchg instance.
        portfolio: A sample portfolio.
    '''
    assert x.portfolio == approx(portfolio, 1e-10)


def test_buy(x: Xchg, balance_after_buy: dict, balance_after_full_buy: dict):
    '''Test a buy operation.

    Args:
        x: A Xchg instance.
        balance_after_buy: A balance after a buy operation.
        balance_after_full_buy: A balance after buying currency for all cash.
    '''
    # A normal path.
    assert x.buy('cur0', 10).balance == approx(balance_after_buy, 1e-10)

    # Buy less than a minimum order size.
    assert x.buy('cur0', 0.1).balance == approx(x.balance, 1e-10)

    # Buy more than the existing cash amount.
    assert x.buy('cur0', 100).balance == approx(x.balance, 1e-10)

    # Buy zero amount.
    assert x.buy('cur0', 0).balance == approx(x.balance, 1e-10)

    # Buy a currency for all cash.
    assert x.buy('cur0', 49.800796812749006).balance == \
           approx(balance_after_full_buy, 1e-10)

    # Buy a little more than we have cash, it must be forgived.
    assert x.buy('cur0', 49.800796812749006 + 1e-10).balance == \
           approx(balance_after_full_buy, 1e-10)


def test_sell(x: Xchg, balance_after_sell: dict,
              balance_after_full_sell: dict):
    '''Test a sell operation.

    Args:
        x: A Xchg instance.
        balance_after_sell: A balance after a sell operation.
        balance_after_full_sell: A balance after a complete sell of one
            currency.
    '''
    # A normal path.
    assert x.sell('cur1', 0.3).balance == approx(balance_after_sell, 1e-10)

    # Buy less than a minimum order size.
    assert x.sell('cur1', 0.05).balance == approx(x.balance, 1e-10)

    # Sell more than we have.
    assert x.sell('cur1', 1).balance == approx(x.balance, 1e-10)

    # Sell zero amount.
    assert x.sell('cur1', 0).balance == approx(x.balance, 1e-10)

    # Sell one currency completely.
    assert x.sell('cur1', 0.5).balance == \
           approx(balance_after_full_sell, 1e-10)

    # Sell a little more than we have, it must be forgived.
    assert x.sell('cur1', 0.5 + 1e-10).balance == \
           approx(balance_after_full_sell, 1e-10)


def test_make_portfolio(x: Xchg, target_portfolios: dict):
    '''Test a making portfolio.

    Args:
        x: A Xchg instance.
        target_portfolios: Several test cases for a desired portfolio.
    '''
    x_new = x
    for target_portfolio in target_portfolios:
        x_new = x_new.make_portfolio(target_portfolio)
        assert x_new.portfolio == approx(target_portfolio, 1e-10)

Functions

def test_buy(x: Xchg, balance_after_buy: dict, balance_after_full_buy: dict)

Test a buy operation.

Args

x
A Xchg instance.
balance_after_buy
A balance after a buy operation.
balance_after_full_buy
A balance after buying currency for all cash.
Expand source code
def test_buy(x: Xchg, balance_after_buy: dict, balance_after_full_buy: dict):
    '''Test a buy operation.

    Args:
        x: A Xchg instance.
        balance_after_buy: A balance after a buy operation.
        balance_after_full_buy: A balance after buying currency for all cash.
    '''
    # A normal path.
    assert x.buy('cur0', 10).balance == approx(balance_after_buy, 1e-10)

    # Buy less than a minimum order size.
    assert x.buy('cur0', 0.1).balance == approx(x.balance, 1e-10)

    # Buy more than the existing cash amount.
    assert x.buy('cur0', 100).balance == approx(x.balance, 1e-10)

    # Buy zero amount.
    assert x.buy('cur0', 0).balance == approx(x.balance, 1e-10)

    # Buy a currency for all cash.
    assert x.buy('cur0', 49.800796812749006).balance == \
           approx(balance_after_full_buy, 1e-10)

    # Buy a little more than we have cash, it must be forgived.
    assert x.buy('cur0', 49.800796812749006 + 1e-10).balance == \
           approx(balance_after_full_buy, 1e-10)
def test_capital(x: Xchg)

Test a capital property.

Args

x
A Xchg instance.
Expand source code
def test_capital(x: Xchg):
    '''Test a capital property.

    Args:
        x: A Xchg instance.
    '''
    assert x.capital == 1.062048
def test_init(files: dict, tmp_path: str, candles: list, balance: dict, xchg_repr: str, default_balance: dict, incomplete_balance: dict, permuted_balance: dict)

Test a construction of a Xchg class.

Args

files
A dictionary with csv files content.
tmp_path
A path which authomatically created by pytest for testing.
candles
An expected result.
balance
An initial balance.
xchg_repr
A representation repr of Xchg class.
default_balance
A default balance when it is not set.
incomplete_balance
An incomplete balance which should be filled with

zeros. permuted_balance: A permuted balance which should be sorted.

Expand source code
def test_init(files: dict, tmp_path: str, candles: list, balance: dict,
              xchg_repr: str, default_balance: dict, incomplete_balance: dict,
              permuted_balance: dict):
    '''Test a construction of a Xchg class.

    Args:
        files: A dictionary with csv files content.
        tmp_path: A path which authomatically created by pytest for testing.
        candles: An expected result.
        balance: An initial balance.
        xchg_repr: A representation __repr__ of Xchg class.
        default_balance: A default balance when it is not set.
        incomplete_balance: An incomplete balance which should be filled with
    zeros.
        permuted_balance: A permuted balance which should be sorted.
    '''
    # Prepare files.
    for filename, content in files.items():
        filepath = tmp_path / filename
        with open(filepath, 'w') as f:
            f.write(content)

    fee = 0.1
    min_order_size = 0.01
    x = Xchg(fee, min_order_size, balance=balance, data_path=tmp_path)
    assert x.current_candle == candles[0]
    assert x.balance == balance
    assert x.fee == fee
    assert x.min_order_size == min_order_size
    assert x.currencies == [f.split('.')[0] for f in files.keys()]
    assert repr(x) == xchg_repr
    x = Xchg(fee, min_order_size, tmp_path)
    assert x.balance == default_balance
    x = Xchg(fee, min_order_size, tmp_path, 1.0)
    assert x.balance == default_balance

    # Check that incompleted balance filled to the full.
    x = Xchg(fee, min_order_size, tmp_path, balance=incomplete_balance)
    assert x.balance == default_balance

    # Check that currencies are sorted in the balance.
    x = Xchg(fee, min_order_size, tmp_path, balance=permuted_balance)
    assert str(x.balance) == str(balance)
def test_make_portfolio(x: Xchg, target_portfolios: dict)

Test a making portfolio.

Args

x
A Xchg instance.
target_portfolios
Several test cases for a desired portfolio.
Expand source code
def test_make_portfolio(x: Xchg, target_portfolios: dict):
    '''Test a making portfolio.

    Args:
        x: A Xchg instance.
        target_portfolios: Several test cases for a desired portfolio.
    '''
    x_new = x
    for target_portfolio in target_portfolios:
        x_new = x_new.make_portfolio(target_portfolio)
        assert x_new.portfolio == approx(target_portfolio, 1e-10)
def test_next_step(x: Xchg, candles: list)

Test a next_step method.

Args

x
A Xchg instance.
candles
A candles list.
Expand source code
def test_next_step(x: Xchg, candles: list):
    '''Test a next_step method.

    Args:
        x: A Xchg instance.
        candles: A candles list.
    '''
    x = x.next_step()
    assert x.current_candle == candles[1]

    # Test for an exception at the end of a file.
    with raises(StopIteration):
        x.next_step()
def test_portfolio(x: Xchg, portfolio: dict)

Test a portfolio property.

Args

x
A Xchg instance.
portfolio
A sample portfolio.
Expand source code
def test_portfolio(x: Xchg, portfolio: dict):
    '''Test a portfolio property.

    Args:
        x: A Xchg instance.
        portfolio: A sample portfolio.
    '''
    assert x.portfolio == approx(portfolio, 1e-10)
def test_sell(x: Xchg, balance_after_sell: dict, balance_after_full_sell: dict)

Test a sell operation.

Args

x
A Xchg instance.
balance_after_sell
A balance after a sell operation.
balance_after_full_sell
A balance after a complete sell of one currency.
Expand source code
def test_sell(x: Xchg, balance_after_sell: dict,
              balance_after_full_sell: dict):
    '''Test a sell operation.

    Args:
        x: A Xchg instance.
        balance_after_sell: A balance after a sell operation.
        balance_after_full_sell: A balance after a complete sell of one
            currency.
    '''
    # A normal path.
    assert x.sell('cur1', 0.3).balance == approx(balance_after_sell, 1e-10)

    # Buy less than a minimum order size.
    assert x.sell('cur1', 0.05).balance == approx(x.balance, 1e-10)

    # Sell more than we have.
    assert x.sell('cur1', 1).balance == approx(x.balance, 1e-10)

    # Sell zero amount.
    assert x.sell('cur1', 0).balance == approx(x.balance, 1e-10)

    # Sell one currency completely.
    assert x.sell('cur1', 0.5).balance == \
           approx(balance_after_full_sell, 1e-10)

    # Sell a little more than we have, it must be forgived.
    assert x.sell('cur1', 0.5 + 1e-10).balance == \
           approx(balance_after_full_sell, 1e-10)